@elizaos/plugin-agent-orchestrator 2.0.0-alpha.9 → 2.0.3-beta.2

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 (75) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +232 -0
  4. package/docs/SUBAGENT_FLOW_AND_PARITY.md +138 -0
  5. package/docs/default-eliza-skills-and-agent-bridge-plan.md +225 -0
  6. package/docs/economics-goal-runbook.md +140 -0
  7. package/docs/multi-account-orchestration.md +76 -0
  8. package/docs/orchestrator-buildout-followups.md +124 -0
  9. package/docs/orchestrator-dashboard-task-widget-secrets-assessment.md +174 -0
  10. package/docs/orchestrator-dashboard-task-widget-secrets-design.md +301 -0
  11. package/docs/research/orchestrator-view-research-report.md +273 -0
  12. package/docs/sub-agent-routing.md +227 -0
  13. package/package.json +197 -38
  14. package/dist/index.d.ts +0 -21
  15. package/dist/index.d.ts.map +0 -1
  16. package/dist/index.js +0 -21120
  17. package/dist/index.js.map +0 -109
  18. package/dist/src/actions/messaging.d.ts +0 -24
  19. package/dist/src/actions/messaging.d.ts.map +0 -1
  20. package/dist/src/actions/peek-subagent.d.ts +0 -3
  21. package/dist/src/actions/peek-subagent.d.ts.map +0 -1
  22. package/dist/src/actions/subagent-management.d.ts +0 -7
  23. package/dist/src/actions/subagent-management.d.ts.map +0 -1
  24. package/dist/src/actions/task-management.d.ts +0 -9
  25. package/dist/src/actions/task-management.d.ts.map +0 -1
  26. package/dist/src/config.d.ts +0 -4
  27. package/dist/src/config.d.ts.map +0 -1
  28. package/dist/src/providers/orchestrator-config.d.ts +0 -80
  29. package/dist/src/providers/orchestrator-config.d.ts.map +0 -1
  30. package/dist/src/providers/task-context.d.ts +0 -3
  31. package/dist/src/providers/task-context.d.ts.map +0 -1
  32. package/dist/src/services/agent-orchestrator-service.d.ts +0 -59
  33. package/dist/src/services/agent-orchestrator-service.d.ts.map +0 -1
  34. package/dist/src/services/messaging-service.d.ts +0 -111
  35. package/dist/src/services/messaging-service.d.ts.map +0 -1
  36. package/dist/src/services/sandbox-service.d.ts +0 -103
  37. package/dist/src/services/sandbox-service.d.ts.map +0 -1
  38. package/dist/src/services/subagent-service.d.ts +0 -140
  39. package/dist/src/services/subagent-service.d.ts.map +0 -1
  40. package/dist/src/sub-agents/adapter.d.ts +0 -13
  41. package/dist/src/sub-agents/adapter.d.ts.map +0 -1
  42. package/dist/src/sub-agents/claude-agent-sdk-sub-agent.d.ts +0 -18
  43. package/dist/src/sub-agents/claude-agent-sdk-sub-agent.d.ts.map +0 -1
  44. package/dist/src/sub-agents/codex-sdk-sub-agent.d.ts +0 -18
  45. package/dist/src/sub-agents/codex-sdk-sub-agent.d.ts.map +0 -1
  46. package/dist/src/sub-agents/eliza-sub-agent.d.ts +0 -27
  47. package/dist/src/sub-agents/eliza-sub-agent.d.ts.map +0 -1
  48. package/dist/src/sub-agents/elizaos-native-sub-agent.d.ts +0 -61
  49. package/dist/src/sub-agents/elizaos-native-sub-agent.d.ts.map +0 -1
  50. package/dist/src/sub-agents/index.d.ts +0 -10
  51. package/dist/src/sub-agents/index.d.ts.map +0 -1
  52. package/dist/src/sub-agents/opencode-sub-agent.d.ts +0 -44
  53. package/dist/src/sub-agents/opencode-sub-agent.d.ts.map +0 -1
  54. package/dist/src/sub-agents/registry.d.ts +0 -3
  55. package/dist/src/sub-agents/registry.d.ts.map +0 -1
  56. package/dist/src/sub-agents/sweagent-sub-agent.d.ts +0 -19
  57. package/dist/src/sub-agents/sweagent-sub-agent.d.ts.map +0 -1
  58. package/dist/src/sub-agents/tools.d.ts +0 -15
  59. package/dist/src/sub-agents/tools.d.ts.map +0 -1
  60. package/dist/src/sub-agents/types.d.ts +0 -170
  61. package/dist/src/sub-agents/types.d.ts.map +0 -1
  62. package/dist/src/types/index.d.ts +0 -12
  63. package/dist/src/types/index.d.ts.map +0 -1
  64. package/dist/src/types/messaging.d.ts +0 -202
  65. package/dist/src/types/messaging.d.ts.map +0 -1
  66. package/dist/src/types/sandbox.d.ts +0 -228
  67. package/dist/src/types/sandbox.d.ts.map +0 -1
  68. package/dist/src/types/subagent.d.ts +0 -232
  69. package/dist/src/types/subagent.d.ts.map +0 -1
  70. package/dist/src/types.d.ts +0 -138
  71. package/dist/src/types.d.ts.map +0 -1
  72. package/dist/src/utils/index.d.ts +0 -7
  73. package/dist/src/utils/index.d.ts.map +0 -1
  74. package/dist/src/utils/session.d.ts +0 -184
  75. package/dist/src/utils/session.d.ts.map +0 -1
