Files
waggle-os/docs/backend-map/sections/05e-subsystem-waggledance-aios.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

28 KiB
Raw Permalink Blame History

05e — Subsystem: WaggleDance + the AI-OS Arc

Purpose. This subsystem is Waggle's multi-agent coordination layer. It has two halves that meet at one shared protocol (WaggleMessage): (1) WaggleDance — a message protocol + dispatcher + in-memory ring buffer (SignalBus) that carries "what's happening across agents/tools right now"; and (2) the AI-OS arc — the path that detects 7 external AI tools on the user's machine, installs reversible hooks into them, launches them with a workspace-context env var, then lets those tools' hooks emit signals back into Waggle so the UI shows cross-tool activity in real time. There is also a local sub-agent layer (spawn/list/result, orchestrated workflows, cross-workspace messaging) that shares the same vocabulary.

This file is the contract for rebuilding the frontend surfaces that consume these endpoints (the WaggleDanceApp activity feed, the LauncherApp dock, the Mission Control inventory tile, and the Room sub-agent tiles). Every identifier and path below is quoted from the source under packages/waggle-dance/, packages/agent/src/, packages/hive-mind-shim-core/src/, packages/shared/src/, and packages/server/src/local/.


1. Mental model — two signal planes

There are two distinct signal shapes in this subsystem, and a bridge that converts one into the other. Do not confuse them.

Plane Shape Subtypes Where it lives Who consumes it
v2 protocol bus (WaggleDance) WaggleMessage 10 protocol subtypes (discovery, task_delegation, …) SignalBus ring buffer (packages/server/src/local/signal-bus.ts) GET /api/waggle-dance/signals; the bridge
Legacy UX stream WaggleSignal 5 UX categories (discovery/handoff/insight/alert/coordination) in-memory array in waggle-signals.ts GET /api/waggle/signals + GET /api/waggle/stream (SSE); the WaggleDanceApp UI

The bridge (installWaggleDanceBridge, waggle-dance-bridge.ts) subscribes to the v2 bus and re-emits every v2 message as a legacy UX signal, so the existing UI surfaces cross-tool activity with zero frontend changes. A frontend rebuild can target either plane; the legacy stream is the one the current UI already speaks and is SSE-streamed.

flowchart LR
  subgraph v2["v2 protocol plane (WaggleMessage)"]
    POST["POST /api/waggle-dance/signal"]
    DISP["WaggleDanceDispatcher.dispatch()"]
    BUS["SignalBus ring buffer (cap 500)"]
    GET2["GET /api/waggle-dance/signals"]
  end
  subgraph legacy["legacy UX plane (WaggleSignal)"]
    EMIT["emitWaggleSignal()"]
    ARR["in-memory array (cap 500)"]
    GET1["GET /api/waggle/signals"]
    SSE["GET /api/waggle/stream (SSE)"]
  end
  POST --> DISP --> BUS --> GET2
  BUS -->|installWaggleDanceBridge subscribes| EMIT
  EMIT --> ARR --> GET1
  ARR --> SSE
  SSE -->|consumed by| UI["WaggleDanceApp UI (useWaggleDance)"]

2. The WaggleDance protocol (packages/waggle-dance + packages/shared/src/types.ts)

2.1 WaggleMessage — canonical protocol envelope

Defined in packages/shared/src/types.ts. This is the v2 signal shape.

Field Type Nullable Meaning
id string no UUID; server-assigned (randomUUID()) on POST
teamId string no Team id; defaults to personal::<senderId> when omitted (the personal-tier moat)
senderId string no Logical sender ('claude-code-hook', 'cursor-hook', 'local', agent-loop:<persona>)
type MessageType no One of 'broadcast' | 'request' | 'response'
subtype MessageSubtype no One of 10 subtypes (see 2.2)
content Record<string, unknown> no Free-form structured payload; preserved verbatim
referenceId string | null yes For response types: correlates back to a prior request
routing Array<{ userId: string; reason: string }> | null yes For routed_share: directed recipients + rationale
createdAt Date no Server-assigned timestamp

