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
- func NewRouter(b Backend, hub *Hub, spa fs.FS) http.Handler
- type Backend
- type BlackboardInfo
- type CheckInfo
- type EventAuth
- type EventIn
- type HealthInfo
- type Hub
- type MemberInfo
- type MemberMetricsInfo
- type MemberSpec
- type MemoryFileInfo
- type MessageInfo
- type MetricsInfo
- type ProposalInfo
- type SkillInfo
- type SkillSpec
- type SpaceInfo
- type TaskInfo
- type TaskPage
- type TranscriptEntry
- type VacuumStats
Constants ¶
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 ¶
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.
// 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
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 (*Hub) Connections ¶
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.
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
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 ¶
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 ¶
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 ¶
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.