webapi

package
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package webapi serves the swarm workstation API: REST snapshots (/api/swarms, /api/swarm/:id, /api/tasks, /api/agents/:name/transcript, /api/messages) and a WebSocket bridge that streams each agent's pkg/event.Event out to the browser, fanned out by (spaceID, AgentID). Inbound, the browser drives each agent's pkg/agent Controller (Run, RespondPermission, RespondQuestion) and the Supervisor (suspend, add, freeze, halt).

The pkg/event doc already anticipates "a JSON-over-websocket bridge" — the Hub here is it: internal/swarm/service pumps each space's tagged event stream into Hub.Publish, which fans it out to the matching sockets.

The package owns its own wire DTOs (SpaceInfo/MemberInfo/TaskInfo/…) and talks to the host only through the narrow Backend interface, so it imports no agent/store/llm types — the swarm domain maps into these shapes, not the reverse.

Index

Constants

View Source
const WebhookSecretHeader = "X-Evva-Webhook-Secret"

WebhookSecretHeader carries a space's shared webhook secret on event POSTs (RP-15). Only consulted for spaces that set settings.webhook_secret.

Variables

This section is empty.

Functions

func NewRouter

func NewRouter(b Backend, hub *Hub, spa fs.FS) http.Handler

NewRouter assembles the swarm workstation HTTP handler: token-gated REST snapshots + command endpoints, the token-gated WebSocket bridge fed by hub, an unauthenticated /healthz, and the embedded SPA at /. spa is the built vue tree (web/dist sub-FS); a nil spa skips the static mount (tests).

Types

type Backend