2.2 MessageType × MessageSubtype and valid combinations

MessageType = 'broadcast' | 'request' | 'response'

MessageSubtype (10 values): knowledge_check, task_delegation, skill_request, model_recommendation, knowledge_match, task_claim, discovery, routed_share, skill_share, model_recipe.

Valid combos are enforced by validateMessageTypeCombo(type, subtype) in packages/waggle-dance/src/protocol.ts against VALID_COMBINATIONS:

type Allowed subtypes
request knowledge_check, task_delegation, skill_request, model_recommendation
response knowledge_match, task_claim
broadcast discovery, routed_share, skill_share, model_recipe

isRoutedMessage(subtype) returns true only for routed_share. An invalid combo at the route returns HTTP 400.

2.3 The dispatcher — WaggleDanceDispatcher (dispatcher.ts)

dispatch(message: WaggleMessage): Promise<DispatchResult> validates the combo, then branches on subtype. DispatchResult = { handled: boolean; response?: string; error?: string }.

Dependencies injected via DispatchDepsv1 (team-internal) deps are required; v2 (cross-tool bus) deps are all optional, so the route layer can wire them incrementally and unwired branches still return handled: true:

Dep Signature Used by subtypes
searchMemory (query) => Promise<string> knowledge_check
resolveCapability (query) => Array<{source,name,description,available}> skill_request
spawnWorker (task, role, context?) => Promise<string> task_delegation
emitSignal? (msg) => Promise<void> discovery, routed_share, model_recipe (broadcasts)
recordResponse? (msg) => Promise<void> knowledge_match, task_claim (responses)
recommendModel? (query, context) => Promise<string | null> model_recommendation

Per-subtype dispatch behavior (the 6 v2 branches the prompt asks about plus the 4 v1):

Subtype Handler Behavior Required content fields
task_delegation (v1) handleTaskDelegation Calls spawnWorker(task, role, context) content.task (required); content.role (default 'analyst'), content.context
knowledge_check (v1) handleKnowledgeCheck Calls searchMemory(query) content.query or content.topic
skill_request (v1) handleSkillRequest Calls resolveCapability(query), returns a formatted route list content.skill or content.query
skill_share (v1) handleSkillShare Returns JSON {action:'install_shared_skill', skillName, skillContent, sharedBy} content.name/content.skill + content.content
discovery (v2) handleBroadcastSignal If emitSignal set → emit to bus; else accept none enforced
routed_share (v2) handleBroadcastSignal Same; UI shows as a directed handoff routing carries recipients
model_recipe (v2) handleBroadcastSignal Same; shares a model+prompt recipe none enforced
knowledge_match (v2) handleResponseRelay If recordResponse set → record; correlated via referenceId none enforced
task_claim (v2) handleResponseRelay Same; a worker claiming a delegated task none enforced
model_recommendation (v2) handleModelRecommendation Calls recommendModel(query, restOfContent); friendly fallback if unwired content.query (required)

hive-query.ts defines HiveQuery { topic; scope? } and HiveQueryResult { entities[]; relatedTasks[]; relatedMessages[] } — DB-backed hive queries are executed by the server's MessageService, not in this package (the package is types + dispatcher only). packages/waggle-dance/src/index.ts barrel-exports protocol.js, hive-query.js, dispatcher.js.


3. SignalBus — the v2 ring buffer (packages/server/src/local/signal-bus.ts)

