smol-symphony 0.3.2 → 0.4.0

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 (67) hide show
  1. package/AGENTS.md +6 -3
  2. package/PRODUCT.md +2 -2
  3. package/README.md +99 -85
  4. package/WORKFLOW.template.yaml +55 -11
  5. package/WORKFLOW.yaml +9 -3
  6. package/assets/symphony-mise.system.toml +6 -0
  7. package/dist/core/credential/adapter-config.js +28 -0
  8. package/dist/core/credential/adapter-config.js.map +1 -1
  9. package/dist/core/credential/availability.js +44 -1
  10. package/dist/core/credential/availability.js.map +1 -1
  11. package/dist/core/credential/extract.js +34 -0
  12. package/dist/core/credential/extract.js.map +1 -1
  13. package/dist/core/credential/fake-creds.js +39 -2
  14. package/dist/core/credential/fake-creds.js.map +1 -1
  15. package/dist/core/credential/shape.js +19 -0
  16. package/dist/core/credential/shape.js.map +1 -1
  17. package/dist/core/image/managed-image.js +3 -12
  18. package/dist/core/image/managed-image.js.map +1 -1
  19. package/dist/core/mcp/pi-extension.js +124 -0
  20. package/dist/core/mcp/pi-extension.js.map +1 -0
  21. package/dist/core/schedule/sleep-cycle.js +11 -6
  22. package/dist/core/schedule/sleep-cycle.js.map +1 -1
  23. package/dist/core/schedule/tick.js +1 -0
  24. package/dist/core/schedule/tick.js.map +1 -1
  25. package/dist/core/workflow/derive.js +2 -0
  26. package/dist/core/workflow/derive.js.map +1 -1
  27. package/dist/core/workflow/parse.js +6 -1
  28. package/dist/core/workflow/parse.js.map +1 -1
  29. package/dist/core/workflow/validate.js +2 -2
  30. package/dist/core/workflow/validate.js.map +1 -1
  31. package/dist/shell/adapter/adapter-registry.js +23 -1
  32. package/dist/shell/adapter/adapter-registry.js.map +1 -1
  33. package/dist/shell/{cli → adapter}/doctor.js +8 -2
  34. package/dist/shell/adapter/doctor.js.map +1 -0
  35. package/dist/shell/adapter/gondolin-image-fetch.js +17 -6
  36. package/dist/shell/adapter/gondolin-image-fetch.js.map +1 -1
  37. package/dist/shell/{main-scaffold.js → adapter/scaffold.js} +4 -4
  38. package/dist/shell/adapter/scaffold.js.map +1 -0
  39. package/dist/shell/adapter/workflow-loader.js +20 -1
  40. package/dist/shell/adapter/workflow-loader.js.map +1 -1
  41. package/dist/shell/interp/credential-defaults.js +83 -1
  42. package/dist/shell/interp/credential-defaults.js.map +1 -1
  43. package/dist/shell/interp/credential.js +10 -1
  44. package/dist/shell/interp/credential.js.map +1 -1
  45. package/dist/shell/interp/ensure-tracker-root.js +30 -0
  46. package/dist/shell/interp/ensure-tracker-root.js.map +1 -0
  47. package/dist/shell/interp/tracker-disk.js +19 -0
  48. package/dist/shell/interp/tracker-disk.js.map +1 -1
  49. package/dist/shell/interp/vm.js +15 -1
  50. package/dist/shell/interp/vm.js.map +1 -1
  51. package/dist/shell/main-credential.js +1 -1
  52. package/dist/shell/main-credential.js.map +1 -1
  53. package/dist/shell/main-doctor.js +1 -1
  54. package/dist/shell/main-doctor.js.map +1 -1
  55. package/dist/shell/main-http-handler.js +4 -4
  56. package/dist/shell/main-http-handler.js.map +1 -1
  57. package/dist/shell/main-http-views.js +0 -19
  58. package/dist/shell/main-http-views.js.map +1 -1
  59. package/dist/shell/main-preflight.js +1 -1
  60. package/dist/shell/main-preflight.js.map +1 -1
  61. package/dist/shell/main-runner.js +28 -22
  62. package/dist/shell/main-runner.js.map +1 -1
  63. package/dist/shell/main.js +14 -6
  64. package/dist/shell/main.js.map +1 -1
  65. package/package.json +2 -3
  66. package/dist/shell/cli/doctor.js.map +0 -1
  67. package/dist/shell/main-scaffold.js.map +0 -1
package/AGENTS.md CHANGED
@@ -94,11 +94,14 @@ re-express, not a directive.
94
94
 
95
95
  ## Build, test, and check before declaring done
96
96
 
97
- - `npm run typecheck` — must pass.
98
- - `npm test` must pass.
97
+ - `npm run verify` — must pass (typecheck + eslint purity/budgets + depcruise
98
+ walls + the three ratcheted arch gates against `arch-baseline.json` + tests).
99
+ CI runs the same gates, so a red gate here is a red PR.
99
100
  - `npm run build` — must pass.
100
101
 
101
- Run all three before calling `symphony.transition` into a terminal state.
102
+ Run both before calling `symphony.transition` into a terminal state. If a gate
103
+ fails "good news" (the count FELL), lower the matching `arch-baseline.json`
104
+ number in the same commit — that is the ratchet locking in the win.
102
105
 
103
106
  ## Filing tracker issues
104
107
 
package/PRODUCT.md CHANGED
@@ -23,7 +23,7 @@ process is logging.
23
23
 
24
24
  smol-symphony is a small TypeScript orchestrator that takes Markdown issues off a
25
25
  local tracker, prepares per-issue workspaces, and runs coding agents
26
- (Claude Code, Codex, OpenCode) inside isolated per-issue Gondolin microVMs over
26
+ (Claude Code, Codex) inside isolated per-issue Gondolin microVMs over
27
27
  ACP. The
28
28
  HTTP dashboard exists to do two things well:
29
29
 
@@ -83,7 +83,7 @@ Three patterns this must never resemble:
83
83
  stuck?" and "is anything running?" Density beats whitespace luxury for
84
84
  the running / retry / issue tables.
85
85
 
86
- 4. **Agents are not the brand.** This tool dispatches Claude, Codex, OpenCode
86
+ 4. **Agents are not the brand.** This tool dispatches Claude, Codex —
87
87
  it does not perform AI-ness. The product narrative is "small orchestrator
88
88
  with isolated VM execution", not "AI-powered work runner". Visual language
89
89
  should reflect that.
package/README.md CHANGED
@@ -5,15 +5,18 @@
5
5
  > repository was authored by those agents under human review.
6
6
 
7
7
  A small TypeScript orchestrator that reads issues off a local Markdown tracker,
8
- prepares per-issue workspaces, and runs coding agents (Claude Code, Codex,
9
- OpenCode) inside isolated per-issue [Gondolin](https://github.com/earendil-works/gondolin)
8
+ prepares per-issue workspaces, and runs coding agents (Claude Code, Codex)
9
+ inside isolated per-issue [Gondolin](https://github.com/earendil-works/gondolin)
10
10
  microVMs over the [Agent Client Protocol](https://agentclientprotocol.com).
11
11
 
12
- The agent signals progress through an injected MCP server (`transition`,
13
- `request_human_steering`, `propose_issue`); the orchestrator handles state,
14
- retry, concurrency, and opens a pull request per issue when configured for
15
- a GitHub remote. In local-only mode the per-issue branch is left in the
16
- workspace for review.
12
+ You file issues; symphony dispatches one agent per issue and handles state,
13
+ retry, and concurrency. The agent signals progress through an injected MCP
14
+ server (`transition`, `request_human_steering`, `propose_issue`). When you point
15
+ it at a GitHub remote it opens a pull request per issue; in local-only mode the
16
+ per-issue `agent/<id>` branch is left in the workspace for review.
17
+
18
+ **New here? Jump to [Quick start](#quick-start)** — install, scaffold, run, with
19
+ no repo checkout and no image to build.
17
20
 
18
21
  ```
19
22
  ┌──────────────────────────────────────────────────────────────────────────┐
@@ -45,96 +48,103 @@ original architectural narrative, see
45
48
 
46
49
  ## Quick start
47
50
 
48
- The golden path from zero to your first dispatched issue. The details behind
49
- each step live in [Prerequisites in detail](#prerequisites-in-detail) below you
50
- should not need them to get here.
51
+ From zero to your first dispatched issue. **No repo checkout, no image to build** —
52
+ the agent image is a prebuilt asset symphony fetches from a published release the
53
+ first time it runs.
51
54
 
52
55
  ```bash
53
- # 1. Build the agent rootfs image once. This ships only `mise` (jdx/mise) — node +
54
- # the coding-agent CLIs are mise-installed at dispatch, the launcher is injected —
55
- # and tags the result `symphony-agents:latest` — the tag the scaffolded
56
- # WORKFLOW.yaml pins, so a first run needs no edit.
57
- git clone https://github.com/dizk/smol-symphony.git
58
- cd smol-symphony && npm install && npm run build:image
56
+ # 1. Install symphony. (Or skip this and prefix each command below with `npx`,
57
+ # e.g. `npx smol-symphony doctor WORKFLOW.yaml`.)
58
+ npm i -g smol-symphony
59
59
 
60
- # 2. In your project, scaffold the minimal WORKFLOW.yaml. With no workflow file
61
- # present, symphony offers to write a ~30-line starter (every key required,
62
- # no power features), then exits so you can glance at it.
60
+ # 2. In your project, scaffold a starter workflow. With no WORKFLOW.yaml present,
61
+ # symphony offers to write a ~30-line starter (plus a prompts/Todo.md and an
62
+ # issue-filing skill), then exits so you can glance at it. It runs as-is.
63
63
  cd /path/to/your/project
64
- npx smol-symphony WORKFLOW.yaml
65
-
66
- # 3. Verify the whole setup in one pass before the first dispatch. `symphony
67
- # doctor` runs every adoption-prerequisite probe — Node version, the
68
- # `gondolin.image` pin, each configured adapter's host credential, a
69
- # writable `tracker.root`, and a bindable dashboard port and prints a
70
- # PASS/FAIL line per check with a one-line fix for each failure, exiting 0
71
- # iff every check passes. Unlike startup (which bails on the first failure),
72
- # it runs all probes so you see the whole picture at once.
73
- npx smol-symphony doctor WORKFLOW.yaml
74
-
75
- # 4. Drop one issue into the tracker.
76
- mkdir -p issues/Todo
77
- printf -- '---\ntitle: "Say hi"\n---\nAdd a hi() that returns "hi".\n' > issues/Todo/1.md
78
-
79
- # 5. Run symphony and watch it dispatch.
80
- npx smol-symphony WORKFLOW.yaml --port 8787
81
- # Open http://127.0.0.1:8787/ — issue 1 moves from "on disk" into a running
82
- # session as symphony boots a per-issue microVM and dispatches the agent.
64
+ symphony WORKFLOW.yaml
65
+
66
+ # 3. Check the host is ready before the first dispatch. `doctor` runs every
67
+ # prerequisite probe — Node version, the agent image, your adapter credential,
68
+ # a writable tracker, a bindable dashboard port and prints a PASS/FAIL line
69
+ # with a one-line fix for each failure, exiting 0 only if every check passes.
70
+ symphony doctor WORKFLOW.yaml
71
+
72
+ # 4. Run symphony. The first run downloads the prebuilt agent image (one-time,
73
+ # surfaced on the dashboard); then it serves the dashboard and starts dispatching.
74
+ symphony WORKFLOW.yaml --port 8787
75
+
76
+ # 5. Open http://127.0.0.1:8787/ and add your first issue from the "new issue"
77
+ # form. symphony picks it up, boots a per-issue microVM, and dispatches the
78
+ # agent — watch the issue move from "on disk" into a running session.
83
79
  ```
84
80
 
85
- The same first-run scaffold also installs an issue-filing skill at
86
- `.claude/skills/symphony-issues/SKILL.md` (best-effort it never blocks the
87
- workflow scaffold), so a coding agent in the freshly-onboarded repo can file
88
- well-formed tracker issues and decompose a large task into a `blocked_by` DAG
89
- from day one.
81
+ That's the whole path. Two host prerequisites symphony can't install for you,
82
+ both checked by `doctor` in step 3:
83
+
84
+ - **Node.js 23.6** (the `engines.node` floor; `doctor` reports your version).
85
+ - **One adapter credential.** For the default `claude` adapter, a
86
+ `~/.claude/.credentials.json` on the host — run `claude login` once. (For
87
+ `codex`, see [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).) The credential
88
+ never enters the VM — see [Trust posture](#trust-posture).
90
89
 
91
- The scaffolded file ([WORKFLOW.minimal.yaml](./WORKFLOW.minimal.yaml) is the shipped
92
- copy) runs as-is once `symphony-agents:latest` exists; the only edits a different
93
- setup needs are `gondolin.image` (if you pinned a build id) and the adapter
94
- credential for your chosen agent. When you want a power feature it omits — typed
95
- action DAGs, PR autopilot, per-state adapters, egress allowlists graduate to
96
- the annotated [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).
90
+ The scaffolded [WORKFLOW.minimal.yaml](./WORKFLOW.minimal.yaml) runs as-is on the
91
+ managed image; the only edits a different setup needs are that adapter credential
92
+ and if you'd rather pin your own image than fetch the managed one —
93
+ `gondolin.image`. The first-run scaffold also drops an issue-filing skill at
94
+ `.claude/skills/symphony-issues/SKILL.md` (best-effort it never blocks the
95
+ scaffold), so a coding agent in the freshly-onboarded repo can file well-formed
96
+ tracker issues and decompose a large task into a `blocked_by` DAG from day one.
97
97
 
98
- (Or `npm i -g smol-symphony` and then `symphony doctor` / `symphony WORKFLOW.yaml`
99
- to skip the `npx` fetchboth invoke the `symphony` bin shipped in this
100
- package.)
98
+ When you want a power feature the starter omits typed action DAGs, PR autopilot,
99
+ per-state adapters, egress allowlists, sleep-cycle reflection graduate to the
100
+ annotated [WORKFLOW.template.yaml](./WORKFLOW.template.yaml). Full detail behind
101
+ each step is in [Prerequisites in detail](#prerequisites-in-detail) — you should
102
+ not need it to get here.
101
103
 
102
104
  ### Prerequisites in detail
103
105
 
104
- - **Node.js ≥ 20.**
105
- - **The agent rootfs image** (step 1). Gondolin is the in-process microVM
106
+ - **Node.js ≥ 23.6.** The `engines.node` floor; `symphony doctor` reports your
107
+ running version against it.
108
+ - **The agent image — fetched, not built.** Gondolin is the in-process microVM
106
109
  substrate (`@earendil-works/gondolin`), so there is no separate VM daemon to
107
- run. The image is built **once** from `images/agents/` via
108
- `npm run build:image`: it starts from `node:24-bookworm-slim`, installs base
109
- CLI tooling, and ships only the `mise` (jdx/mise) binary. node + every
110
- ACP-capable coding agent (`claude-agent-acp`, `codex-acp`, …) are
111
- NO LONGER baked they come from a mise SYSTEM config symphony stages into the
112
- guest at dispatch (`assets/symphony-mise.system.toml`) and installs via
113
- `mise install` into a warm-cached data dir; a consuming repo's own `mise.toml`
114
- adds project toolchains (rust, go, kotlin+gradle, …) on top. The in-VM stdio
115
- launcher is likewise injected at dispatch, not baked. So bumping an agent CLI is
116
- a one-line edit to the staged mise config + a restart — no image rebuild + repin.
117
- The build prints a content-addressed build id; pin it (or the
118
- `symphony-agents:latest` tag) in `WORKFLOW.yaml`'s `gondolin.image`. See
119
- [images/agents/README.md](./images/agents/README.md) for the build steps and
120
- requirements, and `gondolin.mise` in
121
- [WORKFLOW.template.yaml](./WORKFLOW.template.yaml) to tune the data dir or opt out.
110
+ run. The scaffolded workflow sets `gondolin.image: managed`, which fetches a
111
+ **prebuilt** agent rootfs from a published smol-symphony release on first run —
112
+ one asset per host arch (`x86_64`, `aarch64`), keyed to the symphony version.
113
+ No docker, no `npm run build:image`, no local OCI convert. The image ships only
114
+ the `mise` (jdx/mise) binary; node + every ACP-capable coding agent CLI
115
+ (`claude-agent-acp`, `codex-acp`) come from a mise SYSTEM config symphony stages
116
+ into the guest at dispatch (`assets/symphony-mise.system.toml`), and a consuming
117
+ repo's own `mise.toml` adds project toolchains (rust, go, kotlin+gradle, …) on
118
+ top. So bumping an agent CLI is a one-line edit to the staged mise config + a
119
+ restart no image rebuild. On an unsupported arch (no published asset), or when
120
+ you'd rather build/pin your own, set `gondolin.image` to a local build id /
121
+ `name:tag` (`npm run build:image` from a checkout prints one — see
122
+ [images/agents/README.md](./images/agents/README.md)) or `gondolin.oci_image` to
123
+ an OCI ref symphony auto-converts on first reconcile. Tune the managed fetch and
124
+ the mise data dir via the `gondolin` keys in
125
+ [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).
122
126
  - **An adapter credential.** For the default `acp.adapter: claude`: a
123
- credentials file at `~/.claude/.credentials.json` on the host. Symphony reads
124
- it only on the host side: the guest holds only a token-shaped placeholder, and
125
- the host substitutes the real OAuth access token into the outbound request at
126
- Gondolin egress (TLS-MITM). The credential file itself is never staged into
127
- the VM. (For `acp.adapter: codex`, see [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).)
128
- - **Skipping the `npx` fetch.** `npm i -g smol-symphony` installs the `symphony`
129
- bin globally so `symphony WORKFLOW.yaml` works without re-fetching; the image
130
- build still needs a checkout (it lives in `images/agents/`).
127
+ credentials file at `~/.claude/.credentials.json` on the host (run
128
+ `claude login` once). Symphony reads it only on the host side: the guest holds
129
+ only a token-shaped placeholder, and the host substitutes the real OAuth access
130
+ token into the outbound request at Gondolin egress (TLS-MITM). The credential
131
+ file itself is never staged into the VM. (For `acp.adapter: codex`, see
132
+ [WORKFLOW.template.yaml](./WORKFLOW.template.yaml).)
133
+ - **Where state lives.** The tracker, run logs, and per-issue workspaces default
134
+ to `~/.symphony/{trackers,logs,workspaces}/<project>` outside your repo, so a
135
+ delete / re-clone doesn't wipe them. `<project>` is derived from
136
+ `workspace.github_repo` (or the workflow-file directory name). The startup
137
+ banner prints the resolved tracker root, workspace root, dashboard URL, and log
138
+ file; override any with the matching `*.root` key (e.g. `tracker.root: ./issues`
139
+ to keep issues inside the repo).
131
140
  - **Console output.** With the default log file configured, symphony prints a
132
141
  one-line-per-field startup banner (workflow, tracker root, dashboard URL,
133
- log-file path) and routes the structured `key=value` stream to
134
- `.symphony/logs/symphony.log` only. `tail -f` that file to follow the detail.
135
- Pass `--verbose` (alias `--foreground`) to mirror the structured stream back
136
- to the console for interactive debugging. With no log file configured (the
137
- `SYMPHONY_LOG_FILE=""` override), the structured stream stays on stderr.
142
+ log-file path) and routes the structured `key=value` stream to the log file only
143
+ (`~/.symphony/logs/<project>/symphony.log` by default). `tail -f` that file to
144
+ follow the detail. Pass `--verbose` (alias `--foreground`) to mirror the
145
+ structured stream back to the console for interactive debugging. With no log
146
+ file configured (the `SYMPHONY_LOG_FILE=""` override), the structured stream
147
+ stays on stderr.
138
148
 
139
149
  ### From a checkout
140
150
 
@@ -365,12 +375,16 @@ posture:
365
375
 
366
376
  ```bash
367
377
  npm run typecheck # tsc --noEmit
368
- npm test # 170 tests across workflow, tracker, prompt, workspace,
369
- # adapters, http, mcp, acp-bridge, orchestrator, run log,
370
- # runner state resolution, and tool-call summary surfaces
378
+ npm test # ~1400 tests across the core deciders + shell adapters:
379
+ # workflow, tracker, prompt, workspace, adapters, http, mcp,
380
+ # acp-bridge, orchestrator, run log, and runner state resolution
371
381
  npm run build # tsc emit to dist/
372
382
  ```
373
383
 
384
+ `npm run verify` runs the full gate set CI enforces: typecheck, the eslint
385
+ purity/budget rules, the dependency-cruiser layering walls, the ratcheted
386
+ architecture gates, and the tests.
387
+
374
388
  An end-to-end smoke run needs the built agent image (see `images/agents/`).
375
389
 
376
390
  See [CHANGELOG.md](./CHANGELOG.md) for operator-visible changes between
@@ -54,13 +54,16 @@ tracker:
54
54
  # and the landing directory for `symphony.propose_issue`.
55
55
  # adapter (string, optional): override the workflow-level `acp.adapter` for
56
56
  # agents dispatched in this state. Must be a known profile (claude,
57
- # codex). Both use host-side credential substitution at Gondolin
57
+ # codex, pi). All use host-side credential substitution at Gondolin
58
58
  # egress and are startup-probed so a missing credential fails fast.
59
59
  # claude has a single host credential file
60
60
  # (~/.claude/.credentials.json) that is probed for readability; codex
61
61
  # passes when either ~/.codex/auth.json holds a token (ChatGPT-OAuth
62
62
  # tokens.access_token or a top-level OPENAI_API_KEY) or the host
63
- # OPENAI_API_KEY env var is set.
63
+ # OPENAI_API_KEY env var is set; pi passes when ~/.pi/agent/auth.json
64
+ # holds a `github-copilot` entry (access or refresh token — log in
65
+ # once with `pi` /login on the host) or the host COPILOT_GITHUB_TOKEN
66
+ # env var is set.
64
67
  # prompt_file (path): this state's prompt template, in its own file. The shell
65
68
  # loader reads it and assembles `prompt.preamble_file` + this file +
66
69
  # `prompt.footer_file` (see the top-level `prompt:` block below) into
@@ -189,11 +192,20 @@ tracker:
189
192
  # a trigger fires the orchestrator MINTS a FRESH ephemeral issue into
190
193
  # this state (it runs once and terminates in a dedicated terminal),
191
194
  # instead of re-arming one immortal parked ticket. Fields:
192
- # • on_idle (bool): spawn when the orchestrator is idle AND >=1
193
- # issue reached a terminal state since the last cycle (the ">=1
194
- # since last cycle" gate is load-bearing — without it an idle
195
- # orchestrator re-spawns in a tight loop with nothing new to
196
- # mine). Default false.
195
+ # • on_idle (bool): spawn when the orchestrator is idle AND at
196
+ # least `idle_min_terminal` issues reached a terminal state since
197
+ # the last cycle (the "since last cycle" gate is load-bearing —
198
+ # without it an idle orchestrator re-spawns in a tight loop with
199
+ # nothing new to mine). Default false.
200
+ # • idle_min_terminal (int): the idle trigger's signal floor — the
201
+ # minimum terminal transitions since the last cycle that on_idle
202
+ # requires before it fires. Default 1 (fire on any single finish,
203
+ # the historic behavior). Raise it to reflect only once enough new
204
+ # signal has accumulated, collapsing strings of single-finish idle
205
+ # cycles into one; the after_terminal backstop still covers busy
206
+ # stretches, so reflection never starves. Keep it >= 1 — a floor of
207
+ # 0 lets the idle trigger fire with nothing finished, reintroducing
208
+ # the tight re-spawn loop. Has no effect when on_idle is false.
197
209
  # • after_terminal (int): a backstop for busy stretches that never
198
210
  # go idle — spawn once this many issues have reached a terminal
199
211
  # state (Done/Cancelled/the spawn's terminal) since the last
@@ -371,9 +383,10 @@ states:
371
383
  # checks the lesson against the evidence rather than the reflector's summary.
372
384
  #
373
385
  # CADENCE: the orchestrator auto-spawns a fresh reflection issue from the
374
- # Reflect state's `spawn:` block — `on_idle` (idle with >=1 issue finished since
375
- # the last cycle) or `after_terminal` (a backstop once N issues have reached a
376
- # terminal state since the last cycle). The counter resets on a successful mint;
386
+ # Reflect state's `spawn:` block — `on_idle` (idle with >=`idle_min_terminal`
387
+ # issues finished since the last cycle; default 1) or `after_terminal` (a backstop
388
+ # once N issues have reached a terminal state since the last cycle). The counter
389
+ # resets on a successful mint;
377
390
  # `max_in_flight: 1` prevents a second cycle while one is live. An operator can
378
391
  # also mint a cycle by hand by creating a Reflect issue (a real dashboard
379
392
  # "reflect now" button or a `symphony reflect` verb is a natural fit) — a clean
@@ -392,6 +405,7 @@ states:
392
405
  # allowed_transitions: [Reflected]
393
406
  # spawn:
394
407
  # on_idle: true
408
+ # idle_min_terminal: 3 # idle signal floor (default 1); >=N finished since last cycle
395
409
  # after_terminal: 10 # backstop for busy stretches (0 disables)
396
410
  # title: "Reflection {{ stamp }}"
397
411
  # max_in_flight: 1 # never two reflections live at once
@@ -470,7 +484,8 @@ pr:
470
484
  # role: active
471
485
  # allowed_transitions: [Reflected]
472
486
  # spawn:
473
- # on_idle: true # spawn when idle with >=1 terminal since last cycle
487
+ # on_idle: true # spawn when idle with >=idle_min_terminal finished since last cycle
488
+ # idle_min_terminal: 3 # idle signal floor (default 1)
474
489
  # after_terminal: 10 # spawn after N terminal transitions (0 disables)
475
490
  # title: "Reflection {{ stamp }}"
476
491
  # max_in_flight: 1 # never two reflections live at once
@@ -734,6 +749,11 @@ acp:
734
749
  # bearer (in a fake ~/.codex/auth.json); the host substitutes the
735
750
  # real OpenAI/ChatGPT token at egress. No real credential — and no
736
751
  # real OPENAI_API_KEY — enters the VM.
752
+ # pi — pi-acp wrapping `pi --mode rpc` (the pi coding agent), backed by a
753
+ # GitHub Copilot subscription. Same model: the guest holds a
754
+ # placeholder COPILOT_GITHUB_TOKEN env bearer; the host substitutes
755
+ # the real short-lived Copilot token at egress. No real credential
756
+ # enters the VM.
737
757
  adapter: claude
738
758
 
739
759
  # Credentials never enter the VM (issue 113; codex generalized in 116). The
@@ -756,6 +776,23 @@ acp:
756
776
  # so codex-acp runs in its native mode without an in-VM OAuth handshake or
757
777
  # refresh (both stay host-side). Every credential-bearing var is stripped from
758
778
  # the forwarded VM boot env.
779
+ #
780
+ # For pi: the host credential is the `github-copilot` entry in
781
+ # ~/.pi/agent/auth.json — run `pi` once on the host and `/login` with GitHub
782
+ # Copilot to create it (or export COPILOT_GITHUB_TOKEN with a ready Copilot
783
+ # bearer). The host reads the short-lived `access` Copilot token (NEVER the
784
+ # durable `refresh` GitHub-OAuth token) and substitutes it at egress to
785
+ # api.individual.githubcopilot.com; when it nears expiry the host re-exchanges
786
+ # the refresh token against api.github.com/copilot_internal/v2/token directly
787
+ # (no model call) and rewrites auth.json. In the guest, pi resolves its bearer
788
+ # from the COPILOT_GITHUB_TOKEN placeholder env var (used verbatim — pi's env
789
+ # path performs no exchange/refresh), and `defaultProvider: github-copilot` is
790
+ # pinned via a staged ~/.pi/agent/settings.json. Because pi has no built-in MCP
791
+ # client (and pi-acp ignores the ACP mcpServers descriptor), symphony also
792
+ # stages a per-dispatch extension at ~/.pi/agent/extensions/symphony-mcp.ts
793
+ # that bridges the symphony tools (transition / request_human_steering /
794
+ # propose_issue) to the MCP endpoint. Business/enterprise Copilot endpoints
795
+ # (api.business.githubcopilot.com etc.) are not wired — individual only.
759
796
 
760
797
  # model (string | null): optional model selector forwarded to the chosen adapter.
761
798
  # Each adapter profile knows how to surface it natively:
@@ -763,6 +800,10 @@ acp:
763
800
  # claude-agent-acp would (aliases like "opus", "sonnet", or full IDs
764
801
  # like "claude-opus-4-7").
765
802
  # codex — passed as `-c model="<value>"` argv to codex-acp (parsed as TOML).
803
+ # pi — written as `defaultModel` into the staged ~/.pi/agent/settings.json
804
+ # (pi-acp forwards no argv and pi has no model env var). Accepts a
805
+ # Copilot model id (e.g. "gpt-5", "claude-sonnet-4-6") or pi's
806
+ # provider/id + fuzzy forms; unset ⇒ pi's own Copilot default.
766
807
  # Leave unset / null to use the adapter's own default model. Default: null.
767
808
  # model: claude-opus-4-7
768
809
 
@@ -777,6 +818,9 @@ acp:
777
818
  # from drifting from the adapter's own supported list.
778
819
  # codex — not wired (codex-acp has no first-class effort knob on the wrapper);
779
820
  # setting `acp.effort` for a codex-backed state is a no-op.
821
+ # pi — not wired (pi's thinking level shares the same settings.json the
822
+ # model pin is staged into, and the injection channels have no merge
823
+ # semantics); setting `acp.effort` for a pi-backed state is a no-op.
780
824
  # Leave unset / null for the adapter's own default. Default: null.
781
825
  # effort: xhigh
782
826
 
package/WORKFLOW.yaml CHANGED
@@ -101,9 +101,14 @@ states:
101
101
  allowed_transitions: [Reflected]
102
102
  # Recurring spawn trigger. This active state mints a FRESH ephemeral
103
103
  # reflection issue into itself: `on_idle` when the orchestrator is idle and
104
- # >=1 issue reached a terminal state since the last cycle, and `after_terminal`
105
- # as a backstop once that many issues have reached a terminal state since the
106
- # last cycle. The terminal-transition counter resets to 0 on a successful mint.
104
+ # `idle_min_terminal` issues have reached a terminal state since the last
105
+ # cycle, and `after_terminal` as a backstop once that many issues have reached
106
+ # a terminal state since the last cycle. The terminal-transition counter resets
107
+ # to 0 on a successful mint. `idle_min_terminal: 3` raises the idle signal floor
108
+ # (was a hardcoded >=1): a quiet period reflects once >=3 issues have finished,
109
+ # collapsing strings of single-finish idle cycles into one and skipping cycles
110
+ # with too little new signal. The `after_terminal: 10` backstop still covers
111
+ # busy stretches, so reflection never starves.
107
112
  # `max_in_flight: 1` is load-bearing — it replaces the immortal ticket's
108
113
  # built-in "one location = one reflection" mutex, so a busy stretch never mints
109
114
  # a second cycle while one is still live. `title` stamps the mint time
@@ -113,6 +118,7 @@ states:
113
118
  # approve/discard, so this does not bypass the human gate.
114
119
  spawn:
115
120
  on_idle: true
121
+ idle_min_terminal: 3
116
122
  after_terminal: 10
117
123
  title: "Reflection {{ stamp }}"
118
124
  max_in_flight: 1
@@ -66,3 +66,9 @@ node = "24"
66
66
  "npm:@agentclientprotocol/claude-agent-acp" = "0.39.0"
67
67
  "npm:@zed-industries/codex-acp" = { version = "0.15.0", npm_args = "--ignore-scripts=false" }
68
68
  "npm:opencode-ai" = { version = "1.15.12", npm_args = "--ignore-scripts=false" }
69
+ # pi (adapter: pi) — the pi coding agent + the pi-acp ACP wrapper that spawns
70
+ # `pi --mode rpc` (pi has no native ACP mode). Both are pure JS with NO install
71
+ # scripts (pi's own docs install with --ignore-scripts), so plain pins suffice.
72
+ # pi-coding-agent needs node >= 22.19 — satisfied by the node pin above.
73
+ "npm:@earendil-works/pi-coding-agent" = "0.79.1"
74
+ "npm:pi-acp" = "0.0.27"
@@ -102,6 +102,27 @@ export function codexPlaceholderAuthSpec() {
102
102
  guestPath: CODEX_AUTH_GUEST_PATH,
103
103
  };
104
104
  }
105
+ // ── pi settings.json (provider + model) ────────────────────────────────────
106
+ /** Absolute guest path pi's global settings.json lands at. */
107
+ export const PI_SETTINGS_GUEST_PATH = '/root/.pi/agent/settings.json';
108
+ /**
109
+ * The pi `~/.pi/agent/settings.json` spec. pi-acp spawns `pi --mode rpc` with
110
+ * NO passthrough argv and pi has no model env var, so provider/model selection
111
+ * rides pi's own global settings file. `defaultProvider` is always pinned to
112
+ * `github-copilot` (the only provider symphony stages a credential for);
113
+ * `defaultModel` is included only when a model was chosen (omitted ⇒ pi's own
114
+ * Copilot default).
115
+ */
116
+ export function piSettingsSpec(model) {
117
+ return {
118
+ stagedName: 'pi-settings.json',
119
+ content: JSON.stringify({
120
+ defaultProvider: 'github-copilot',
121
+ ...(model !== null && model.length > 0 ? { defaultModel: model } : {}),
122
+ }),
123
+ guestPath: PI_SETTINGS_GUEST_PATH,
124
+ };
125
+ }
105
126
  /**
106
127
  * Aggregate every staged-file spec one adapter dispatch needs into a single
107
128
  * `StagedFileSpec[]`, in a stable order (identity/config first, then the
@@ -113,6 +134,9 @@ export function codexPlaceholderAuthSpec() {
113
134
  * • claude — optional non-secret identity (when extracted) + optional
114
135
  * effortLevel settings.json (when effort is set).
115
136
  * • codex — the fake placeholder auth.json (always; the init check needs it).
137
+ * • pi — the settings.json provider/model pin (always; `effort` is not
138
+ * wired for pi — there is no collision-free channel for pi's
139
+ * thinking level alongside the model file).
116
140
  */
117
141
  export function buildStagedConfigs(input) {
118
142
  const specs = [];
@@ -130,6 +154,10 @@ export function buildStagedConfigs(input) {
130
154
  specs.push(codexPlaceholderAuthSpec());
131
155
  break;
132
156
  }
157
+ case 'pi': {
158
+ specs.push(piSettingsSpec(input.model));
159
+ break;
160
+ }
133
161
  }
134
162
  return specs;
135
163
  }
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-config.js","sourceRoot":"","sources":["../../../src/core/credential/adapter-config.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,gEAAgE;AAChE,mFAAmF;AACnF,qEAAqE;AACrE,+EAA+E;AAC/E,EAAE;AACF,0EAA0E;AAC1E,gFAAgF;AAChF,+EAA+E;AAC/E,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,EAAE;AACF,sFAAsF;AACtF,gFAAgF;AAChF,iFAAiF;AACjF,qFAAqF;AACrF,mDAAmD;AACnD,mFAAmF;AACnF,EAAE;AACF,mDAAmD;AACnD,gFAAgF;AAChF,8EAA8E;AAC9E,kFAAkF;AAClF,0EAA0E;AAC1E,uEAAuE;AACvE,gFAAgF;AAChF,8DAA8D;AAS9D,8EAA8E;AAE9E,2EAA2E;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAExE,wEAAwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAAG,oBAAoB,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO;QACL,UAAU,EAAE,sBAAsB;QAClC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;QAChD,SAAS,EAAE,0BAA0B;KACtC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA+B;IAChE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO;QACL,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;YACtB,YAAY,EAAE;gBACZ,WAAW,EAAE,QAAQ,CAAC,WAAW;gBACjC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;aAC5C;SACF,CAAC;QACF,SAAS,EAAE,0BAA0B;KACtC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAE9E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAE9D,kFAAkF;AAClF,MAAM,CAAC,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO;QACL,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;YACtB,cAAc,EAAE,yBAAyB;YACzC,SAAS,EAAE,QAAQ;SACpB,CAAC;QACF,SAAS,EAAE,qBAAqB;KACjC,CAAC;AACJ,CAAC;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAwB;IACzD,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,kBAAkB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YAC1D,IAAI,QAAQ;gBAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5C,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,CAAC;YACvC,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"adapter-config.js","sourceRoot":"","sources":["../../../src/core/credential/adapter-config.ts"],"names":[],"mappings":"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"}
@@ -31,7 +31,7 @@ import { nonEmptyString } from './strings.js';
31
31
  // The seam between the config layer and the IO adapter layer: both sides agree
32
32
  // on which adapter ids exist. The full adapter *profile* lives elsewhere
33
33
  // (AdapterProfile / the shell's adapter registry); this is only the id set.
34
- export const KNOWN_ADAPTER_IDS = ['claude', 'codex'];
34
+ export const KNOWN_ADAPTER_IDS = ['claude', 'codex', 'pi'];
35
35
  export function isKnownAdapter(id) {
36
36
  return KNOWN_ADAPTER_IDS.includes(id);
37
37
  }
@@ -53,6 +53,10 @@ export function hostClaudeCredentialPath(homeDir) {
53
53
  export function hostCodexCredentialPath(homeDir) {
54
54
  return joinPath(homeDir, '.codex', 'auth.json');
55
55
  }
56
+ /** Absolute path to the host's pi credential file (`~/.pi/agent/auth.json`). */
57
+ export function hostPiCredentialPath(homeDir) {
58
+ return joinPath(homeDir, '.pi', 'agent', 'auth.json');
59
+ }
56
60
  // ─── codex credential resolution ─────────────────────────────────────────────
57
61
  /**
58
62
  * Pure: does codex have a resolvable credential from either valid source? The
@@ -80,6 +84,45 @@ export function codexCredentialAvailable(authFileText, env) {
80
84
  export function codexMissingCredentialMessage(homeDir) {
81
85
  return `adapter "codex" requires a host credential, but none is available: neither a token in ${hostCodexCredentialPath(homeDir)} (ChatGPT-OAuth tokens.access_token or OPENAI_API_KEY) nor an OPENAI_API_KEY environment variable is present`;
82
86
  }
87
+ // ─── pi (GitHub Copilot) credential resolution ───────────────────────────────
88
+ /**
89
+ * Pure: does pi have a resolvable GitHub-Copilot credential from either valid
90
+ * source? The host reads two — the `github-copilot` entry in `~/.pi/agent/auth.json`
91
+ * (either a live short-lived `access` Copilot token or the durable `refresh`
92
+ * GitHub-OAuth token the host refresher can mint one from) or a
93
+ * `COPILOT_GITHUB_TOKEN` env var (used verbatim as the Copilot bearer, no
94
+ * refresh). The shell reads the file (passing `null` when absent/unreadable)
95
+ * and its env in, mirroring the dispatch-time resolution.
96
+ */
97
+ export function piCredentialAvailable(authFileText, env) {
98
+ if (nonEmptyString(env['COPILOT_GITHUB_TOKEN']))
99
+ return true;
100
+ if (authFileText === null)
101
+ return false;
102
+ let parsed;
103
+ try {
104
+ parsed = JSON.parse(authFileText);
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ return piAuthFileHasCredential(parsed);
110
+ }
111
+ /** Human-readable explanation of which pi credential sources were checked. */
112
+ export function piMissingCredentialMessage(homeDir) {
113
+ return `adapter "pi" requires a host credential, but none is available: neither a github-copilot entry in ${hostPiCredentialPath(homeDir)} (run \`pi\` on the host and /login with GitHub Copilot) nor a COPILOT_GITHUB_TOKEN environment variable is present`;
114
+ }
115
+ function piAuthFileHasCredential(parsed) {
116
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
117
+ return false;
118
+ const entry = parsed['github-copilot'];
119
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
120
+ return false;
121
+ const rec = entry;
122
+ // Either a live access token (substitutable now) or the durable refresh token
123
+ // (the host refresher mints a fresh access token before dispatch needs it).
124
+ return nonEmptyString(rec['access']) !== null || nonEmptyString(rec['refresh']) !== null;
125
+ }
83
126
  function codexAuthFileHasToken(parsed) {
84
127
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
85
128
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"availability.js","sourceRoot":"","sources":["../../../src/core/credential/availability.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,oEAAoE;AACpE,6DAA6D;AAC7D,0EAA0E;AAC1E,EAAE;AACF,4EAA4E;AAC5E,iFAAiF;AACjF,yDAAyD;AACzD,EAAE;AACF,0DAA0D;AAC1D,EAAE;AACF,6DAA6D;AAC7D,2EAA2E;AAC3E,iFAAiF;AACjF,8EAA8E;AAC9E,0EAA0E;AAC1E,6EAA6E;AAC7E,+EAA+E;AAC/E,oFAAoF;AACpF,uEAAuE;AACvE,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,iFAAiF;AACjF,qEAAqE;AAIrE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,yEAAyE;AACzE,4EAA4E;AAE5E,MAAM,CAAC,MAAM,iBAAiB,GAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAE9E,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,OAAQ,iBAAuC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,yEAAyE;AACzE,oEAAoE;AACpE,MAAM,QAAQ,GAAG,cAAc,CAAC;AAEhC,gFAAgF;AAChF,EAAE;AACF,yEAAyE;AACzE,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,mDAAmD;AAEnD,gGAAgG;AAChG,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,OAAO,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC;AAC3D,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,OAAO,QAAQ,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAClD,CAAC;AAED,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,YAA2B,EAC3B,GAAgB;IAEhB,IAAI,cAAc,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,YAAY,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,6BAA6B,CAAC,OAAe;IAC3D,OAAO,yFAAyF,uBAAuB,CACrH,OAAO,CACR,8GAA8G,CAAC;AAClH,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAe;IAC5C,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACjF,MAAM,IAAI,GAAG,MAAiC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,IAAI,cAAc,CAAE,MAAkC,CAAC,cAAc,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IACvF,CAAC;IACD,OAAO,cAAc,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,KAAK,IAAI,CAAC;AACzD,CAAC;AAED,sEAAsE;AACtE,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E"}
1
+ {"version":3,"file":"availability.js","sourceRoot":"","sources":["../../../src/core/credential/availability.ts"],"names":[],"mappings":"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"}