type Backend interface {
	// Token is the session token every /api and /ws request must present.
	Token() string

	// AllowRemote reports whether the host was started with the non-loopback
	// opt-in (RP-15). When true the loopback-trust conveniences are off: the
	// router hides the GET /api/auth/bootstrap endpoint entirely (behind a
	// reverse proxy every caller LOOKS loopback, so the endpoint would leak
	// the token to the world).
	AllowRemote() bool

	// HasSpace reports whether a space id is registered (the WS subscribe guard).
	HasSpace(spaceID string) bool

	// Lifecycle (Docker-style). Register brings a NEW space up from a workdir
	// with an optional explicit name (POST /api/swarms). StopSpace stops a running
	// space but KEEPS it as "stopped" (POST /api/swarm/:ref/stop); RunSpace
	// rebuilds a stopped one under its same id/name (POST /api/swarm/:ref/run);
	// RemoveSpace forgets it entirely (DELETE /api/swarm/:ref). ResetSpace wipes it
	// to a blank slate — fresh ledger + cleared agent context — under the SAME id
	// (POST /api/swarm/:ref/reset). ReloadSpace re-reads the manifest + every agent's
	// prompt/tools and rebuilds in place under the SAME id, KEEPING the ledger +
	// transcripts (POST /api/swarm/:ref/reload) — the one-click "apply my config
	// edits" without a remove+restart. For all of the above except Register, ref is a
	// space id OR its name. Register/Run/Reset/Reload return the (stable) space id.
	Register(workdir, name string) (string, error)
	StopSpace(ref string) error
	RunSpace(ref string) (string, error)
	RemoveSpace(ref string) error
	ResetSpace(ref string) (string, error)
	ReloadSpace(ref string) (string, error)

	// Read snapshots. The bool is false when the space id is unknown.
	Spaces() []SpaceInfo
	Roster(spaceID string) ([]MemberInfo, bool)
	// Tasks is the board snapshot: every active (non-completed) task plus only
	// the most recent few completed; TaskPage.Total is the full completed count,
	// so the 2.5s board poll stays small however much history accumulates (RP-6).
	Tasks(spaceID string) (TaskPage, bool)
	// TasksByStatus is the on-demand paged view of one status (the Completed tab):
	// limit/offset page the rows (completed newest-first), Total is the full count.
	TasksByStatus(spaceID, status string, limit, offset int) (TaskPage, bool)
	Messages(spaceID string) ([]MessageInfo, bool)
	// Proposals lists the space's worker-filed work proposals (RP-23), every
	// status, oldest first — the web's bottom-up inbox.
	Proposals(spaceID string) ([]ProposalInfo, bool)
	Transcript(spaceID, agent string) ([]TranscriptEntry, bool)
	// ChatLog replays the space's durable event log as chat-relevant wire
	// events, oldest-first, capped at limit (<= 0 = server default): whole-turn
	// text/thinking, tool start/result, errors, turn/run boundaries, plus
	// operator mail as synthetic user_message events. The web console rebuilds
	// its stream from this on load and reconnect — the durable successor to
	// re-seeding from members' live (compaction-rewritten) contexts. Empty when
	// the space logs no events (event_log: false); false when the space is
	// unknown or stopped.
	ChatLog(spaceID string, limit int) ([]json.RawMessage, bool)
	// PendingGates returns the space's outstanding approval/question gates as raw
	// wire events (same shape the WS sends), so a reconnecting browser re-renders
	// overlays for members still blocked instead of leaving them hung (RP-2 §3.3).
	PendingGates(spaceID string) ([]any, bool)

	// SendUserMessage delivers an operator message onto a member's mailbox as
	// sender "user" (or broadcasts when to == "all"). It rides the same bus +
	// drain path as inter-agent mail, so an idle member is woken and a busy one
	// folds it mid-run — flat operator↔member comms without disturbing the
	// workflow. Returns the durable message id — the CLI's send receipt
	// (RP-27). See docs/roadmap/veronica/direction-flat-comms.md.
	SendUserMessage(spaceID, to, subject, body string) (string, error)

	// Inbound commands. Run is asynchronous — it kicks off a turn whose events
	// stream back over the WebSocket; the rest are immediate.
	Run(spaceID, agent, prompt string) error
	// RespondPermission delivers an approval reply. A non-empty ruleTool means
	// the operator picked "Always allow" — add a session-scope allow rule for
	// that tool so it stops re-prompting for the rest of the session.
	RespondPermission(spaceID, agent, reqID, behavior, reason, ruleTool string) error
	RespondQuestion(spaceID, agent, reqID string, answers map[string][]string) error
	Suspend(spaceID, agent string) error
	Resume(spaceID, agent string) error
	Freeze(spaceID, agent string) error
	Unfreeze(spaceID, agent string) error
	HaltAll(spaceID string) error

	// ClearMemberSession wipes one member's conversation to a blank slate
	// (fresh live session + persisted transcript deleted) while its seat —
	// membership, schedule, skills, memory — survives. A busy member refuses
	// with a "busy" error the handler maps to 409; suspend it or wait.
	ClearMemberSession(spaceID, agent string) error

	// SetMemberPermissionMode switches one member's permission stance
	// ("default" | "accept_edits" | "plan" | "bypass") at runtime. Applies
	// to the live gate immediately (mid-run included) and persists as a
	// runtime override until the space is freshly re-registered. An invalid
	// mode is operator input → 400; unknown space/member → 404.
	SetMemberPermissionMode(spaceID, agent, mode string) error

	// CompactMember compacts one member's live context on operator command —
	// the per-member twin of the TUI's /compact. kind is "micro" (elide older
	// tool-result bodies, no LLM call) or "full" (one summarization call that
	// replaces the transcript with a context brief). A busy member refuses with
	// a "busy" error the handler maps to 409; an unknown kind → 400; unknown
	// space/member → 404.
	CompactMember(spaceID, agent, kind string) error

	// Schedule CRUD (RP-8). The web path has NO self-guard — the operator may
	// set/clear ANY member's schedule, including the leader's (the symmetric
	// complement to the leader tool, which refuses to reschedule itself, RP-7).
	// A bad cron is a validation error the handler maps to 400.
	SetSchedule(spaceID, agent, cron, prompt string) error
	ClearSchedule(spaceID, agent string) error

	// Membership editing (RP-8). CreateMember authors a new worker from a spec
	// (writes its dir, hot-loads it, records it in the manifest); RemoveMember
	// retires one (deleteDir also erases its on-disk definition). The leader is
	// unique — neither can target it. SelectableTools is the catalog the add-agent
	// form offers (collaboration tools excluded — they are role-injected).
	CreateMember(spaceID string, spec MemberSpec) error
	RemoveMember(spaceID, agent string, deleteDir bool) error
	SelectableTools() []string
	// SelectableModels is the model catalog the add-agent form offers for the
	// optional per-member model pin (every model of every built-in provider).
	SelectableModels() []string

	// IngestEvent delivers an external webhook event (RP-9) onto a member's
	// mailbox (default the leader), waking it through the ordinary bus path — a
	// webhook is just a message. duplicate is true when the idempotency key was
	// already seen (no second delivery/wake). Errors carry "unauthorized" /
	// "unknown space" / "stopped" so the handler can map 401 / 404 / 409. The
	// route skips the session-token guard; instead the backend authorizes from
	// auth (RP-15): a space-configured webhook_secret must match for everyone,
	// and without one only loopback peers pass (the RP-9 trust boundary).
	IngestEvent(ref string, evt EventIn, auth EventAuth) (messageID string, duplicate bool, err error)

	// Agent skills (RP-10). MemberSkills lists a member's authored skills (bool false
	// when the space/member is unknown); AddSkill writes a new SKILL.md and hot-reloads
	// that member's prompt; DeleteSkill removes one. Author path is User/web ONLY —
	// agents never author skills, only load them. Add/Delete errors carry "unknown" for
	// a missing space/member (→ 404); bad input (illegal/duplicate name, empty body) is
	// 400.
	MemberSkills(spaceID, agent string) ([]SkillInfo, bool)
	AddSkill(spaceID, agent string, spec SkillSpec) error
	DeleteSkill(spaceID, agent, skill string) error

	// Space-shared skills (RP-26): one copy every member loads. SharedSkills
	// lists them (bool false when the space is unknown); AddSharedSkill authors
	// one (create-only — delete first to replace) and reloads EVERY member;
	// DeleteSharedSkill is the User's final-arbiter delete over anything the
	// leader's skill_publish put there, also reloading every member.
	SharedSkills(spaceID string) ([]SkillInfo, bool)
	AddSharedSkill(spaceID string, spec SkillSpec) error
	DeleteSharedSkill(spaceID, skill string) error

	// MemberMemory lists a member's long-term memory files read-only (RP-25):
	// the User's transparency window onto the team's mind. bool false when the
	// space or member is unknown. Curation (delete) is deferred with the FE tab.
	MemberMemory(spaceID, agent string) ([]MemoryFileInfo, bool)

	// Blackboard reads the team blackboard read-only (BB): the leader-curated
	// standing picture every member's wake brief carries. bool false when the
	// space is unknown; an empty board is Content "" (dormant), not an error.
	// Web write access is deferred (BB open question #1) — the operator edits
	// .vero/blackboard.md on disk.
	Blackboard(spaceID string) (BlackboardInfo, bool)

	// Vacuum runs one ledger retention pass now (RP-16): archive-then-delete
	// messages read ≥ days ago and tasks completed ≥ days ago. days <= 0 uses
	// the space's configured window (or the default); dryRun only counts.
	Vacuum(ref string, days int, dryRun bool) (VacuumStats, error)

	// Metrics snapshots a space's scheduler counters (RP-17): per-member
	// wakes/runs/aborts + run-duration buckets, bus hint drops, and the event
	// log's logged/dropped counts. false = unknown or stopped space.
	Metrics(spaceID string) (MetricsInfo, bool)

	// Health is the unauthenticated liveness snapshot (RP-18): version,
	// uptime, and aggregate counts only — enough to tell "alive but idle"
	// from "in service" with one curl, while leaking no names or detail.
	Health() HealthInfo
}