In-memory ring buffer of WaggleMessage. Created once per server, decorated as server.signalBus. Not durable — survives until sidecar restart or rollover.

  • DEFAULT_BUFFER_SIZE = 500. On overflow the oldest is dropped (shift()).
  • record(signal) → appends, drops oldest if over capacity, notifies all subscribers synchronously (subscriber errors are swallowed so one bad subscriber can't poison the bus), returns the signal.
  • query(filter) → snapshot, newest first. SignalFilter:
Filter field Type Effect
subtype MessageSubtype exact match
tool string matches content.tool
teamId string exact match
since ISO string createdAt > since
limit number cap result count
  • subscribe(sub) → returns an unsubscribe fn (this is how the bridge attaches).
  • size getter; clear() (tests).

4. The two streaming UX endpoints (legacy plane) — waggle-signals.ts

WaggleSignal (the legacy/UX shape the current UI renders):

Field Type Nullable Meaning
id string no sig-<ts>-<rand>
type string no e.g. agent:started, tool:called, memory:saved, agent:completed, or bridged waggle-dance:<category>
workspaceId string no Defaults to 'global'
content string no Human-readable primary text
metadata Record<string,unknown> yes Provenance (bridge fills subtype/senderId/tool/teamId/referenceId/routing/priority/protocolMessage)
timestamp string (ISO) no Emit time
acknowledged boolean no Ack state

Store is a capped in-memory array (MAX_SIGNALS = 500, newest-first via unshift). emitWaggleSignal(...) is the exported publish fn used by the chat loop and the bridge.


5. The bridge — v2 → legacy (waggle-dance-bridge.ts)

installWaggleDanceBridge(bus: SignalBus): () => void subscribes to the v2 bus and re-emits each WaggleMessage via emitWaggleSignal. It is auto-installed the first time server.signalBus is created (inside waggle-dance.ts).

Subtype → UX category mapping (categorizeSubtype, exported for tests):

v2 subtype UX category Legacy type emitted
discovery, knowledge_check, skill_request discovery waggle-dance:discovery
task_delegation, skill_share, routed_share handoff waggle-dance:handoff
knowledge_match insight waggle-dance:insight
task_claim, model_recipe, model_recommendation coordination waggle-dance:coordination
(any subtype) with priority === 'critical' alert (override) waggle-dance:alert

Priority is resolved by inferPriority: content.priority (low/normal/high/critical) wins; else content.importance (high→high, critical→critical); else normal. buildLegacyContent builds the display string as "<subtype>: <topic>" where topic falls back through content.topic → title → query → task → skill, then to a key summary. Full provenance (including the verbatim protocolMessage) is carried in metadata.


6. AI-OS Phase 0 — tool detection (packages/shared/src/tool-detection.ts + packages/agent/src/tool-detection.ts)

6.1 The 7 supported tools

SUPPORTED_TOOLS (@waggle/shared), with display names from TOOL_DISPLAY_NAMES:

ToolId Display name Detection strategy Linux/macOS/Win paths
claude-code Claude Code PATH lookup (claude) which/where.exe
claude-desktop Claude Desktop candidate paths /Applications/Claude.app/..., %LOCALAPPDATA%\AnthropicClaude\Claude.exe
cursor Cursor candidate paths /Applications/Cursor.app/..., …\Programs\cursor\Cursor.exe
codex Codex CLI PATH lookup (codex) which/where.exe
codex-desktop Codex Desktop candidate paths (unreleased; speculative vendor paths) /Applications/Codex.app/...
hermes Hermes Agent PATH lookup (hermes) which/where.exe
openclaw OpenClaw PATH lookup (openclaw) which/where.exe

LAUNCH_COHORT = all 7. (The launcher's runtime guard message still says "claude-code, cursor, claude-desktop" — a stale Phase-2 string — but the actual array includes all 7.)

6.2 DetectedTool (per-tool result)

Field Type Meaning
id ToolId tool id
displayName string from TOOL_DISPLAY_NAMES
installed boolean true iff binary found at a known path
installedPath string | null absolute path to binary
version string | null best-effort (--version); null if exec failed
hooksInstalled boolean true iff hook pointer file exists and its referenced backup file still exists (partial rollback → false)
hookPointerPath string | null the pointer file probed (~/<config-dir>/hive-mind-install.json)
diagnostic? string optional human reason for a partial failure (e.g. '--version exec failed')

6.3 ToolDetectionResult (envelope) — returned by GET /api/tools/detect

Field Type Meaning
platform NodeJS.Platform | 'other' platform detection ran on
detectedAt string (ISO) completion timestamp
tools DetectedTool[] per-tool results, in SUPPORTED_TOOLS order (stable for the UI)

Implementation notes: detectInstalledTools(opts?) runs every detector in parallel (Promise.all) but preserves SUPPORTED_TOOLS order in the output. All fs/exec/PATH calls are injectable (ToolDetectionDeps) for hermetic tests. Hook-pointer config dirs are in HOOK_POINTER_BY_TOOL (e.g. claude-code.claude/hive-mind-install.json, cursor.cursor/..., codex/codex-desktop.codex/...).


7. AI-OS Phase 2 — launcher + hook installer (packages/agent/src/tool-launcher.ts + tool-process-tracker.ts)

7.1 launchTool(opts) → LaunchResult

Spawns the tool detached (detached: true, stdio: 'ignore', unref() so it outlives the sidecar). Injects WAGGLE_WORKSPACE_ID into the child env when workspaceId is given — this is the single env var the tool's hooks pick up (via shim-core workspace-resolver) to tag captured memory.

LaunchOptions: { id: ToolId; installedPath: string (required); workspaceId?; cwd? (default = dirname of binary); args?; deps? }. LaunchResult: { ok: boolean; pid: number | null; executed: { binary; args; cwd? }; error? }.

Guards: id must be in LAUNCH_COHORT (all 7); installedPath required (caller passes it from a fresh detect).

7.2 runHookCommand(opts) → HookCommandResult (reversible hooks)

Runs npx --yes @waggle/hive-mind-hooks-<id> <install|verify|uninstall> (adds --cli-path <path> only on install). Captures stdout/stderr/exit-code.

  • HookAction = 'install' | 'verify' | 'uninstall'.
  • HOOKS_COHORT = ['claude-code', 'codex', 'codex-desktop', 'cursor', 'hermes', 'openclaw'] (6 tools — every tool with a real bin). claude-desktop is excluded (binless stub). Hook actions gate on HOOKS_COHORT, not LAUNCH_COHORT, so the UI never offers a hook action npx can't fulfil.
  • hookPackageFor(id)@waggle/hive-mind-hooks-${id}.
  • HookCommandResult: { ok; action; packageName; stdout; stderr; code; error? }.

Reversibility: install writes a pointer file ~/<config>/hive-mind-install.json whose backup field points at the original config; uninstall restores it; detection's hooksInstalled flips false if the backup is gone (partial rollback is honestly reported).

7.3 ToolProcessTracker — the 'Running' badge backend

In-memory map of TrackedProcess { pid; toolId; startedAt; workspaceId? }. Not durable (dropped on restart — losing attribution is preferred over reporting stale liveness).

Method Behavior
register(pid, toolId, workspaceId?) record a freshly spawned pid (idempotent on pid)
list() GC dead pids (process.kill(pid,0) liveness), return alive ones
forget(pid) drop a pid
kill(pid, gracefulTimeoutMs=3000) refuses untracked pids; SIGTERM → wait → SIGKILL escalation; returns { ok; pid; reason } where reason ∈ not-tracked / already-dead / sigterm-ok / sigkill-ok / sigterm-failed-sigkill-failed

8. AI-OS Phase 1D — the shim-core emitter (packages/hive-mind-shim-core/src/signal-emitter.ts)

This is the library a tool's hook calls to push a signal back into Waggle. Fail-open by design: any error (ENOTFOUND, ECONNREFUSED, non-2xx, parse) returns null and logs one stderr warning — the host AI tool's hook chain never sees a failure. Uses only Node 20 built-in fetch, 2-second default timeout.

URL resolution: options.url > env.WAGGLE_SIDECAR_URL > http://127.0.0.1:3333.

  • emitSignalToWaggleDance(opts: EmitSignalOptions): Promise<EmittedSignal | null> — POSTs to <url>/api/waggle-dance/signal.
    • EmitSignalOptions: { type: SignalType; subtype: SignalSubtype; content: Record<string,unknown>; senderId? (default 'hook'); teamId?; referenceId?; routing?; url?; timeoutMs? (2000); fetchImpl?; onWarn? }.
    • SignalType / SignalSubtype mirror the protocol enums exactly.
    • EmittedSignal is the server-persisted message echoed back: { id; teamId; senderId; type; subtype; content; referenceId; routing; createdAt }.
  • maybeEmitDiscovery(eventType, importance, payload, opts) — convenience policy: emit a broadcast/discovery only when importance is 'high'|'critical' and eventType is 'stop'|'pre-compact'. Phase 1E wires the claude-code Stop hook to this (opt-in via WAGGLE_SIGNAL_EMIT).

Hook event vocabulary (hook-event-types.ts): EventType = session-start | session-end | user-prompt-submit | pre-compact | stop | pre-tool-use | post-tool-use; ShimSource = claude-code | cursor | hermes | codex | opencode | openclaw.


9. The end-to-end AI-OS flow: detect → launch → signal → UI

sequenceDiagram
  participant UI as Web UI (LauncherApp / Mission Control)
  participant SC as Sidecar (Fastify @ :3333)
  participant DET as detectInstalledTools()
  participant LT as launchTool()
  participant EXT as External AI tool (Claude Code, …)
  participant HOOK as Tool hook (+ shim-core)
  participant BUS as SignalBus (v2)
  participant BR as installWaggleDanceBridge
  participant STREAM as /api/waggle/stream (SSE)

  UI->>SC: GET /api/tools/detect
  SC->>DET: run 7 detectors in parallel
  DET-->>SC: ToolDetectionResult { tools[] }
  SC-->>UI: 200 ToolDetectionResult

  UI->>SC: POST /api/tools/hooks { id, action:'install' }
  SC->>EXT: npx @waggle/hive-mind-hooks-<id> install (reversible)
  SC-->>UI: 200 HookCommandResult

  UI->>SC: POST /api/tools/launch { id, installedPath, workspaceId }
  SC->>LT: spawn detached, env WAGGLE_WORKSPACE_ID
  LT-->>SC: { ok, pid }
  SC->>SC: toolProcessTracker.register(pid,...)
  SC-->>UI: 202 LaunchResult { pid }

  EXT->>HOOK: lifecycle event (stop / pre-compact)
  HOOK->>SC: POST /api/waggle-dance/signal (emitSignalToWaggleDance, fail-open)
  SC->>SC: WaggleDanceDispatcher.dispatch()
  SC->>BUS: emitSignal → bus.record(WaggleMessage)
  BUS->>BR: subscriber fires
  BR->>SC: emitWaggleSignal(mapped WaggleSignal)
  SC-->>STREAM: event: signal (SSE)
  STREAM-->>UI: live cross-tool activity

Rollback anchor for the whole arc: git tag checkpoint/pre-ai-os-2026-05-20.


10. Local sub-agent + cross-workspace layer (same vocabulary, different transport)

These are agent tools (callable by the LLM), not HTTP routes, but the frontend sees their effects through the notifications/Room stream and they share the coordination theme.

10.1 Sub-agent spawning — createSubAgentTools(deps) (subagent-tools.ts)

Exposes 3 tools to the main agent. Sub-agents run in-process via the shared runLoop. In-memory registries: activeAgents + agentResults (capped at MAX_AGENT_RESULTS = 100, stale eviction at STALE_THRESHOLD_MS = 30 min).

Tool Inputs Output
spawn_agent name, role, task (required); context?, tools? (role=custom only), model?, max_turns? (50) Markdown sub-agent result (role, duration, tools used, tokens, content)
list_agents none Active + completed agents with previews
get_agent_result agent_id (or name) Full stored result

ROLE_TOOL_PRESETS maps role → tool allowlist: researcher, writer, coder, analyst, reviewer, planner (default fallback = analyst). Lifecycle is surfaced to the UI via onSubAgentStatus (running/done/error) which the server wires to emitSubagentStatus → the Room canvas. onSubAgentComplete persists results to the active mind (best-effort, never throws).

SubAgentResult: { agentId; agentName; role; response; usage{inputTokens,outputTokens}; toolsUsed[]; duration; completedAt }. SubAgentStatusEvent: { agentId; name; role; status; task; toolsUsed[]; startedAt; completedAt? }.

10.2 Workflow orchestration — SubagentOrchestrator (subagent-orchestrator.ts)

Supervisor/worker pattern over spawn_agent. runWorkflow(template) executes steps in dependency order (topological, sequential), injects upstream results as context, then aggregates. Emits worker:status events (an EventEmitter).

  • WorkflowStep: { name; role; task; tools?; dependsOn?; contextFrom?; maxTurns? }.
  • WorkflowTemplate: { name; description; steps[]; aggregation: 'concatenate' | 'last' | 'synthesize' }.
  • WorkerState: { id; name; role; status: 'pending'|'running'|'done'|'failed'; task; startedAt?; completedAt?; result?; error?; toolsUsed[]; usage }.
  • Adds synthesizer + summarizer to the role presets. Circular dependency → remaining steps marked failed with a clear error.

10.3 Cross-workspace messaging — AgentMessageBus + createAgentCommsTools

AgentMessageBus (agent-message-bus.ts) is an in-memory, per-machine bus for concurrent agent sessions in different workspaces (distinct from team/PostgreSQL messaging). AgentMessage: { id; from (workspaceId); to (workspaceId); content; correlationId?; timestamp; ttlMs }. Default TTL = 5 min; receive() is one-shot (drains + filters expired); peek() is non-destructive; cleanup() GCs expired.

createAgentCommsTools(bus, currentWorkspaceId, isSessionActive?) exposes 2 tools:

Tool Inputs Notes
send_agent_message workspace, message, correlationId? Rejects self-send; rejects target whose session isn't active
check_agent_messages none Consumes pending messages for the current workspace

11. API reference — every route

All routes are served by the local sidecar (Fastify, loopback :3333). Registration in packages/server/src/local/index.ts: toolsRoutes (1990), waggleDanceRoutes (1991), waggleSignalRoutes (2016). The signalBus and toolProcessTracker decorations propagate to the parent instance via fastify-plugin.

Method Full path Request shape Response shape Streaming?
POST /api/waggle-dance/signal { type, subtype, content, senderId?, teamId?, referenceId?, routing? } (Zod signalRequestSchema) 201 { dispatched:true, response, message: WaggleMessage } · 400 { error, details? } No
GET /api/waggle-dance/signals query { subtype?, tool?, teamId?, limit?(≤1000), since? } 200 { signals: WaggleMessage[], total } (newest-first) · 400 No
GET /api/waggle/signals query { limit?(≤200, default 50), unacked?('1') } { signals: WaggleSignal[], total } No
POST /api/waggle/signals { type, content, workspaceId?, metadata? } 201 WaggleSignal · 400 { error } No
PATCH /api/waggle/signals/:id/ack path :id { acknowledged:true, id } No
GET /api/waggle/stream — (SSE; Origin echoed only if allow-listed) event: signal frames of WaggleSignal; :heartbeat every 30s Yes (SSE)
GET /api/tools/detect 200 ToolDetectionResult · 500 { error, message } No
POST /api/tools/launch { id, installedPath, workspaceId?, cwd?, args? } (Zod) 202 LaunchResult · 400 LaunchResult/validation No
GET /api/tools/processes 200 { processes: TrackedProcess[], total } No
POST /api/tools/kill { pid } 200 { ok, pid, reason } · 404 not-tracked · 500 No
POST /api/tools/hooks { id, action:'install'|'verify'|'uninstall', cliPath? } 200 HookCommandResult · 400 · 500 { error, message } No

Server-side defaulting on POST /api/waggle-dance/signal: id=randomUUID(), createdAt=now, senderId='local' if omitted, teamId=personal::<senderId> if omitted, referenceId/routing=null. The route wires real v2 deps (emitSignalbus.record, recordResponsebus.record, recommendModelnull stub). v1 deps are local stubs (the team-internal paths run through the cloud team workspace, not this loopback route).


12. Phase 3 — skill diffusion (closed-loop → bus)

When the agent's D1 closed learning loop fires (onSkillDistillationFire, wired in chat.ts), the server records a skill_share broadcast directly onto server.signalBus:

{ type:'broadcast', subtype:'skill_share',
  teamId:'personal::<workspace>', senderId:'agent-loop:<persona>',
  content:{ tool:'waggle-agent', patternKey, toolsUsed[], directive, sessionId, workspaceId } }

Via the bridge this surfaces in the UI as a waggle-dance:handoff signal, so MCP-consuming external tools can adopt the soon-to-be-authored skill. The whole emission is best-effort and gated on server.signalBus existing.


13. Frontend rebuild checklist

  • Activity feed (WaggleDanceApp): consume GET /api/waggle/stream (SSE) for live signals and GET /api/waggle/signals for backfill; render by the 5 UX categories; ack via PATCH /api/waggle/signals/:id/ack. Drill-down detail is in metadata.protocolMessage (the raw WaggleMessage). If you want the raw protocol plane instead, poll GET /api/waggle-dance/signals.
  • Launcher dock (LauncherApp): GET /api/tools/detect to list the 7 tools (installed/version/hooksInstalled); POST /api/tools/hooks for install/verify/uninstall (only enable for the 6 HOOKS_COHORT tools; claude-desktop has no hook action); POST /api/tools/launch (pass installedPath from the detect result + the current workspaceId); poll GET /api/tools/processes for the 'Running' badge; POST /api/tools/kill for stop.
  • Mission Control inventory tile: the same ToolDetectionResult (count installed, count hooked).
  • Room sub-agent tiles: driven by the notifications stream (subagent_status), fed by onSubAgentStatus/emitSubagentStatus; the underlying tools are spawn_agent/list_agents/get_agent_result.

14. Source map

Concern File
Protocol types packages/shared/src/types.ts (WaggleMessage, MessageType, MessageSubtype)
Combo validation packages/waggle-dance/src/protocol.ts
Dispatcher packages/waggle-dance/src/dispatcher.ts
Hive-query types packages/waggle-dance/src/hive-query.ts
v2 ring buffer packages/server/src/local/signal-bus.ts
v2 routes packages/server/src/local/routes/waggle-dance.ts
Bridge v2→legacy packages/server/src/local/waggle-dance-bridge.ts
Legacy stream + SSE packages/server/src/local/routes/waggle-signals.ts
Tool-detection types packages/shared/src/tool-detection.ts
Tool-detection impl packages/agent/src/tool-detection.ts
Launcher + hooks packages/agent/src/tool-launcher.ts
Process tracker packages/agent/src/tool-process-tracker.ts
Tools routes packages/server/src/local/routes/tools.ts
Shim-core emitter packages/hive-mind-shim-core/src/signal-emitter.ts
Hook event vocab packages/hive-mind-shim-core/src/hook-event-types.ts
Sub-agent tools packages/agent/src/subagent-tools.ts
Orchestrator packages/agent/src/subagent-orchestrator.ts
Cross-workspace bus packages/agent/src/agent-message-bus.ts + agent-comms-tools.ts
Skill-diffusion wiring packages/server/src/local/routes/chat.ts (onSkillDistillationFire)