@elizaos/plugin-agent-orchestrator 2.0.0-beta.1 → 2.0.11-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/CHANGELOG.md +6 -8
  2. package/README.md +102 -30
  3. package/docs/SUBAGENT_FLOW_AND_PARITY.md +138 -0
  4. package/docs/default-eliza-skills-and-agent-bridge-plan.md +9 -15
  5. package/docs/economics-goal-runbook.md +140 -0
  6. package/docs/multi-account-orchestration.md +76 -0
  7. package/docs/orchestrator-buildout-followups.md +124 -0
  8. package/docs/orchestrator-dashboard-task-widget-secrets-assessment.md +174 -0
  9. package/docs/orchestrator-dashboard-task-widget-secrets-design.md +301 -0
  10. package/docs/research/orchestrator-view-research-report.md +273 -0
  11. package/docs/sub-agent-routing.md +53 -23
  12. package/package.json +108 -25
  13. package/PROJECT.md +0 -119
  14. package/dist/browser/index.browser.js +0 -21051
  15. package/dist/browser/index.browser.js.map +0 -29
  16. package/dist/browser/index.d.ts +0 -2
  17. package/dist/cjs/index.d.ts +0 -2
  18. package/dist/cjs/index.node.cjs +0 -20491
  19. package/dist/cjs/index.node.js.map +0 -72
  20. package/dist/index.d.ts +0 -2
  21. package/dist/node/index.browser.d.ts +0 -3
  22. package/dist/node/index.browser.d.ts.map +0 -1
  23. package/dist/node/index.d.ts +0 -2
  24. package/dist/node/index.d.ts.map +0 -1
  25. package/dist/node/index.node.d.ts +0 -4
  26. package/dist/node/index.node.d.ts.map +0 -1
  27. package/dist/node/index.node.js +0 -20471
  28. package/dist/node/index.node.js.map +0 -72
  29. package/dist/node/src/actions/cancel-task.d.ts +0 -3
  30. package/dist/node/src/actions/cancel-task.d.ts.map +0 -1
  31. package/dist/node/src/actions/coding-task-handlers.d.ts +0 -69
  32. package/dist/node/src/actions/coding-task-handlers.d.ts.map +0 -1
  33. package/dist/node/src/actions/coding-task-helpers.d.ts +0 -35
  34. package/dist/node/src/actions/coding-task-helpers.d.ts.map +0 -1
  35. package/dist/node/src/actions/common.d.ts +0 -56
  36. package/dist/node/src/actions/common.d.ts.map +0 -1
  37. package/dist/node/src/actions/create-task.d.ts +0 -8
  38. package/dist/node/src/actions/create-task.d.ts.map +0 -1
  39. package/dist/node/src/actions/eval-metadata.d.ts +0 -11
  40. package/dist/node/src/actions/eval-metadata.d.ts.map +0 -1
  41. package/dist/node/src/actions/finalize-workspace.d.ts +0 -11
  42. package/dist/node/src/actions/finalize-workspace.d.ts.map +0 -1
  43. package/dist/node/src/actions/list-agents.d.ts +0 -4
  44. package/dist/node/src/actions/list-agents.d.ts.map +0 -1
  45. package/dist/node/src/actions/manage-issues.d.ts +0 -11
  46. package/dist/node/src/actions/manage-issues.d.ts.map +0 -1
  47. package/dist/node/src/actions/provision-workspace.d.ts +0 -11
  48. package/dist/node/src/actions/provision-workspace.d.ts.map +0 -1
  49. package/dist/node/src/actions/sandbox-stub.d.ts +0 -21
  50. package/dist/node/src/actions/sandbox-stub.d.ts.map +0 -1
  51. package/dist/node/src/actions/send-to-agent.d.ts +0 -4
  52. package/dist/node/src/actions/send-to-agent.d.ts.map +0 -1
  53. package/dist/node/src/actions/spawn-agent.d.ts +0 -8
  54. package/dist/node/src/actions/spawn-agent.d.ts.map +0 -1
  55. package/dist/node/src/actions/stop-agent.d.ts +0 -4
  56. package/dist/node/src/actions/stop-agent.d.ts.map +0 -1
  57. package/dist/node/src/actions/task-control.d.ts +0 -3
  58. package/dist/node/src/actions/task-control.d.ts.map +0 -1
  59. package/dist/node/src/actions/task-history.d.ts +0 -3
  60. package/dist/node/src/actions/task-history.d.ts.map +0 -1
  61. package/dist/node/src/actions/task-share.d.ts +0 -3
  62. package/dist/node/src/actions/task-share.d.ts.map +0 -1
  63. package/dist/node/src/actions/task-thread-target.d.ts +0 -11
  64. package/dist/node/src/actions/task-thread-target.d.ts.map +0 -1
  65. package/dist/node/src/actions/tasks.d.ts +0 -88
  66. package/dist/node/src/actions/tasks.d.ts.map +0 -1
  67. package/dist/node/src/api/agent-routes.d.ts +0 -18
  68. package/dist/node/src/api/agent-routes.d.ts.map +0 -1
  69. package/dist/node/src/api/bridge-routes.d.ts +0 -32
  70. package/dist/node/src/api/bridge-routes.d.ts.map +0 -1
  71. package/dist/node/src/api/coordinator-routes.d.ts +0 -22
  72. package/dist/node/src/api/coordinator-routes.d.ts.map +0 -1
  73. package/dist/node/src/api/hook-routes.d.ts +0 -18
  74. package/dist/node/src/api/hook-routes.d.ts.map +0 -1
  75. package/dist/node/src/api/issue-routes.d.ts +0 -17
  76. package/dist/node/src/api/issue-routes.d.ts.map +0 -1
  77. package/dist/node/src/api/parent-context-routes.d.ts +0 -17
  78. package/dist/node/src/api/parent-context-routes.d.ts.map +0 -1
  79. package/dist/node/src/api/route-utils.d.ts +0 -18
  80. package/dist/node/src/api/route-utils.d.ts.map +0 -1
  81. package/dist/node/src/api/routes.d.ts +0 -23
  82. package/dist/node/src/api/routes.d.ts.map +0 -1
  83. package/dist/node/src/api/workspace-routes.d.ts +0 -17
  84. package/dist/node/src/api/workspace-routes.d.ts.map +0 -1
  85. package/dist/node/src/index.d.ts +0 -32
  86. package/dist/node/src/index.d.ts.map +0 -1
  87. package/dist/node/src/providers/action-examples.d.ts +0 -14
  88. package/dist/node/src/providers/action-examples.d.ts.map +0 -1
  89. package/dist/node/src/providers/active-sub-agents.d.ts +0 -15
  90. package/dist/node/src/providers/active-sub-agents.d.ts.map +0 -1
  91. package/dist/node/src/providers/active-workspace-context.d.ts +0 -13
  92. package/dist/node/src/providers/active-workspace-context.d.ts.map +0 -1
  93. package/dist/node/src/providers/available-agents.d.ts +0 -4
  94. package/dist/node/src/providers/available-agents.d.ts.map +0 -1
  95. package/dist/node/src/register-routes.d.ts +0 -11
  96. package/dist/node/src/register-routes.d.ts.map +0 -1
  97. package/dist/node/src/routes/coding-agents-fallback-routes.d.ts +0 -19
  98. package/dist/node/src/routes/coding-agents-fallback-routes.d.ts.map +0 -1
  99. package/dist/node/src/services/acp-service.d.ts +0 -61
  100. package/dist/node/src/services/acp-service.d.ts.map +0 -1
  101. package/dist/node/src/services/acpx-subprocess.d.ts +0 -3
  102. package/dist/node/src/services/acpx-subprocess.d.ts.map +0 -1
  103. package/dist/node/src/services/agent-credentials.d.ts +0 -23
  104. package/dist/node/src/services/agent-credentials.d.ts.map +0 -1
  105. package/dist/node/src/services/agent-metrics.d.ts +0 -30
  106. package/dist/node/src/services/agent-metrics.d.ts.map +0 -1
  107. package/dist/node/src/services/agent-selection.d.ts +0 -53
  108. package/dist/node/src/services/agent-selection.d.ts.map +0 -1
  109. package/dist/node/src/services/ansi-utils.d.ts +0 -64
  110. package/dist/node/src/services/ansi-utils.d.ts.map +0 -1
  111. package/dist/node/src/services/claude-code-skill-installer.d.ts +0 -33
  112. package/dist/node/src/services/claude-code-skill-installer.d.ts.map +0 -1
  113. package/dist/node/src/services/config-env.d.ts +0 -23
  114. package/dist/node/src/services/config-env.d.ts.map +0 -1
  115. package/dist/node/src/services/coordinator-event-normalizer.d.ts +0 -50
  116. package/dist/node/src/services/coordinator-event-normalizer.d.ts.map +0 -1
  117. package/dist/node/src/services/custom-validator-runner.d.ts +0 -66
  118. package/dist/node/src/services/custom-validator-runner.d.ts.map +0 -1
  119. package/dist/node/src/services/debug-capture.d.ts +0 -38
  120. package/dist/node/src/services/debug-capture.d.ts.map +0 -1
  121. package/dist/node/src/services/json-model-output.d.ts +0 -2
  122. package/dist/node/src/services/json-model-output.d.ts.map +0 -1
  123. package/dist/node/src/services/parent-agent-broker.d.ts +0 -21
  124. package/dist/node/src/services/parent-agent-broker.d.ts.map +0 -1
  125. package/dist/node/src/services/pty-auto-response.d.ts +0 -30
  126. package/dist/node/src/services/pty-auto-response.d.ts.map +0 -1
  127. package/dist/node/src/services/pty-init.d.ts +0 -55
  128. package/dist/node/src/services/pty-init.d.ts.map +0 -1
  129. package/dist/node/src/services/pty-service.d.ts +0 -218
  130. package/dist/node/src/services/pty-service.d.ts.map +0 -1
  131. package/dist/node/src/services/pty-session-io.d.ts +0 -49
  132. package/dist/node/src/services/pty-session-io.d.ts.map +0 -1
  133. package/dist/node/src/services/pty-spawn.d.ts +0 -104
  134. package/dist/node/src/services/pty-spawn.d.ts.map +0 -1
  135. package/dist/node/src/services/pty-types.d.ts +0 -94
  136. package/dist/node/src/services/pty-types.d.ts.map +0 -1
  137. package/dist/node/src/services/repo-input.d.ts +0 -16
  138. package/dist/node/src/services/repo-input.d.ts.map +0 -1
  139. package/dist/node/src/services/session-event-queue.d.ts +0 -25
  140. package/dist/node/src/services/session-event-queue.d.ts.map +0 -1
  141. package/dist/node/src/services/session-store.d.ts +0 -107
  142. package/dist/node/src/services/session-store.d.ts.map +0 -1
  143. package/dist/node/src/services/skill-callback-bridge.d.ts +0 -78
  144. package/dist/node/src/services/skill-callback-bridge.d.ts.map +0 -1
  145. package/dist/node/src/services/skill-essentials.d.ts +0 -16
  146. package/dist/node/src/services/skill-essentials.d.ts.map +0 -1
  147. package/dist/node/src/services/skill-lifeops-context-broker.d.ts +0 -20
  148. package/dist/node/src/services/skill-lifeops-context-broker.d.ts.map +0 -1
  149. package/dist/node/src/services/skill-manifest.d.ts +0 -48
  150. package/dist/node/src/services/skill-manifest.d.ts.map +0 -1
  151. package/dist/node/src/services/skill-recommender.d.ts +0 -51
  152. package/dist/node/src/services/skill-recommender.d.ts.map +0 -1
  153. package/dist/node/src/services/spawn-trajectory.d.ts +0 -23
  154. package/dist/node/src/services/spawn-trajectory.d.ts.map +0 -1
  155. package/dist/node/src/services/stall-classifier.d.ts +0 -69
  156. package/dist/node/src/services/stall-classifier.d.ts.map +0 -1
  157. package/dist/node/src/services/structured-proof-bridge.d.ts +0 -99
  158. package/dist/node/src/services/structured-proof-bridge.d.ts.map +0 -1
  159. package/dist/node/src/services/sub-agent-router.d.ts +0 -38
  160. package/dist/node/src/services/sub-agent-router.d.ts.map +0 -1
  161. package/dist/node/src/services/swarm-coordinator-prompts.d.ts +0 -93
  162. package/dist/node/src/services/swarm-coordinator-prompts.d.ts.map +0 -1
  163. package/dist/node/src/services/swarm-coordinator.d.ts +0 -473
  164. package/dist/node/src/services/swarm-coordinator.d.ts.map +0 -1
  165. package/dist/node/src/services/swarm-decision-loop.d.ts +0 -69
  166. package/dist/node/src/services/swarm-decision-loop.d.ts.map +0 -1
  167. package/dist/node/src/services/swarm-event-triage.d.ts +0 -49
  168. package/dist/node/src/services/swarm-event-triage.d.ts.map +0 -1
  169. package/dist/node/src/services/swarm-history.d.ts +0 -27
  170. package/dist/node/src/services/swarm-history.d.ts.map +0 -1
  171. package/dist/node/src/services/swarm-idle-watchdog.d.ts +0 -22
  172. package/dist/node/src/services/swarm-idle-watchdog.d.ts.map +0 -1
  173. package/dist/node/src/services/task-acceptance.d.ts +0 -8
  174. package/dist/node/src/services/task-acceptance.d.ts.map +0 -1
  175. package/dist/node/src/services/task-agent-auth.d.ts +0 -69
  176. package/dist/node/src/services/task-agent-auth.d.ts.map +0 -1
  177. package/dist/node/src/services/task-agent-frameworks.d.ts +0 -90
  178. package/dist/node/src/services/task-agent-frameworks.d.ts.map +0 -1
  179. package/dist/node/src/services/task-kind.d.ts +0 -3
  180. package/dist/node/src/services/task-kind.d.ts.map +0 -1
  181. package/dist/node/src/services/task-policy.d.ts +0 -17
  182. package/dist/node/src/services/task-policy.d.ts.map +0 -1
  183. package/dist/node/src/services/task-registry.d.ts +0 -550
  184. package/dist/node/src/services/task-registry.d.ts.map +0 -1
  185. package/dist/node/src/services/task-share.d.ts +0 -18
  186. package/dist/node/src/services/task-share.d.ts.map +0 -1
  187. package/dist/node/src/services/task-validation.d.ts +0 -69
  188. package/dist/node/src/services/task-validation.d.ts.map +0 -1
  189. package/dist/node/src/services/task-verifier-runner.d.ts +0 -5
  190. package/dist/node/src/services/task-verifier-runner.d.ts.map +0 -1
  191. package/dist/node/src/services/trajectory-context.d.ts +0 -73
  192. package/dist/node/src/services/trajectory-context.d.ts.map +0 -1
  193. package/dist/node/src/services/trajectory-feedback.d.ts +0 -53
  194. package/dist/node/src/services/trajectory-feedback.d.ts.map +0 -1
  195. package/dist/node/src/services/types.d.ts +0 -152
  196. package/dist/node/src/services/types.d.ts.map +0 -1
  197. package/dist/node/src/services/workspace-git-ops.d.ts +0 -28
  198. package/dist/node/src/services/workspace-git-ops.d.ts.map +0 -1
  199. package/dist/node/src/services/workspace-github.d.ts +0 -60
  200. package/dist/node/src/services/workspace-github.d.ts.map +0 -1
  201. package/dist/node/src/services/workspace-lifecycle.d.ts +0 -18
  202. package/dist/node/src/services/workspace-lifecycle.d.ts.map +0 -1
  203. package/dist/node/src/services/workspace-service.d.ts +0 -122
  204. package/dist/node/src/services/workspace-service.d.ts.map +0 -1
  205. package/dist/node/src/services/workspace-types.d.ts +0 -81
  206. package/dist/node/src/services/workspace-types.d.ts.map +0 -1
  207. package/dist/node/src/setup-routes.d.ts +0 -9
  208. package/dist/node/src/setup-routes.d.ts.map +0 -1
  209. package/dist/node/vitest.config.d.ts +0 -3
  210. package/dist/node/vitest.config.d.ts.map +0 -1