Backend is everything the HTTP/WS layer needs from the swarm host. It is a narrow translation seam: handlers turn JSON into these calls and expose nothing beyond them (invariant #1). internal/swarm/service implements it over its SwarmSpace registry + per-space Supervisors. The DTOs below keep this package free of any agent/store/llm imports, so the wire shape is owned here, not leaked from the domain.

type BlackboardInfo added in v1.11.0

type BlackboardInfo struct {
	Content   string `json:"content"`
	UpdatedAt int64  `json:"updatedAt"`
	By        string `json:"by,omitempty"`
}

BlackboardInfo is GET /api/swarm/{id}/blackboard (BB): the team blackboard document. UpdatedAt is the file's mtime in unix millis (0 when the board is empty/absent); By is the last tool writer when the file is still that writer's version ("" after a restart or an operator disk edit).

type CheckInfo added in v1.11.0

type CheckInfo struct {
	Command    string `json:"command"`
	Exit       int    `json:"exit"`
	TimedOut   bool   `json:"timedOut,omitempty"`
	DurationMs int64  `json:"durationMs"`
	StartedAt  int64  `json:"startedAt"`
	Workdir    string `json:"workdir,omitempty"`
	Tail       string `json:"tail,omitempty"`
	Truncated  bool   `json:"truncated,omitempty"`
	Pass       bool   `json:"pass"`
}

CheckInfo mirrors store.CheckEvidence on the wire — one verify-time check run's machine evidence (CHK).

type EventAuth added in v1.7.0

type EventAuth struct {
	Secret   string
	Loopback bool
}

EventAuth is what the transport observed about an event POST's caller: the presented shared secret (may be empty) and whether the TCP peer was a loopback address. The router only reports; the backend decides (RP-15).

type EventIn

type EventIn struct {
	Title          string          `json:"title"`
	Body           string          `json:"body"`
	Source         string          `json:"source"`
	Data           json.RawMessage `json:"data"`
	To             string          `json:"to"`
	IdempotencyKey string          `json:"idempotency_key"`
}

EventIn is the body of POST /api/swarm/{id}/event — an external app's signal (RP-9). Only Body is required. Source/Title give the leader provenance; Data is carried verbatim; To defaults to the leader; IdempotencyKey collapses retries.

type HealthInfo added in v1.7.0

type HealthInfo struct {
	Status        string `json:"status"` // always "ok" when the host answers
	Version       string `json:"version"`
	UptimeSecs    int64  `json:"uptimeSecs"`
	SpacesRunning int    `json:"spacesRunning"`
	SpacesStopped int    `json:"spacesStopped"`
	MembersActive int    `json:"membersActive"`
	MembersFrozen int    `json:"membersFrozen"`
	// CostTodayUSD is the service-wide priced spend today — the sum over
	// running spaces (CST). An estimate at list price, and a floor when any
	// member runs an unpriced model.
	CostTodayUSD float64 `json:"costTodayUsd"`
}

HealthInfo is GET /healthz (RP-18). The endpoint stays unauthenticated (liveness probes can't hold a token), so it deliberately carries no names, ids, or workdirs — per-space detail lives behind the guard (GET /api/swarms, /api/swarm/{id}/metrics).

type Hub

type Hub struct {
	// contains filtered or unexported fields
}

Hub fans each space's event stream out to the browser. Every WebSocket client subscribes to exactly one space — and optionally one agent within it — and the hub delivers only events whose (spaceID, AgentID) match that subscription. This is the on-the-wire half of per-space isolation (invariant #2, AC#3): a client watching space A never receives space B's events.

golang.org/x/net/websocket connections are not safe for concurrent writes, so each conn serialises its sends under its own mutex; the hub itself only holds its registry lock long enough to snapshot the target set.

func NewHub

func NewHub() *Hub

NewHub returns an empty hub.

func (*Hub) Connections

func (h *Hub) Connections() int

Connections returns the current subscriber count — used by tests to wait for a client's subscription to land before driving the agent that should reach it.

func (*Hub) Publish

func (h *Hub) Publish(spaceID, agentID string, payload []byte)

Publish delivers a marshalled event to every connection whose subscription matches (spaceID, agentID). A connection with no agent filter matches all of its space's agents. A failed write tears down that connection — the browser will reconnect — without blocking the others.

type MemberInfo

type MemberInfo struct {
	Name        string `json:"name"`
	AgentID     string `json:"agentId"`
	Role        string `json:"role"`
	Membership  string `json:"membership"`
	Run         string `json:"run"`                  // coarse lifecycle: idle | busy | suspended
	Phase       string `json:"phase,omitempty"`      // fine, event-derived sub-phase (RP-3)
	Tool        string `json:"tool,omitempty"`       // tool name for executing / waiting-approval
	PhaseSince  int64  `json:"phaseSince,omitempty"` // unix millis the phase was entered (RP-4 timing)
	CurrentTask int64  `json:"currentTask"`
	WhenToUse   string `json:"whenToUse,omitempty"`
	// PermissionMode is the member's effective permission stance (manifest
	// member override > space setting; RP-24): default | accept_edits |
	// plan | bypass. "bypass" members run fully autonomous; "default"
	// members queue approvals in the web inbox.
	PermissionMode string `json:"permissionMode,omitempty"`
	// ContextTokens / ContextLimit drive the per-member context-utilization meter
	// (the web roster's CTX bar). ContextTokens is the input-token count of the
	// member's most recent turn — how full its prompt is right now; ContextLimit
	// is its model's context window. They are the same pair evva's TUI status bar
	// reads (controller.LastTurnInputTokens / constant.MODEL_CONTEXT_SIZE; see
	// pkg/ui/.../status.SetContext). ContextLimit is 0 when the model is unknown
	// (custom/stub models absent from the context-size table) — the UI then shows
	// an unknown rail with no %. Not omitempty: 0 is meaningful (no turn yet /
	// unknown window) and the TS contract expects both fields always present.
	ContextTokens int `json:"contextTokens"`
	ContextLimit  int `json:"contextLimit"`
	// Token meter (RP-13): cumulative session input/output tokens as of the
	// member's last run boundary, today's spend, and the member's effective
	// daily budget (0 = unlimited). TokensToday vs TokensBudget is the budget
	// breaker's gauge; a frozen membership plus an exhausted gauge reads as
	// "frozen by the breaker".
	TokensIn     int `json:"tokensIn,omitempty"`
	TokensOut    int `json:"tokensOut,omitempty"`
	TokensToday  int `json:"tokensToday,omitempty"`
	TokensBudget int `json:"tokensBudget,omitempty"`
	// Cost meter (CST): today's cache-class tokens and the USD priced at
	// meter time. CostUnpriced marks a member whose model has no rate-card
	// entry — its dollars are missing from every $ figure, not zero.
	TokensCacheRead  int     `json:"tokensCacheRead,omitempty"`
	TokensCacheWrite int     `json:"tokensCacheWrite,omitempty"`
	CostTodayUSD     float64 `json:"costTodayUsd,omitempty"`
	CostUnpriced     bool    `json:"costUnpriced,omitempty"`
	// Ephemeral / SpawnedFrom mark a DWF member_spawn clone (the roster's
	// ephemeral pill): a spawned member that retires itself when its work
	// completes, and the base it was cloned from.
	Ephemeral   bool   `json:"ephemeral,omitempty"`
	SpawnedFrom string `json:"spawnedFrom,omitempty"`
	// Cron / SchedulePrompt expose the member's recurring timer (RP-7/RP-8), read
	// live from the space's schedule map (the schedule's owner — it is NOT on
	// MemberView). Empty when the member has no schedule.
	Cron           string `json:"cron,omitempty"`
	SchedulePrompt string `json:"schedulePrompt,omitempty"`
}

MemberInfo mirrors swarm.MemberView on the wire (GET /api/swarm/:id). AgentID is the event-stream identity, so the web can demux the per-(space, agent) WS feed into a focused per-member console.

type MemberMetricsInfo added in v1.7.0

type MemberMetricsInfo struct {
	WakesMessage int64            `json:"wakesMessage"`
	WakesTimer   int64            `json:"wakesTimer"`
	Runs         int64            `json:"runs"`
	Aborts       int64            `json:"aborts"`
	RunSeconds   map[string]int64 `json:"runSeconds"`
	RunTokens    map[string]int64 `json:"runTokens"`
}

MemberMetricsInfo is one member's scheduler counters. RunSeconds buckets completed runs by wall-clock duration: lt10s / lt1m / lt10m / gte10m. RunTokens buckets the same runs by token cost (input+output, the RP-13 delta): lt1k / lt10k / lt50k / gte50k (RP-28) — the histogram that says whether a watchdog's per-run cost is creeping up with history length.

type MemberSpec

type MemberSpec struct {
	Name         string   `json:"name"`
	SystemPrompt string   `json:"systemPrompt"`
	WhenToUse    string   `json:"whenToUse"`
	Model        string   `json:"model"`  // optional model pin; "" = configured default. Fixed at creation.
	Effort       string   `json:"effort"` // optional effort pin (low|medium|high|ultra); "" = default. Fixed at creation.
	Active       []string `json:"active"`
	Deferred     []string `json:"deferred"`
	Cron         string   `json:"cron"`
	Prompt       string   `json:"prompt"`
}

MemberSpec is the wire shape of the web "add agent" form (RP-8): the operator authors a new worker. Collaboration tools are role-injected at construction, so they never appear here. Cron/Prompt are an optional recurring schedule.

type MemoryFileInfo added in v1.7.0

type MemoryFileInfo struct {
	Name    string `json:"name"`
	Content string `json:"content"`
}

MemoryFileInfo is one file of GET /api/agents/{name}/memory (RP-25): a member's memory file, dir-relative name + raw markdown content. MEMORY.md (the index) is first when present.

type MessageInfo

type MessageInfo struct {
	ID        string `json:"id"`
	Sender    string `json:"sender"`
	Recipient string `json:"recipient"`
	Subject   string `json:"subject,omitempty"`
	Body      string `json:"body"`
	RefTask   *int64 `json:"refTask,omitempty"`
	// ReadAt / ClaimedAt expose the unread→claimed→read lifecycle (store
	// migration 0002). ReadAt is stamped only when a member's run ends cleanly
	// (SettleClaimed); ClaimedAt marks a message currently folded into an
	// in-flight run. Surfacing Claimed lets the UI show "reading…" for mail the
	// agent is actively processing, instead of it looking plain-unread until the
	// whole run settles.
	ReadAt    *int64 `json:"readAt,omitempty"`
	ClaimedAt *int64 `json:"claimedAt,omitempty"`
	CreatedAt int64  `json:"createdAt"`
}

MessageInfo mirrors store.Message on the wire (GET /api/messages).

type MetricsInfo added in v1.7.0

type MetricsInfo struct {
	UptimeSecs    int64 `json:"uptimeSecs"`
	EventsLogged  int64 `json:"eventsLogged"`
	EventsDropped int64 `json:"eventsDropped"`
	HintsDropped  int64 `json:"hintsDropped"`
	// TasksStale / MailboxStale count RP-22 workflow-watchdog notifications
	// sent since the space started (stale-task reminders, backlog alerts).
	TasksStale   int64 `json:"tasksStale"`
	MailboxStale int64 `json:"mailboxStale"`
	// DWF engine tallies: tasks the engine dispatched (no leader relay), and
	// the ephemeral-member lifecycle.
	AutoDispatches int64 `json:"autoDispatches"`
	MembersSpawned int64 `json:"membersSpawned"`
	MembersRetired int64 `json:"membersRetired"`
	// CHK check-runner tallies: delivered runs, failing runs (timeouts
	// included), and timeouts — the signal the configured command outgrew
	// its budget.
	ChecksRun     int64 `json:"checksRun"`
	ChecksFailed  int64 `json:"checksFailed"`
	ChecksTimeout int64 `json:"checksTimeout"`
	// NTF notifier tallies: notifications delivered, dropped (dead endpoint,
	// full queue, teardown), and rate-limit-suppressed. All zero when the
	// space configures no notify block.
	NotifsSent       int64 `json:"notifsSent"`
	NotifsDropped    int64 `json:"notifsDropped"`
	NotifsSuppressed int64 `json:"notifsSuppressed"`
	// CST space-day cost aggregate: today's In+Out tokens across the roster,
	// the cache classes, the PRICED spend (CostUnpriced marks exclusions),
	// the configured ceilings (0 = axis off), and whether the ceiling
	// tripped today (everyone frozen until rollover).
	SpaceTokensToday   int                          `json:"spaceTokensToday"`
	SpaceCostTodayUSD  float64                      `json:"spaceCostTodayUsd"`
	SpaceCostUnpriced  bool                         `json:"spaceCostUnpriced,omitempty"`
	CeilingTotalTokens int                          `json:"ceilingTotalTokens,omitempty"`
	CeilingTotalUSD    float64                      `json:"ceilingTotalUsd,omitempty"`
	CeilingTripped     bool                         `json:"ceilingTripped,omitempty"`
	Members            map[string]MemberMetricsInfo `json:"members"`
}

MetricsInfo is GET /api/swarm/{id}/metrics (RP-17): plain counters, no timeseries — the user-side exporter (if any) owns history.

type ProposalInfo added in v1.7.0

type ProposalInfo struct {
	ID                int64  `json:"id"`
	Proposer          string `json:"proposer"`
	Title             string `json:"title"`
	Spec              string `json:"spec,omitempty"`
	SuggestedAssignee string `json:"suggestedAssignee,omitempty"`
	Status            string `json:"status"`
	DecidedBy         string `json:"decidedBy,omitempty"`
	DecideNote        string `json:"decideNote,omitempty"`
	RefTask           int64  `json:"refTask,omitempty"`
	CreatedAt         int64  `json:"createdAt"`
	DecidedAt         int64  `json:"decidedAt,omitempty"`
}

ProposalInfo mirrors store.Proposal on the wire (GET /api/swarm/{id}/proposals, RP-23). Status: open | accepted | declined; RefTask is the task an accepted proposal became (0 = none yet).

type SkillInfo

type SkillInfo struct {
	Name        string `json:"name"`
	Description string `json:"description"`
}

SkillInfo is one row of GET /api/agents/{name}/skills (RP-10): a member's authored skill — name + description, the same pair the prompt's # Skills section lists.

type SkillSpec

type SkillSpec struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Body        string `json:"body"`
}

SkillSpec is the body of POST /api/agents/{name}/skills (RP-10): the operator authors a skill. The first line of the written SKILL.md becomes `# <name> <description>`; Body is the rest (the instructions the skill tool loads).

type SpaceInfo

type SpaceInfo struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Workdir string `json:"workdir"`
	Status  string `json:"status"`
	Members int    `json:"members"`
	Leader  string `json:"leader,omitempty"` // leader member name
	Busy    int    `json:"busy"`             // members with a run in flight
}