@@ -0,0 +1,225 @@
1
+ # Default Eliza Skills And Agent Bridge Plan
2
+
3
+ Date: 2026-05-10
4
+
5
+ ## Goal
6
+
7
+ Make Eliza's default task workers self-sufficient:
8
+
9
+ - They should receive editable, repo-owned default skills for Eliza, elizaOS plugin/app development, Eliza Cloud APIs, and monetization.
10
+ - Claude, Codex, and OpenCode workers should receive those defaults through the ACP agent orchestrator.
11
+ - Applications should be able to override defaults without forking the shipped skill package.
12
+ - Workers should be able to ask the running parent Eliza agent for context or actions that only the parent can perform.
13
+ - Paid, private, or destructive operations should remain mediated by the parent agent and its confirmation flow.
14
+
15
+ ## Current Inventory
16
+
17
+ ### Shipped skills
18
+
19
+ Repo-owned default skills live under:
20
+
21
+ ```text
22
+ packages/skills/skills/<slug>/SKILL.md
23
+ ```
24
+
25
+ The `@elizaos/skills` package publishes those files. Runtime startup wires bundled, workspace, and extra skill roots through `packages/agent/src/runtime/eliza.ts`. Managed/workspace copies override bundled defaults.
26
+
27
+ Relevant shipped skills now include:
28
+
29
+ - `eliza` for the running Eliza app, parent-agent callback protocol, capabilities, and lifecycle.
30
+ - `elizaos` for upstream runtime/plugin abstractions.
31
+ - `eliza-app-development` for this app repo's build and edit workflow.
32
+ - `eliza-cloud` for Cloud APIs, apps, credits, billing, containers, domains, auth, and earnings.
33
+ - `build-monetized-app` for the new-app deploy and monetization loop.
34
+ - `task-agent-eliza-bridge` for low-level task-agent bridge details.
35
+
36
+ ### Agent orchestration
37
+
38
+ The task agent infrastructure is in `plugins/plugin-agent-orchestrator`.
39
+
40
+ Important surfaces:
41
+
42
+ - `src/services/acp-service.ts` spawns ACP agents, records session metadata, and manages session lifecycle.
43
+ - `src/actions/tasks.ts` prepares ACP spawn/send/list/control requests through the single task-agent action surface.
44
+ - `src/services/skill-manifest.ts` renders the task-local manifest and virtual broker skills.
45
+ - `src/services/skill-lifeops-context-broker.ts` already exposes task-scoped LifeOps context.
46
+
47
+ ### Cloud and monetization
48
+
49
+ Current Cloud source-of-truth files:
50
+
51
+ - OpenAPI: `cloud/apps/api/openapi.json/route.ts`
52
+ - SDK: `cloud/packages/sdk/src/client.ts`
53
+ - billing/pricing: `cloud/packages/lib/services/ai-billing.ts`, `ai-pricing.ts`, `credits.ts`
54
+ - app chat: `cloud/apps/api/v1/apps/[id]/chat/route.ts`
55
+ - OpenAI-compatible chat: `cloud/apps/api/v1/chat/completions/route.ts`
56
+ - app monetization: `cloud/apps/api/v1/apps/[id]/monetization/route.ts`
57
+ - app credits: `cloud/packages/lib/services/app-credits.ts`
58
+ - container billing: `cloud/apps/api/cron/container-billing/route.ts`
59
+
60
+ Active app monetization is markup/share based. Inference in the current app-chat path debits the caller/user org credits and credits creator markup to redeemable earnings. App-credit purchase/share flows exist, but should not be assumed to fund inference unless the endpoint being edited explicitly does so.
61
+
62
+ ## Implemented Architecture
63
+
64
+ ### Default editable skills
65
+
66
+ Default skills remain bundled in `@elizaos/skills` and are seeded into the managed skills directory only when the user does not already have an editable copy.
67
+
68
+ Applications and workspaces can override defaults by placing a same-slug skill in a higher-priority skill root. This preserves:
69
+
70
+ - global defaults included with Eliza
71
+ - user/application customization
72
+ - stable slugs for orchestrator recommendation and invocation
73
+ - short `SKILL.md` bodies with longer references under `references/`
74
+
75
+ The root `scripts/ensure-skills.mjs` now resolves the shipped skill tree from `@elizaos/skills`, then falls back to `packages/skills/skills`, then legacy `skills/`.
76
+
77
+ ### Parent-agent broker
78
+
79
+ A new virtual broker skill, `parent-agent`, lets a child ask the running parent Eliza agent to use its loaded capabilities:
80
+
81
+ ```text
82
+ USE_SKILL parent-agent {"request":"Find the next open 30 minute slot on my calendar tomorrow afternoon"}
83
+ ```
84
+
85
+ The broker builds a synthetic message memory and sends it through the parent's normal `messageService.handleMessage(...)` pipeline with `continueAfterActions: true`. This means the parent can use the same actions, providers, services, connectors, model handlers, confirmation policies, and user approval flow it already uses for normal chat.
86
+
87
+ It also supports action discovery:
88
+
89
+ ```text
90
+ USE_SKILL parent-agent {"mode":"list-actions","query":"github","limit":25}
91
+ ```
92
+
93
+ Sensitive broker access is session allow-listed. The orchestrator includes `parent-agent` in generated manifests for coding-task sessions and registers the manifest slugs as the session allow-list.
94
+
95
+ ### Worker manifest
96
+
97
+ `SKILLS.md` now tells workers to emit a standalone `USE_SKILL <slug> <json_args>` line. The orchestrator writes this manifest into the workspace and sets `ELIZA_SKILLS_MANIFEST`, so Codex/Claude/OpenCode workers can discover skill protocol without hardcoding.
98
+
99
+ The injected parent runtime memory also now explains:
100
+
101
+ - read-only loopback endpoints
102
+ - `ELIZA_SKILLS_MANIFEST` / `SKILLS.md`
103
+ - `USE_SKILL parent-agent`
104
+ - `USE_SKILL lifeops-context`
105
+ - when to ask the parent for connector/private/paid actions
106
+
107
+ ### Git hygiene
108
+
109
+ The orchestrator-managed gitignore block now includes common generated agent files:
110
+
111
+ - `CLAUDE.md`
112
+ - `.claude/`
113
+ - `AGENTS.md`
114
+ - `.codex/`
115
+ - `.opencode/`
116
+ - `SKILLS.md`
117
+
118
+ Tracked repo files with the same names still need care; ignore rules only prevent new untracked files from being committed.
119
+
120
+ ## Security Model
121
+
122
+ The child worker never receives the parent API key, full memory dump, or raw connector credentials.
123
+
124
+ Allowed paths:
125
+
126
+ - read-only loopback endpoints for bounded parent context
127
+ - `USE_SKILL` directives for session allow-listed skill/broker slugs
128
+ - parent-agent requests mediated by the parent message pipeline
129
+ - parent confirmation flow for paid, destructive, private, or external operations
130
+
131
+ Denied or discouraged paths:
132
+
133
+ - ad hoc POSTs to loopback context endpoints
134
+ - bypassing Cloud/user confirmation for paid steps
135
+ - directly recreating parent credentials inside a child workspace
136
+ - treating task-agent output as a persistent authenticated identity
137
+
138
+ ## Five-Pass Implementation Plan
139
+
140
+ ### Pass 1: Prep
141
+
142
+ - Inventory shipped skills and confirm `@elizaos/skills` is the bundled default source.
143
+ - Inventory orchestrator spawn/memory/manifest/callback code.
144
+ - Inventory Cloud billing, monetization, app chat, SDK, and container deploy source-of-truth files.
145
+ - Decide stable slugs and default override model.
146
+ - Define task-scoped broker protocol and sensitive-broker allow-list behavior.
147
+
148
+ Status: implemented for the default skill and broker surfaces.
149
+
150
+ ### Pass 2: Implementation
151
+
152
+ - Add `eliza` default skill and references.
153
+ - Expand app-development, Cloud, monetization, and task-agent bridge skills.
154
+ - Fix root shipped-skill seeding to resolve `@elizaos/skills`.
155
+ - Add `parent-agent` virtual broker.
156
+ - Include `parent-agent` in `SKILLS.md` and the session allow-list.
157
+ - Update worker memory injection and manifest protocol text.
158
+ - Add generated agent files to orchestrator gitignore.
159
+
160
+ Status: implemented.
161
+
162
+ ### Pass 3: Cleanup
163
+
164
+ - Keep skill prose short and push details into references.
165
+ - Remove stale "read-only only / no action delegation" wording from task-agent bridge docs.
166
+ - Correct monetization docs where they implied app-specific inference balances in the active route.
167
+ - Align Cloud skill references to current implementation files.
168
+
169
+ Status: implemented.
170
+
171
+ ### Pass 4: Review And Fixing
172
+
173
+ Review focus:
174
+
175
+ - Type correctness around synthetic `Memory` construction and `messageService.handleMessage`.
176
+ - Session allow-list behavior for virtual brokers.
177
+ - Whether all ACP agent types can see `SKILLS.md` and parent memory text.
178
+ - Whether tracked `AGENTS.md` repos need a safer memory-file strategy than writing in the workspace root.
179
+ - Whether OpenCode's vendored ACP path has enough smoke coverage against Cerebras.
180
+
181
+ Status: partially implemented. The first three are covered by code/tests; live OpenCode/Cerebras validation remains credential-gated.
182
+
183
+ ### Pass 5: Testing, Verification, Validation
184
+
185
+ Minimum local tests:
186
+
187
+ - unit test `parent-agent` broker list-actions, missing request, and message pipeline callback capture
188
+ - unit test `USE_SKILL` directive parsing and sensitive broker routing if practical
189
+ - typecheck `plugin-agent-orchestrator`
190
+ - test `packages/skills` loader/frontmatter paths
191
+ - run `scripts/ensure-skills.mjs` against a temporary `ELIZA_STATE_DIR`
192
+
193
+ Live validation:
194
+
195
+ - Start a parent Eliza runtime with agent skills and orchestrator loaded.
196
+ - Spawn Codex, Claude, and OpenCode workers and verify they read `SKILLS.md`.
197
+ - Spawn OpenCode through ACP using the vendored OpenCode shim with Cerebras `gpt-oss-120b` and verify it can emit `USE_SKILL parent-agent ...`.
198
+ - Simulate:
199
+ - action listing
200
+ - parent memory search
201
+ - calendar/GitHub/browser-style parent request
202
+ - confirmation-required paid Cloud/domain request
203
+ - denied broker when not allow-listed
204
+ - task continuation after a returned parent result
205
+ - Run existing SWE/orchestrator benchmarks with and without the broker skill hint.
206
+
207
+ Status: local unit/type/skills tests are implemented and passing. Live OpenCode/Cerebras/SWE benchmarking requires running services and credentials, so it remains follow-up unless the environment already has them active.
208
+
209
+ ## Remaining Gaps
210
+
211
+ - Writing adapter memory files named `AGENTS.md` can collide with repos that already track `AGENTS.md`. A safer strategy is needed for Codex memory injection in tracked-repo workspaces.
212
+ - Live agent benchmarking against OpenCode + Eliza Cloud + Cerebras requires credentials and running infrastructure.
213
+ - The parent-agent broker currently asks through the normal parent message pipeline. Direct action invocation APIs could be added later, but the message path is more flexible and preserves confirmation behavior.
214
+
215
+ ## AGI-Level Direction
216
+
217
+ The long-term target is not a hardcoded "agent helper" action. It is a capability broker:
218
+
219
+ - the parent runtime exposes a typed capability graph derived from loaded plugins, actions, providers, services, connectors, and policies
220
+ - workers receive task-scoped manifests and can request capabilities without knowing implementation details
221
+ - the parent decides whether to answer, execute, request confirmation, redact, or deny
222
+ - applications override skills and policies by configuration rather than editing orchestrator code
223
+ - benchmarks measure whether workers solve more repo tasks with fewer human interventions and fewer credential leaks
224
+
225
+ The current implementation establishes the default skill surface and the child-to-parent request path needed for that architecture.
@@ -0,0 +1,140 @@
1
+ # Economics `/goal` runbook — autonomous monetized-app loop
2
+
3
+ How to drive (and what currently blocks) a `/goal` sub-agent that autonomously
4
+ runs the monetized-app loop: create app → deploy container → enable monetization
5
+ → buy a domain → stay alive on earnings, with paid Cloud commands auto-authorized
6
+ within a spend cap.
7
+
8
+ ## What already works (verified)
9
+
10
+ - **Capped self-spend allowance.** `decideSpendAuthorization`
11
+ (`src/services/spend-allowance.ts`) gates each Cloud command by risk/cap, and
12
+ `runCloudCommand` (`src/services/parent-agent-broker.ts:1067`) emits the
13
+ structured `event: "spend_auto_authorized"` log when a self-spend command
14
+ auto-authorizes within `ELIZA_AGENT_SPEND_CAP_USD`. Confirmed by
15
+ `src/__tests__/parent-agent-broker.test.ts` › _capped self-spend allowance_
16
+ (4 cases pass: within-cap auto-authorizes, over-cap confirms, non-self-spend
17
+ mutating auto-authorizes).
18
+ - **Economics capability profile.** `/economics` (or `/monetize`,
19
+ `/monetized-app`) in the composer sets `metadata.capabilityProfile = "economics"`
20
+ (`plugin-task-coordinator` composer directives → `createOrchestratorTask`), and
21
+ `spawnAgentForTask` reads `coerceGoalCapabilityProfile(task.metadata.capabilityProfile)`
22
+ and widens the goal fence via `ECONOMICS_GOAL_CAPABILITIES` (`goal-prompt.ts`).
23
+ - **The Cloud loop itself.** `apps.create → monetization.update → domains.buy
24
+ (real credit debit) → earnings → survival economics` is exercised end-to-end
25
+ against the mock stack by
26
+ `packages/test/cloud-e2e/tests/monetized-app-loop.spec.ts`.
27
+
28
+ ## Runbook
29
+
30
+ 1. Boot a stubbed-but-real Cloud so paid commands succeed without real money:
31
+
32
+ ```bash
33
+ CLOUD_E2E=1 NODE_ENV=test bun run cloud:mock --reset
34
+ # note the printed "Ready on http://127.0.0.1:<apiPort>"
35
+ ```
36
+
37
+ `cloud:mock` opens its PGlite store as a single-writer file, so you cannot
38
+ seed an org/API key from a second process while it runs. To seed live (and to
39
+ give the broker a key the granular per-route permission gate accepts), boot
40
+ through the cloud-e2e stack fixture instead — it stands up a PGlite **TCP
41
+ bridge** + the same wrangler launcher, then `seedTestUser()` mints an org with
42
+ credits + an API key. Mint the key with `permissions: ["*"]` if you want the
43
+ actual `containers.create` / `domains.buy` cloud calls to succeed (the default
44
+ `read/write/admin` seed key still proves `spend_auto_authorized`, because that
45
+ log fires from the cap decision *before* the HTTP call — but the call itself
46
+ 401s on routes that require granular scopes like `containers:write`).
47
+
48
+ 2. Point the broker at the mock and arm the spend cap (these resolve through
49
+ `config-env.ts`, so the eliza config `env` section or process env both work):
50
+
51
+ ```bash
52
+ ELIZA_CLOUD_BASE_URL=http://127.0.0.1:<apiPort>
53
+ ELIZAOS_CLOUD_API_KEY=<a seeded org API key> # see cloud-e2e seedTestUser
54
+ ELIZA_AGENT_SPEND_CAP_USD=20
55
+ ELIZA_ACP_DEFAULT_AGENT=opencode # Cerebras auto-detected; or codex/claude with their keys
56
+ OPENCODE_DISABLE_AUTOUPDATE=1 # opencode's network update check can blow the spawn timeout
57
+ ACPX_DEFAULT_TIMEOUT_MS=600000 # first opencode init (compile + provider fetch) ~3-5min
58
+ ```
59
+
60
+ 3. Create an economics task — `/economics build and monetize a tiny app` in the
61
+ composer, or `POST /api/orchestrator` with
62
+ `metadata: { capabilityProfile: "economics" }`.
63
+
64
+ 4. The sub-agent loads the `build-monetized-app` skill and SKILLS.md and drives
65
+ the loop through the parent-agent broker. Watch the logs for
66
+ `event: "spend_auto_authorized"` on `containers.create` / `domains.buy` —
67
+ that line is the proof the agent spent within its cap without a human prompt.
68
+
69
+ - **Domains gotcha:** `domains.buy` (and `media.*`/`promote.*`) resolve to
70
+ unknown cost and stall on confirmation unless the agent first calls
71
+ `domains.check` and threads the quote into `params.spendEstimateUsd`.
72
+ `containers.create` has a built-in `$0.67/day` estimate, so it
73
+ auto-authorizes without a hint.
74
+ - **`containers.create` needs a `name`** in addition to `appId`/`image`, or
75
+ the cloud call 422s (the spend log still fires; the deploy record is not
76
+ created). A capable agent self-corrects via `list-cloud-commands`.
77
+
78
+ ## Sub-agent → broker dispatcher (now wired)
79
+
80
+ `runCloudCommand` (the only emitter of `spend_auto_authorized`) is now reachable
81
+ by a live agent. The three gaps the earlier draft of this runbook called out are
82
+ closed:
83
+
84
+ 1. **Dispatch.** `SubAgentRouter.handleEvent` accumulates the child's streamed
85
+ `message` text and, when a complete `USE_SKILL parent-agent <json>` directive
86
+ appears, bridges it to `runParentAgentBroker({ runtime, sessionId, session,
87
+ args })` and streams `result.text` back via `acp.sendToSession`
88
+ (`src/services/parent-agent-dispatch.ts`). Detection is marker-guarded (it
89
+ only acts on text containing `USE_SKILL parent-agent`, which ordinary coding
90
+ tasks never emit) and capped by `ACPX_SUB_AGENT_ROUND_TRIP_CAP`.
91
+ 2. **Advertise.** `spawnAgentForTask` writes a `SKILLS.md` into the workdir for
92
+ `capabilityProfile === "economics"` tasks via `buildSkillsManifest(runtime, {
93
+ recommendedSlugs: ["build-monetized-app", "eliza-cloud"], virtualSkills:
94
+ [PARENT_AGENT_BROKER_MANIFEST_ENTRY] })`, so the child learns the `parent-agent`
95
+ slug and its arg contract.
96
+ 3. **Estimate.** The broker's unknown-cost stall now returns an *actionable*
97
+ instruction ("fetch a quote with `domains.check` and retry with
98
+ `params.spendEstimateUsd`") instead of a human-only yes/no, and the manifest
99
+ guidance advertises the same pattern — so an autonomous agent self-authorizes
100
+ `domains.buy` within the cap without a human turn.
101
+
102
+ The directive parser and the broker→`sendToSession` bridge are unit-tested in
103
+ `src/__tests__/parent-agent-dispatch.test.ts`; `spend_auto_authorized` itself
104
+ stays covered by `parent-agent-broker.test.ts`.
105
+
106
+ ## Live verification (2026-06-06)
107
+
108
+ The full loop was run end-to-end against `cloud:mock` with a real
109
+ `opencode` ACP child on a Cerebras key (`gpt-oss-120b`) — no human in the loop:
110
+
111
+ - The child read `SKILLS.md`, then drove `cloud.health → apps.create →
112
+ containers.create` entirely through `USE_SKILL parent-agent {…}` directives.
113
+ `apps.create` created a real app; `containers.create` self-authorized within
114
+ the cap and emitted, verbatim:
115
+ `event:"spend_auto_authorized" command:"containers.create" risk:"paid"
116
+ estimatedCostUsd:0.67 runningTotalUsd:0.67 capUsd:20 reason:"within-cap"`.
117
+ When the first `containers.create` 422'd (missing `name`), the child called
118
+ `list-cloud-commands`, corrected the params, and re-authorized — no human turn.
119
+ - The `domains.buy` self-resolve path was verified at the broker level against
120
+ the same live mock: `domains.check` returned a `$14.95` quote, that price was
121
+ threaded into `params.spendEstimateUsd`, `domains.buy` emitted
122
+ `spend_auto_authorized` ($14.95 of the $20 cap), and the org credit balance
123
+ dropped **1000.00 → 985.05** — a real debit.
124
+
125
+ Two defects the live run surfaced (both fixed):
126
+
127
+ 1. **Windows env forwarding (`acp-service.ts`).** `shouldForwardEnv` matched
128
+ `PATH` case-sensitively, but the repo runtime is Bun and Bun-on-Windows
129
+ reports the key as `Path` — so ACP sub-agents spawned with NO `PATH` and the
130
+ opencode shim died with `'bun' is not recognized`. Now matched
131
+ case-insensitively (+ Windows system vars), with the path key canonicalized
132
+ to `PATH` in `buildEnv`.
133
+ 2. **Mid-turn reply delivery (`parent-agent-dispatch.ts`).** A child emits its
134
+ directive *mid-turn* and ends the turn awaiting the reply; delivering the
135
+ reply is a new prompt, which the transport rejects ("session is already
136
+ busy") until the turn finishes. The old code dropped the reply, stalling the
137
+ loop on the first directive. Delivery now retries until the session goes idle.
138
+
139
+ See `default-eliza-skills-and-agent-bridge-plan.md` for the broader bridge design;
140
+ this runbook is the economics-specific slice.
@@ -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).