@@ -0,0 +1,76 @@
1
+ # Multi-account coding-agent orchestration
2
+
3
+ Goal: an Eliza orchestrator agent that runs **multiple Claude Code + Codex
4
+ subscriptions** (and rotates OpenCode across pooled Cerebras accounts), picks
5
+ the **least-used** account for each new
6
+ sub-agent, tracks per-account **session + weekly usage**, manages those
7
+ sub-agents in a **shared task room**, and decides **when to interrupt** a
8
+ running sub-agent vs. let it keep working.
9
+
10
+ ## What already existed (do not rebuild)
11
+
12
+ | Layer | Location | Status |
13
+ |---|---|---|
14
+ | Account contracts (`LinkedAccountConfig`, `LinkedAccountUsage`, 12 provider ids) | `packages/contracts/src/service-routing.ts` | ✅ |
15
+ | Credential storage (`<stateDir>/auth/{providerId}/{accountId}.json`) | `packages/agent/src/auth/account-storage.ts` | ✅ |
16
+ | OAuth flows (Anthropic + Codex) + coding-plan keys + API keys | `packages/agent/src/auth/oauth-flow.ts`, `credentials.ts` | ✅ |
17
+ | `AccountPool` — priority / round-robin / **least-used** / quota-aware, affinity, health | `packages/app-core/src/services/account-pool.ts` | ✅ |
18
+ | Usage probes (`pollAnthropicUsage`, `pollCodexUsage`) + JSONL day counters | `packages/app-core/src/services/account-usage.ts` | ✅ |
19
+ | Accounts REST API (`/api/accounts/*` incl. OAuth SSE) | `packages/agent/src/api/accounts-routes.ts` | ✅ |
20
+ | Settings UI (AccountList / AccountCard / AddAccountDialog / RotationStrategyPicker) + `useAccounts` | `packages/ui/src/components/accounts/*` | ✅ |
21
+ | Multi-account API-key routing for the *main agent* | `credential-resolver.ts` → `resolveProviderCredentialMulti` | ✅ |
22
+ | Orchestrator tasks, sessions, task-rooms, event bridge, usage rollup, REST + SSE | `plugins/plugin-agent-orchestrator/*` | ✅ |
23
+ | `shouldRespond` (RESPOND/IGNORE/STOP) + per-room `TurnControllerRegistry` abort | `packages/core/src/*` | ✅ |
24
+
25
+ ## The keystone gap
26
+
27
+ The `AccountPool` was wired into **API-key model routing** but **not into the
28
+ coding-agent spawn path**. Coding agents authenticate by *subscription*, not API
29
+ key: `applySubscriptionCredentialsLocal` never injects Claude/Codex tokens into
30
+ `process.env`, and `AcpService.buildEnv` even *strips* an OAuth
31
+ `ANTHROPIC_API_KEY` so Claude Code falls back to the single machine login
32
+ (`~/.claude`). So every spawned sub-agent used one account, with no rotation and
33
+ no per-account attribution.
34
+
35
+ The orchestrator plugin depends only on `@elizaos/core`, so it cannot import the
36
+ pool. `account-pool.ts` already solves this with `globalThis`-symbol **bridges**
37
+ (Anthropic + subscription-selector). We add a third: a **coding-agent selector
38
+ bridge**.
39
+
40
+ ## Hitlist
41
+
42
+ ### P0 — Keystone: account selection on spawn (round-robin / least-used)
43
+ - [x] `coding-account-bridge.ts` (app-core): install `Symbol.for("eliza.account-pool.coding-agent.v1")` bridge — `select(agentType)`, `markRateLimited`, `markNeedsReauth`, `recordUsage`, `describe()`.
44
+ - [x] Per-agent credential injection: claude → `CLAUDE_CODE_OAUTH_TOKEN`; codex → per-account `CODEX_HOME/auth.json` + minimal `config.toml`; opencode → `CEREBRAS_API_KEY` (pooled Cerebras); direct API providers → their env key.
45
+ - [x] Wire into `AcpService.spawnSession`: select before transport branch, merge `envPatch` into `customCredentials`, stamp `session.metadata.account*`, surface on `SpawnResult.metadata`.
46
+ - [x] `buildEnv`: when `CLAUDE_CODE_OAUTH_TOKEN` is injected for claude, drop `ANTHROPIC_API_KEY` so the selected subscription wins.
47
+ - [x] `OrchestratorTaskSession` carries `accountProviderId` / `accountId` / `accountLabel`; populate from `result.metadata`.
48
+ - [x] Exclude-on-failure retry: on auth/rate-limit error, mark account + re-select excluding it.
49
+
50
+ ### P1 — Usage attribution + stats
51
+ - [x] `recordUsage` also calls the bridge → pool `recordCall` + `account-usage` JSONL keyed by the serving account.
52
+ - [x] `/api/orchestrator/accounts` route: connected accounts + live usage + which sub-agents are on which account.
53
+ - [x] Settings already shows per-account usage; orchestrator dashboard widget adds the accounts + usage summary.
54
+
55
+ ### P2 — Room system + interruption decider
56
+ - [x] Per-participant interruption decider: a running sub-agent keeps working; a new room message is classified (deliver / queue / interrupt / ignore) before it is injected. The "busy" check covers `tool_running` (the dominant mid-turn state), not just `busy`; queued messages flush when the session returns to `ready`. An Eliza participant's `shouldRespond` verdict threads through unchanged when a caller supplies it; coding sub-agents use the structural decider. `multiParty` (more than one live sub-agent in the room) is computed in the forward handler so ambient-chatter suppression is live.
57
+ - [x] Task-room participant **view**: the "Coding accounts" sidebar widget shows the live sub-agent → account assignment map + per-account usage (the flat global map). The dedicated per-room participant roster (orchestrator + owning user + each sub-agent grouped by room, with `activeAgentCount` / `multiParty`) now ships as `GET /api/orchestrator/rooms` (`OrchestratorTaskService.getRoomRoster`), the room-scoped counterpart to `/accounts`. Rooms with no sub-agent sessions are omitted.
58
+
59
+ ### P3 — Tests + mocks + live E2E
60
+ - [x] Mock account fixtures (multi-account, multi-provider) + an in-memory pool for unit tests.
61
+ - [x] Unit tests: selection strategy, per-agent env injection + API-key drop (Claude & Codex), intra-provider retry, usage attribution, interruption decider, route registration.
62
+ - [x] Live E2E (gated `ORCHESTRATOR_LIVE_MULTI_ACCOUNT=1`): validates the selector bridge + usage probe against ≥2 **real** linked accounts (distinct-account rotation via the exclude set; live session/weekly usage).
63
+ - [x] **Composed offline E2E** (`bun run --cwd plugins/plugin-agent-orchestrator test:e2e:multi-account`): real app-core `AccountPool` (real-format mock accounts in a temp state dir) → installed selector bridge → orchestrator `AcpService.spawnSession` → a REAL spawned subprocess (a fake "acpx" standing in for the subscription CLI). Asserts 10/10: two Claude spawns pick **distinct** accounts (least-used round-robin), each subprocess receives its account's `CLAUDE_CODE_OAUTH_TOKEN`, the parent `ANTHROPIC_API_KEY`/`OPENAI_API_KEY` are dropped, Codex gets a per-account `CODEX_HOME/auth.json` with the right token+account_id. Point `ELIZA_ACP_CLI` at the real binary + connect real accounts for true live-key validation. (This harness caught a real cli-transport bug: `runAcpx` rebuilt the env without `agentType`, re-adding the parent keys — now fixed.)
64
+
65
+ ### P4 — Connect-accounts window
66
+ - [x] The connect-accounts window (Settings → Accounts: `AddAccountDialog` OAuth / API-key / coding-plan-key flows, `AccountList`, `RotationStrategyPicker`) pre-exists and is the surface for linking multiple accounts of each type.
67
+
68
+ ## Known constraints / follow-ups
69
+ - **OpenCode pool-rotates across Cerebras accounts only.** OpenCode resolves a pooled key for exactly one backend — Cerebras (`CEREBRAS_API_KEY`, see `buildOpencodeSpawnConfig`) — so `opencode` is a multi-account selector type mapped to `cerebras-api`: it least-used-rotates across linked Cerebras accounts (the bridge injects the selected `CEREBRAS_API_KEY`, which OpenCode's config reads) and no-ops when none are linked (Eliza Cloud / single-key setups are unchanged). OpenCode's other backends (Eliza Cloud, local, user-configured opencode.json) are not pooled. Precedence: a `CEREBRAS_API_KEY` runtime **setting** still wins over a pooled injection — pooling is authoritative only when no single key is configured.
70
+ - **z.ai / Kimi / GLM have no first-party coding CLI.** Their linked accounts serve the main runtime's API-key routing (`resolveProviderCredentialMulti` for `zai-api` / `moonshot-api`) and OpenCode's provider config — there is no `zai`/`kimi`/`glm` spawnable agent type, so they are not advertised as coding-agent selector candidates.
71
+
72
+ ## Quality bar
73
+ - No regression when zero accounts are linked (bridge returns null → today's behavior).
74
+ - Selected account is **observable** (session metadata + structured log + dashboard), never assumed.
75
+ - Subscription tokens only ever flow to the first-party coding subprocess (TOS), never into runtime `process.env`.
76
+ - Per-agent credential precedence is enforced: a selected Claude subscription drops `ANTHROPIC_API_KEY`; a selected Codex subscription (per-account `CODEX_HOME`) drops a forwarded `OPENAI_API_KEY` — so the chosen account always authenticates.
@@ -0,0 +1,124 @@
1
+ # Orchestrator (`/orchestrator`) build-out — status & follow-ups
2
+
3
+ Date: 2026-05-29. Companion to [`research/orchestrator-view-research-report.md`](./research/orchestrator-view-research-report.md).
4
+
5
+ This records what was finished in the `nubs/orchestrator-view-feature` branch and the
6
+ exact, code-grounded plan for the remaining design-heavy work. The earlier research
7
+ report is largely **out of date**: the durable task model, SQL store, `/api/orchestrator/*`
8
+ routes, central goal-wrapper, validation-gated completion, token aggregation, the
9
+ `OrchestratorWorkbench` view, and the real (non-stub) client methods all already exist
10
+ (Shaw shipped most of it on 2026-05-29). Verify against current code, not the report's
11
+ line numbers.
12
+
13
+ ## Done in this branch (verified)
14
+
15
+ 1. **Mounted the sub-agent credential bridge** (`plugin-agent-orchestrator/src/api/routes.ts`).
16
+ `handleBridgeRoutes` was built+tested but never dispatched. + dispatcher regression test.
17
+ 2. **Unified the goal-wrapper** so the `TASKS` planner action uses the central
18
+ `buildGoalPrompt`/`buildGoalFollowUp` (`goal-prompt.ts`, `actions/tasks.ts`) instead of a
19
+ divergent inline envelope. Workspace-routing / URL-mapping / swarm-room content preserved
20
+ via new optional `GoalPromptInput` fields. + equivalence test (action env == API env).
21
+ 3. **Folded durable task threads into `getCodingAgentStatus`** (`packages/ui` client) — was
22
+ hardcoded to `taskThreadCount:0`. Documented the two genuinely-vestigial ACP stubs
23
+ (`/metrics`, `/workspace-files`) pointing at the real `/api/orchestrator/*` usage surface.
24
+ 4. **Declared capability param schemas** for the 15 orchestrator view capabilities so
25
+ voice/NL routing can pass `taskId`/`title`/`goal`/etc. **Mounted the sensitive-request
26
+ REST routes** (`packages/app-core/src/api/server.ts`) — the secret create/submit/fulfill
27
+ path that orchestrator provider setup will use.
28
+ 5. **Locked capability manifest↔dispatch parity** with a regression test
29
+ (`plugin-task-coordinator/__tests__/unit/orchestrator-capability-parity.test.ts`) + a
30
+ `test` script so CI runs it. The declared capabilities (`ORCHESTRATOR_CAPABILITIES` in
31
+ `index.ts`) must exactly match the ids `runOrchestratorCapability` dispatches; this drift
32
+ previously reopened (`orchestrator-update-task`/`-validate-task`).
33
+
34
+ ## App-shell hardening (this branch)
35
+
36
+ **Stale/unreachable saved server can no longer wedge first-run.** A persisted
37
+ `elizaos:active-server` pointing at a now-dead / CSP-blocked remote backend pinned the
38
+ client to that address; the startup poll (`packages/ui/src/state/startup-phase-poll.ts`)
39
+ retried it until `BACKEND_TIMEOUT` and onboarding hung forever (clearing cookies didn't
40
+ help — the pointer lives in localStorage). Fix: on a *connection-level* failure (the request
41
+ never received an HTTP response) against a non-loopback, non-same-origin base on an http(s)
42
+ page — never on native mobile, where the remote IS the agent — clear the saved server,
43
+ re-point the client at the local origin, and reach the backend (one-shot). The decision is a
44
+ pure exported `shouldFallBackToLocalOrigin` covered by a unit matrix, plus an end-to-end
45
+ recovery test.
46
+
47
+ **Onboarding boot issues we hit that are environmental, not code bugs (no regression test
48
+ applies):**
49
+ - *"No provider registered for TEXT_EMBEDDING" → agent won't boot* — caused by a local
50
+ `ELIZA_DISABLE_LOCAL_EMBEDDINGS=1` in `.env.worktree` with no cloud route, i.e. *zero*
51
+ embedding providers. With local embeddings enabled the agent boots degraded-but-non-fatal
52
+ (the intended behavior). A graceful-degradation guard for the "no embedding provider at
53
+ all" edge would live in agent/runtime bootstrap, not here; flagged for the runtime owner.
54
+ - *Dev service-worker / HTTPS-upgrade theories* — investigated and **disproven** by
55
+ reproduction; the actual cause was the stale active-server above. The dev SW renders fine
56
+ with caching active. An optional dev-only self-unregistering SW remains available as a
57
+ hygiene improvement but guards no confirmed bug.
58
+
59
+ ## Remaining work (specced, not yet built)
60
+
61
+ Ordered by leverage. `[BE]` backend, `[FE]` frontend, `[T]` tests.
62
+
63
+ ### A. `orchestrator-open-task` should navigate the workbench `[FE]` — S/M
64
+ `runOrchestratorCapability` ("open-task") fetches detail but does not call `setSelectedId`,
65
+ so a voice "open task X" returns data without visually navigating. `interact()` is a
66
+ module-level function with no handle to the live component state. Fix: have the workbench
67
+ register a navigation callback (e.g. a module-scoped `setSelectedTaskHandler` the mounted
68
+ `OrchestratorWorkbench` sets in a `useEffect`, called by the open-task capability), or route
69
+ selection through the existing `?task=` deep-link that `readInitialTaskId` already reads.
70
+
71
+ ### B. Bridge-backed provider setup `[BE][FE]` — M (design-touching)
72
+ Today `LlmProviderSection`/`CodingAgentSettingsSection` write `ANTHROPIC_API_KEY` etc.
73
+ straight into agent env via `client.updateConfig({ env })` — not the vault. Now that the
74
+ sensitive-request REST routes are mounted, wire missing-key flows to fire `SECRETS
75
+ action=request` (or POST `/api/sensitive-requests`) so secrets land in `sharedVault`. Reuse
76
+ the existing `ProviderCard`/`ProviderPanels`/`useProviderSelection` components rather than the
77
+ bespoke password inputs. Needs a decision on which providers/scopes/validation; do not change
78
+ secret-storage policy without the design owner. Secure-cloud / secure-local / insecure-local
79
+ fallbacks + redaction tests per the report's "Secret And OAuth Flow" section.
80
+
81
+ ### C. Workbench UX gaps `[FE]` — small each, need visual iteration
82
+ From the fidelity review of `OrchestratorWorkbench.tsx` (largely complete; these are the gaps):
83
+ - **Jump-to-latest / autoscroll** in the timeline — no scroll ref/affordance today (net-new).
84
+ - **Change-provider control** — no UI and no `orchestrator-change-provider` capability/backend; provider policy is read-only. Needs a backend capability first.
85
+ - **Branch display** in the sub-agent roster (only repo/workdir shown).
86
+ - **System-event collapse**: only a global show/hide toggle, not expandable clusters.
87
+ - **Verification checklist**: only per-artifact status pills, no dedicated pass/fail surface.
88
+ - **Per-task USD spend** in the left rail (rail shows tokens; cost only in header + inspector).
89
+ - **Provider "state"**: shows policy/identity, not live health/availability.
90
+
91
+ ### D. Remote / mobile for the orchestrator `[BE][FE]` — L (separate design)
92
+ Pairing primitives exist (`auth-pairing-routes`, `tunnel-to-mobile-client`) but **no `tunnel`
93
+ runtime service is registered** (so `runtime.getService("tunnel")` lookups and
94
+ `tunnel_authenticated_link` delivery are inert). The workbench is **polling-only**
95
+ (`POLL_INTERVAL_MS = 5000`); there is no SSE/WS for task updates. Needs: a registered tunnel
96
+ service (`getStatus/isActive/getUrl`), a relay decision (cloud gateway vs Headscale/ngrok per
97
+ the tunnel-to-mobile design), remote-API auth, and SSE/WS to replace the 5s poll so remote/
98
+ mobile clients aren't hammering. Do not attempt blind — design + visual iteration required.
99
+
100
+ ### E. Test depth `[T]`
101
+ Add the report's e2e workflow matrix with mocked providers (create/fork/pause-all/archive/
102
+ search/secret-flows/validation-failure-reopen), keeping live Claude/Codex tests env-gated.
103
+ Also add an integration-level test that drives a `/credentials/*` request through the runtime
104
+ route matcher (`matchPluginRoutePath`), not just the in-process dispatcher, so the
105
+ registry-registration gap (fixed in this branch) can't silently reopen.
106
+
107
+ ### F. Deferred from adversarial review (low priority, not regressions)
108
+ - **Bridge loopback hardening** `[BE]` — `bridge-routes.ts:isLoopback` trusts only
109
+ `req.socket.remoteAddress` (same model as the existing parent-context bridge). If the
110
+ runtime is ever fronted by a loopback-bound reverse proxy, mirror the extra checks from
111
+ `isTrustedLocalRequest` (reject forwarded/proxy-client headers and cross-site
112
+ `sec-fetch-site`). The single-use scopedToken remains the per-request defense regardless.
113
+ - **Re-anchor follow-ups to the durable goal** `[BE]` — `buildGoalFollowUp` reads
114
+ `metadata.goal`, but the planner spawn paths in `tasks.ts` never stamp a `goal` key, so
115
+ follow-ups currently re-anchor to the latest message rather than the original objective.
116
+ Stash `goal: task` (or the durable goal) onto session metadata at spawn time to realize
117
+ the intended re-anchoring. Not a regression — the old code sent raw text.
118
+
119
+ ## How to work this branch
120
+ Isolated worktree `/home/nubs/Git/iqlabs/eliza-labs/eliza-orchestrator-feat`, branch
121
+ `nubs/orchestrator-view-feature`. Fresh worktrees need `@elizaos/core` built first
122
+ (`cd packages/core && bun run build`) or vitest can't resolve `@elizaos/core`; building the
123
+ full plugin set is required to run app-core integration tests. Rebase onto the active branch
124
+ before merging (file sets don't overlap with the desktop/local-inference work in flight).
@@ -0,0 +1,174 @@
1
+ # Slice 1 critical assessment — chat task widget + OAuth secrets widget
2
+
3
+ Companion to
4
+ [`orchestrator-dashboard-task-widget-secrets-design.md`](./orchestrator-dashboard-task-widget-secrets-design.md).
5
+ Recorded after the first implementation pass, before any UI screenshot.
6
+
7
+ ## What landed
8
+
9
+ | Area | Files | Net change |
10
+ |---|---|---|
11
+ | `[TASK:id]title[/TASK]` parser | `packages/ui/src/components/chat/message-task-parser.ts` (new) + test (new) | Pure parser, strict UUID-shaped threadId, truncates titles, ignores unterminated tags. 9/9 tests green. |
12
+ | `TaskWidget` component | `packages/ui/src/components/chat/widgets/task-widget.tsx` (new) + test (new) | One-line title + one-line status (status · agents · relative · tokens). Polls `getCodingAgentTaskThread` every 5s, stops on terminal status, silent on 404 ("Task removed."), click dispatches `eliza:navigate:view` → `/orchestrator?taskId=…`. 6/6 tests green. |
13
+ | Wire-up in chat | `packages/ui/src/components/chat/MessageContent.tsx` (edit) + integration test (new) | New `kind: "task-widget"` segment + dispatch. Prose around the block continues to render. 4/4 integration tests green. |
14
+ | Sensitive-request OAuth | `packages/ui/src/components/chat/MessageContent.tsx` (edit) + extended test | New `OAuthRequestPanel` rendered when `form.kind === "oauth"`. Opens authorization URL in a popup; on popup block, surfaces "Pop-up blocked" without falling back to the chat stream. Scopes are visible; the URL itself is never substituted into chat text. The secret form keeps its current behavior. |
15
+ | Type widening | `packages/ui/src/api/client-types-chat.ts` (edit) | `SensitiveRequestForm.kind` widened to `"secret" \| "oauth"` with three optional fields: `provider`, `scopes`, `authorizationUrl`. |
16
+ | Tests added | 4 vitest files | Parser (9), Widget (6), MessageContent integration (4), OAuth in SensitiveRequestBlock (3 new). Whole `src/components/chat` suite: 49/49 green. |
17
+ | Lint / typecheck | — | `bun run --cwd packages/ui lint` clean. `bun run --cwd packages/ui typecheck` clean. |
18
+
19
+ ## Honest gaps
20
+
21
+ These are not "in the design doc but I forgot" — they were called out as
22
+ follow-ups in the doc itself. Listing here so the next pass knows the
23
+ priority order:
24
+
25
+ 1. **No emission point in `TASKS_CREATE`.** The widget RENDERS, but no
26
+ production action yet emits `[TASK:<id>]<title>[/TASK]` after a successful
27
+ durable-task creation. `runCreate` in
28
+ [`tasks.ts`](../src/actions/tasks.ts) spawns ACP sessions, not durable
29
+ task threads — the link between an action callback and a durable
30
+ `taskId` is not in place. Without that, the widget only renders if
31
+ the agent text already contains the block, which is the e2e seam used
32
+ in tests. The natural next step is wiring `OrchestratorTaskService`'s
33
+ `createTask` (or a separate `TASKS_CREATE_THREAD` sub-action) so that
34
+ on success it returns a `taskId` and the callback text concatenates the
35
+ block. That is a one-line emission change + a unit test in the plugin,
36
+ but it touches the create flow's wiring and was scoped as a separate
37
+ slice.
38
+ 2. **No OAuth backend adapter yet.** The UI widget works, but no
39
+ `SensitiveRequest` with `target.kind === "oauth"` is produced by the
40
+ policy classifier today; the inline adapter rejects non-secret. A
41
+ sibling `owner-app-oauth-adapter.ts` (and a `target.kind: "oauth"`) is
42
+ the next backend step. See `orchestrator-buildout-followups.md` section B.
43
+ 3. **No Playwright spec yet for the chat path.** Component-level tests cover
44
+ the rendering, dispatch, and event contract; an end-to-end "send chat
45
+ → assistant emits block → widget appears → click → workbench task
46
+ detail visible" would need MSW-style routes for the chat backend plus a
47
+ way to inject the assistant turn. The existing
48
+ `orchestrator-gui-workbench.spec.ts` already covers the create-flow and
49
+ the rail → inspector path. The chat-side spec is in slice 4 of the
50
+ design doc; it didn't land in this pass.
51
+ 4. **`OrchestratorGlanceStrip` was descoped.** On second read of
52
+ [`WorkbenchHeader`](../../../plugins/plugin-task-coordinator/src/OrchestratorWorkbench.tsx#L597),
53
+ the header already reads "12 tasks · 1 active · 3 done" with the same
54
+ minimal language the design called for, and adds a usage chip on the
55
+ right. A separate strip would have been redundant and added a second
56
+ row that competes with the same information. The right follow-up if a
57
+ user calls this out specifically is making the inline counts
58
+ click-to-filter, NOT introducing a new pinned tile.
59
+ 5. **Inspector terminal-task cleanup (slice 5) was descoped.** It's a
60
+ handful of `if (TERMINAL_STATUSES.has(status)) return null;` guards in
61
+ `TaskInspector`; not landed because the inspector is 4142 LOC and I
62
+ prioritized the user-visible chat gap. Worth a follow-up.
63
+
64
+ ## Security audit of what landed
65
+
66
+ - **Secret form (`SensitiveRequestBlock`)** — submission still goes
67
+ through `client.updateSecrets`, never through the chat message stream.
68
+ Existing test asserts the raw secret never appears anywhere in the
69
+ rendered DOM. Added a `data-testid="sensitive-request-submit"` so
70
+ Playwright can target the submit cleanly; behavior unchanged.
71
+ - **OAuth widget (`OAuthRequestPanel`)** — three guards:
72
+ 1. The authorization URL is only consumed inside `window.open()`. It is
73
+ never substituted into the visible chat text. The new test asserts
74
+ the URL substring is not present anywhere in `container.textContent`.
75
+ 2. Popup is opened with `noopener,noreferrer` so the consent page can't
76
+ `window.opener.*` back to chat.
77
+ 3. A blocked popup surfaces "Pop-up blocked. Allow pop-ups for this
78
+ site to continue." rather than silently falling back to a chat
79
+ message that would re-render the URL.
80
+ - **TaskWidget** — only reads structured status fields (`status`,
81
+ `sessionCount`, `activeSessionCount`, `latestActivityAt`,
82
+ `usage.totalTokens`). It does not render `metadata` or `messages`. A
83
+ regression test asserts secret-shaped strings smuggled through `metadata`
84
+ do not appear in the rendered DOM.
85
+
86
+ ## Test coverage table
87
+
88
+ | File | Suite | Count | Status |
89
+ |---|---|---|---|
90
+ | `message-task-parser.test.ts` | parser unit | 9 | green |
91
+ | `widgets/task-widget.test.tsx` | component unit | 6 | green |
92
+ | `MessageContent.task-widget.test.tsx` | integration | 4 | green |
93
+ | `MessageContent.sensitive-request.test.tsx` | sensitive widget (extended) | 7 | green |
94
+ | Whole `src/components/chat` suite | — | 49 | green |
95
+
96
+ `bun run --cwd packages/ui lint` clean. `bun run --cwd packages/ui typecheck` clean.
97
+ `bun run --cwd packages/app-core typecheck` shows two pre-existing
98
+ `@elizaos/plugin-commands` resolution errors unrelated to this change.
99
+
100
+ ## What a screenshot pass would look for (next session)
101
+
102
+ Not run in this session because there is no live UI to capture. The
103
+ checklist for that pass:
104
+
105
+ - **Chat surface, message with a task block** —
106
+ - Title line truncates without ellipsis-jitter at 320px width.
107
+ - Status line wraps gracefully; the dot/label/agents/tokens chips
108
+ never wrap mid-chip.
109
+ - Pulse animation is visible on `active` and `validating`, absent
110
+ on terminal states.
111
+ - Click → orchestrator workbench shows the matching task selected.
112
+ - **Chat surface, OAuth request** —
113
+ - "Connect <provider>" button reads exactly like the surrounding chat
114
+ accent (no accidental blue).
115
+ - Scopes line truncates at small widths.
116
+ - Pop-up blocked state surfaces inline, not modal.
117
+ - **Chat surface, secret request** — unchanged, but confirm the new
118
+ `data-testid` didn't shift any spacing.
119
+ - **Orchestrator workbench, `?taskId=…` arrival** — task is selected,
120
+ inspector visible without scrolling.
121
+
122
+ ## Post-review fixes (second pass)
123
+
124
+ After fanning out independent code-review + aesthetic + verification agents, four
125
+ findings were addressed in this same change-set:
126
+
127
+ 1. **`window.open(..., "noopener,noreferrer")` bug** — per the HTML spec
128
+ `noopener` forces `window.open` to return `null`, so the popup-blocked
129
+ detection misfired in every real browser. Removed `noopener` from the
130
+ features string, kept `noreferrer`, and added an explicit
131
+ `popup.opener = null` immediately after open as a belt-and-suspenders
132
+ measure. Test asserts `noreferrer` is present and `noopener` is not, and
133
+ that `opener` is nulled.
134
+ 2. **`TaskWidget` infinite poll on auth error** — added a
135
+ `MAX_CONSECUTIVE_ERRORS = 3` cap on the silent-error counter; after three
136
+ consecutive 401/403/etc the widget freezes on the last good state and
137
+ stops polling. Counter resets on the next successful fetch.
138
+ 3. **Slop pass — `ExternalLink` decorative icon removed** from `TaskWidget`.
139
+ The whole card is already a `<button>` with `hover:bg-bg-hover`; the icon
140
+ added zero info at a glance.
141
+ 4. **Slop pass — "The value will not be sent as a chat message." removed**
142
+ from the secret-form widget. A `type="password"` input is its own
143
+ signal; the trust copy stays on the OAuth panel where the user is
144
+ genuinely navigating to a third-party origin.
145
+ 5. **OAuth panel testid** — added `data-testid="sensitive-request-oauth"`
146
+ to the panel wrapper for parity with `sensitive-request`.
147
+
148
+ Reopen-on-terminal was reconsidered and intentionally left as-is: done/failed
149
+ tasks have an Archive button that transitions them to `archived`, which
150
+ exposes Reopen. The path exists — it just isn't a single-click escape from
151
+ done → reopened. Acceptable.
152
+
153
+ The legacy `SensitiveRequestOauthTarget` (lowercase `a`) coexists with the
154
+ new `SensitiveRequestOAuthTarget` (capital `A`) in the
155
+ `SensitiveRequestTarget` union. The new OAuth adapter defends with an
156
+ `isOAuthTarget` runtime narrower, so downstream consumers that already
157
+ relied on the loose shape are unaffected. Consolidating these two into a
158
+ single tight type is a follow-up (it widens the blast radius beyond this
159
+ change-set).
160
+
161
+ ## What to revisit if a user looks at this and says "still too busy"
162
+
163
+ - The header's `12 tasks · 1 active · 3 done` line already aims at
164
+ minimal/dense. If that still reads as cluttered, the next reduction
165
+ is dropping `tasks` (just show counts and labels) and making the
166
+ divider thinner.
167
+ - The widget's status line is four chips by default. If three of the
168
+ four are zero (e.g. fresh task, no agents, no tokens), only `status`
169
+ + `relative` render — that's already the case via the existing
170
+ conditional renders.
171
+ - The OAuth panel intentionally explains *why* it's safe ("token is
172
+ stored securely and never shown in chat"). That sentence is the only
173
+ prose in the panel and it's important for trust; it should not be
174
+ cut.