SpaceInfo is one row of GET /api/swarms. Status is "running" | "stopped" (the list is like `docker ps -a` — stopped spaces are shown too). Leader / Busy are live-roster reads, so they carry data only for running spaces — a stopped space has no roster to inspect.

type TaskInfo

type TaskInfo struct {
	ID         int64  `json:"id"`
	Title      string `json:"title"`
	Spec       string `json:"spec"`
	Status     string `json:"status"`
	Assignee   string `json:"assignee"`
	CreatedBy  string `json:"createdBy"`
	Result     string `json:"result,omitempty"`
	VerifyNote string `json:"verifyNote,omitempty"`
	ParentID   *int64 `json:"parentId,omitempty"`
	// DependsOn / VerifyPolicy are the DWF task-graph fields: the dependency
	// edges holding a blocked task (the board's dep badges) and who settles
	// verifying ("leader" | "auto" | "checks").
	DependsOn    []int64 `json:"dependsOn,omitempty"`
	VerifyPolicy string  `json:"verifyPolicy,omitempty"`
	// Checks is the latest check run's evidence (CHK; absent = never ran);
	// CheckRunning marks a queued/executing check — the board chip's third
	// state; CheckOff marks a task opted out at creation.
	Checks       *CheckInfo `json:"checks,omitempty"`
	CheckRunning bool       `json:"checkRunning,omitempty"`
	CheckOff     bool       `json:"checkOff,omitempty"`
	CreatedAt    int64      `json:"createdAt"`
	UpdatedAt    int64      `json:"updatedAt"`
}

TaskInfo mirrors store.Task on the wire (GET /api/tasks).

type TaskPage

type TaskPage struct {
	Tasks []TaskInfo `json:"tasks"`
	Total int        `json:"total"`
}

TaskPage is a bounded slice of tasks plus the full match total, so a paged client can render "N of TOTAL" and decide whether to fetch more (RP-6). On the board snapshot, Total is the completed count (Tasks holds active + a preview).

type TranscriptEntry

type TranscriptEntry struct {
	Role string `json:"role"`
	Text string `json:"text"`
}

TranscriptEntry is one conversation turn (GET /api/agents/:name/transcript).

type VacuumStats added in v1.7.0

type VacuumStats struct {
	Messages int      `json:"messages"`
	Tasks    int      `json:"tasks"`
	Files    []string `json:"files,omitempty"`
	Days     int      `json:"days"`
	DryRun   bool     `json:"dryRun"`
}

VacuumStats is the result of POST /api/swarm/{id}/vacuum (RP-16): how many rows one retention pass archived + deleted (or, on a dry run, would have), the archive files appended, and the effective window used.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL