@junghanacs/entwurf 0.51.0 → 0.53.1
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.
- package/AGENTS.md +1 -1
- package/BASELINE.md +116 -397
- package/CHANGELOG.md +191 -2
- package/CONTRIBUTING.md +4 -4
- package/DELIVERY.md +2 -2
- package/ENTWURF-CLI.json +1 -1
- package/ENTWURF.json +10 -4
- package/README.md +11 -8
- package/VERIFY.md +333 -143
- package/demo/README.md +1 -1
- package/demo/demo-baseline.sh +1 -1
- package/demo/demo.sh +1 -1
- package/docs/acp-backend-rail.md +248 -34
- package/docs/setup-clean-host.md +75 -80
- package/mcp/entwurf-bridge/dist/ENTWURF.json +10 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +90 -14
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +4 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/context.js +15 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/model-ids.js +9 -12
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +41 -56
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +33 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +4 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +34 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +2 -1
- package/package.json +14 -14
- package/pi/pi-durable/carrier/harness-setup.js +1 -1
- package/pi/pi-durable/carrier/subagent.js +1 -1
- package/pi/pi-durable/overlay/upstream-pin.json +15 -15
- package/pi/settings.reference.json +0 -1
- package/pi-extensions/acp-provider.ts +12 -6
- package/pi-extensions/lib/acp/acp-client.ts +6 -7
- package/pi-extensions/lib/acp/backend-adapter.ts +139 -15
- package/pi-extensions/lib/acp/backend.ts +261 -32
- package/pi-extensions/lib/acp/config.ts +4 -9
- package/pi-extensions/lib/acp/context.ts +17 -0
- package/pi-extensions/lib/acp/event-mapper.ts +236 -17
- package/pi-extensions/lib/acp/model-ids.js +9 -12
- package/pi-extensions/lib/acp/models.ts +46 -57
- package/pi-extensions/lib/acp/tool-surface.ts +34 -6
- package/pi-extensions/lib/entwurf-control-rpc.ts +5 -1
- package/pi-extensions/lib/fresh-call-composition.ts +37 -3
- package/pi-extensions/lib/herdr-fresh-call.ts +3 -1
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +3 -1
- package/pi-extensions/meta-bridge-pi-durable.ts +1 -1
- package/run.sh +62 -120
- package/scripts/check-acp-carrier-augment.ts +3 -21
- package/scripts/check-acp-prompt-lifecycle.ts +24 -3
- package/scripts/check-acp-provider-surface.ts +17 -26
- package/scripts/check-acp-session-reuse.ts +23 -4
- package/scripts/check-acp-stop-reason.ts +22 -3
- package/scripts/check-acp-stream-hooks.ts +24 -3
- package/scripts/check-acp-usage-accounting.ts +53 -24
- package/scripts/check-copilot-receive-arm.ts +19 -2
- package/scripts/check-entwurf-v2-surface.ts +7 -21
- package/scripts/check-fresh-call-dispatch.ts +9 -3
- package/scripts/check-gate-qualification.ts +6 -6
- package/scripts/check-herdr-fresh-call.ts +1 -0
- package/scripts/check-install-surface.ts +6 -0
- package/scripts/check-pi-durable-send.ts +4 -1
- package/scripts/check-probe-ordering.ts +34 -24
- package/scripts/check-release-gate-outcomes.ts +9 -23
- package/scripts/lib/codex-fresh-source-receipts.ts +12 -6
- package/scripts/lib/probe-cli-target.ts +3 -2
- package/scripts/mutants/acp-augment.json +0 -26
- package/scripts/mutants/acp-cortex.json +10 -183
- package/scripts/mutants/acp-foreground-only.json +11 -0
- package/scripts/mutants/acp-launch-namespace.json +3 -1
- package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
- package/scripts/mutants/acp-set-model-readback.json +488 -0
- package/scripts/mutants/agent-guidance.json +2 -2
- package/scripts/mutants/codex-native.json +2 -2
- package/scripts/mutants/control-send-receipt.json +47 -7
- package/scripts/mutants/mux-fresh-call.json +12 -1
- package/scripts/mutants/pack-install.json +4 -4
- package/scripts/mutants/pi-floor.json +8 -10
- package/scripts/mutants/release-gate.json +3 -3
- package/scripts/mutants/v2-surface.json +4 -6
- package/scripts/raw-acp-compaction-measure/probe.ts +8 -2
- package/scripts/smoke-acp-cortex-live.ts +4 -408
- package/scripts/smoke-acp-ordering-probe-live.ts +7 -1
- package/scripts/smoke-acp-session-reuse-live.ts +11 -2
- package/scripts/smoke-acp-v2-send-live.ts +1 -1
- package/scripts/smoke-herdr-raw-install-live.sh +4 -4
- package/scripts/check-acp-cortex.ts +0 -710
package/demo/README.md
CHANGED
|
@@ -24,7 +24,7 @@ takes/helpers/tests are maintainer tooling and are deliberately excluded from th
|
|
|
24
24
|
|
|
25
25
|
- Node at the `package.json#engines.node` floor; the checkout's pinned dependencies and
|
|
26
26
|
current compiled bridge. After a source/bootstrap change, build with `pnpm run build-bridge`.
|
|
27
|
-
- Pi at the checkout's supported floor (**current floor 1.5.
|
|
27
|
+
- Pi at the checkout's supported floor (**current floor 1.5.2**); `entwurf` on PATH must be
|
|
28
28
|
the intended source stable-bin. Source evidence is not registry-package acceptance.
|
|
29
29
|
- `herdr` on PATH at exactly `scripts/fixtures/herdr-supply.json#version`; `asciinema` v3.
|
|
30
30
|
- Herdr's existing Pi integration at
|
package/demo/demo-baseline.sh
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
# recordings actually used — they are kept as the historical record, not as a
|
|
16
16
|
# recommendation. Two things are stale about them: `entwurf/gpt-5.4` is no
|
|
17
17
|
# longer a curated `entwurf` ACP id at all (that provider serves only the
|
|
18
|
-
#
|
|
18
|
+
# Claude rows in pi-extensions/lib/acp/models.ts; Cortex ACP is disabled), and GLG's
|
|
19
19
|
# live-spend default moved to `openai-codex/gpt-5.6-luna` on a Plus plan. Do
|
|
20
20
|
# NOT copy these ids into a live harness default — see VERIFY.md and NEXT.md.
|
|
21
21
|
# Layout (tmux, 220x50):
|
package/demo/demo.sh
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# recordings actually used — they are kept as the historical record, not as a
|
|
13
13
|
# recommendation. Two things are stale about them: `entwurf/gpt-5.4` is no
|
|
14
14
|
# longer a curated `entwurf` ACP id at all (that provider serves only the
|
|
15
|
-
#
|
|
15
|
+
# Claude rows in pi-extensions/lib/acp/models.ts; Cortex ACP is disabled), and GLG's
|
|
16
16
|
# live-spend default moved to `openai-codex/gpt-5.6-luna` on a Plus plan. Do
|
|
17
17
|
# NOT copy these ids into a live harness default — see VERIFY.md and NEXT.md.
|
|
18
18
|
# Layout (tmux, 220x50):
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -5,9 +5,11 @@ It is not a second harness and does not create another citizen or socket layer.
|
|
|
5
5
|
host pi session already owns the record-backed citizen identity; each backend owns
|
|
6
6
|
its own process, auth, transcript, and native tool surface.
|
|
7
7
|
|
|
8
|
-
Claude is the
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Claude is the supported adapter. Cortex Code ACP is disabled: its retired model ids
|
|
9
|
+
are not registered and a direct selection refuses before launch, without a Claude fallback.
|
|
10
|
+
Existing Cortex settings, credentials and native citizen records are not removed.
|
|
11
|
+
Dated Cortex observations below are historical, not a current support claim.
|
|
12
|
+
Implementation history and audit chronology live in CHANGELOG, issues, and git.
|
|
11
13
|
|
|
12
14
|
## Boundary
|
|
13
15
|
|
|
@@ -55,6 +57,29 @@ backend-specific keys; `adapterSettings` remains opaque until handed back to its
|
|
|
55
57
|
A connection, model, carrier, MCP declaration, or overlay-relevant setting change must
|
|
56
58
|
change the reuse signature rather than mutate a live incompatible session.
|
|
57
59
|
|
|
60
|
+
**Session reuse needs a resident pi.** Reuse — the live ACP session kept across turns, each
|
|
61
|
+
later turn sending only the new user message — exists only in a pi process started with
|
|
62
|
+
`--entwurf-control` (`entwurf pi` adds it). Every other pi process, plain interactive `pi`
|
|
63
|
+
and `pi -p` included, is turn-scoped: each turn opens a new ACP session, sends the pi
|
|
64
|
+
transcript rendered as one text prompt, and tears the child down when the turn ends
|
|
65
|
+
(`resolveLifecyclePolicy`, `pi-extensions/lib/acp/session-store.ts`). This boundary is as
|
|
66
|
+
old as reuse itself (S2c, hang-safe one-shot exit); it is stated here so that "reuse without
|
|
67
|
+
resending the transcript" is not read as a property of every pi process. Nothing in a pi
|
|
68
|
+
profile or AGENTS file changes it.
|
|
69
|
+
|
|
70
|
+
**What a rebuilt prompt carries (`context.ts`).** The rebuild is a TEXT rendering of pi's own
|
|
71
|
+
transcript, not a replay: user text, assistant text and tool results become `User:` /
|
|
72
|
+
`Assistant:` / `Tool result (…)` / `Tool error (…)` lines; assistant thinking, structured tool calls, the system
|
|
73
|
+
prompt and lifecycle notices are left out; an image becomes the marker `[image omitted: <mime>]`
|
|
74
|
+
on both the rebuild and the reuse delta. Because no path carries image bytes to the backend, every
|
|
75
|
+
curated Claude ACP row declares `input: ["text"]` (`ACP_ROW_INPUT` in `models.ts`,
|
|
76
|
+
`[QK:ACP-ROWS-TEXT-ONLY-INPUT]`), although pi's catalog lists the Anthropic models as
|
|
77
|
+
`["text", "image"]`: that is the model behind the API, not this transport. What pi does with an
|
|
78
|
+
attachment offered to a text-only row is pi's behaviour and is unmeasured here. Real image
|
|
79
|
+
passthrough is a separate decision ([#161](https://github.com/junghan0611/entwurf/issues/161)); a
|
|
80
|
+
row gains `image` only together with that path and its proof. A backend's own file tools (Claude's
|
|
81
|
+
Read on an image file) are a different surface and are not affected.
|
|
82
|
+
|
|
58
83
|
A streaming message begins with `stopReason: "pending"`. ACP's terminal set is mapped
|
|
59
84
|
explicitly: `end_turn → stop`, `max_tokens → length`, `cancelled → aborted`; refusal,
|
|
60
85
|
exhausted turn budget, unknown, and absent reasons end as errors. The original ACP
|
|
@@ -73,6 +98,25 @@ none.** A turn ends only on a lifecycle event:
|
|
|
73
98
|
| the operator aborts | ACP `session/cancel` first — the agent closes its own turn (`cancelled → aborted`); process-group teardown only after a bounded grace, so an abort always returns |
|
|
74
99
|
| the child dies / stdio ends | the turn fails naming the exit status and the session-scoped stderr tail — or an honest bounded absence when transport EOF wins the race — on both the new and the reuse path |
|
|
75
100
|
|
|
101
|
+
**What an abort leaves (#142).** After the turn has settled, the operator is told one of two
|
|
102
|
+
things: `[acp: stopped — changes already made stay; next message continues this session]` when
|
|
103
|
+
the agent closed a reused turn on `session/cancel` (or ended a new turn `cancelled` with no
|
|
104
|
+
operator abort, so the session was retained), so the live ACP session stays and the next
|
|
105
|
+
message is a delta; or `[acp: stopped — changes already made stay; next message opens a new
|
|
106
|
+
session]` when the session was dropped — a cancelled first turn (only a live, un-aborted
|
|
107
|
+
session is retained), escalation teardown, or an abort before the prompt was sent — so the next
|
|
108
|
+
message rebuilds from the pi transcript. That rebuild states the cut to the new session: an
|
|
109
|
+
assistant turn pi recorded as `aborted` is followed by one harness line, `[turn status: aborted —
|
|
110
|
+
actions this turn had started may already have taken effect; cancellation is
|
|
111
|
+
not rollback]`, and claims nothing about what ran (`context.ts` `ABORTED_TURN_STATUS`; a reused
|
|
112
|
+
session gets the user delta only). Neither notice rolls anything back, and neither speaks
|
|
113
|
+
for the private Claude transcript behind the live session. An abort while a reuse turn waits
|
|
114
|
+
for a changed effort's answer settles on the abort, not on the answer or the 30s bound; no
|
|
115
|
+
prompt is sent and the session is dropped, since the effort the driver holds is unknown. Gates:
|
|
116
|
+
`check-acp-set-model-readback` (`backend.thinking.test.ts`, QKs `ACP-THINKING-REUSE-ABORT`,
|
|
117
|
+
`ACP-CANCEL-NOTICE-REUSE-KEPT`, `ACP-CANCEL-NOTICE-NEW-CLOSED`, `ACP-THINKING-REUSE-ABORT-LATE`;
|
|
118
|
+
`context.test.ts`, QKs `ACP-REBUILD-ABORTED-STATUS`, `ACP-REBUILD-ABORTED-EMPTY`).
|
|
119
|
+
|
|
76
120
|
Elapsed time is not evidence of failure, and a silent turn is not a failed turn: tool
|
|
77
121
|
use, reasoning, and provider queueing all legitimately outrun any number we could pick.
|
|
78
122
|
Suspected stalls are handled by exposing progress, never by a killing timer.
|
|
@@ -86,6 +130,27 @@ turned one long turn into four in 0.13.0. Gates: `check-acp-prompt-lifecycle` (b
|
|
|
86
130
|
with pi's own classifier as the oracle), `check-probe-ordering` (no production prompt
|
|
87
131
|
cutoff in source).
|
|
88
132
|
|
|
133
|
+
### Upstream 0.106.0 cancel and effort contracts → this rail (#142)
|
|
134
|
+
|
|
135
|
+
Upstream tests read at immutable `v0.106.0` = `51f514d7` (source read, not executed here; its CI
|
|
136
|
+
runs `vitest run` without the two opt-in env flags below, so a file's presence is not a run).
|
|
137
|
+
**Scope** is the reach of the upstream contract: `common` when it is ACP turn/cancel behaviour any backend's adapter must settle, `Claude` when it rests on a Claude adapter field, env or config; a Claude n=1 or
|
|
138
|
+
a fixture is never inherited by another backend. **Status**: covered (a QK kills on it), open (our
|
|
139
|
+
touchpoint is reachable and unmeasured), not exposed (our surface cannot reach it — not a backlog,
|
|
140
|
+
and not proof the vendor can do nothing).
|
|
141
|
+
|
|
142
|
+
| Upstream contract | Scope | Our touchpoint and proof | Status |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| A running tool's interrupted result is delivered before `cancelled`, verbatim, `_meta.claudeCode.nonExecutionKind:"user-rejected"` ([incomplete-tools:764](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/incomplete-tools.test.ts#L764-L780)) | Claude | rendered as `[tool:failed] …` text; a rebuild adds the `aborted` status line (`ACP-REBUILD-ABORTED-STATUS`/`-EMPTY`). Real n=1: the rejection was delivered while `start` was already on disk — the kind is not evidence of no effect | covered (fake) + n=1 |
|
|
145
|
+
| Results that arrive after the cancelled turn ended are dropped ([incomplete-tools:921](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/incomplete-tools.test.ts#L921-L972)) | common | measured on a scripted partner (#142 private probe, n=1 per cell) with an update of the cancelled turn A on a KEPT session: (P1) before the next turn B starts there is no handler, so it is dropped; (P3) while B sets effort, before B's prompt is sent, a reuse turn now opens its stream only when its own prompt goes out, so the update is neither shown nor sealed as B's (`[QK:ACP-REUSE-LATE-UPDATE-PRE-PROMPT]`); (P2) once B's prompt is out, `session/update` carries no turn id, so routing cannot tell A's update from B's and relies on the adapter not speaking for a turn it already answered (this upstream contract; the real Claude producer is unmeasured here) — a local counter cannot restore an attribution the wire does not carry | P1 measured (private probe n=1, no QK) · P3 pre-prompt window measured → fixed · P2 upstream dependence, open |
|
|
146
|
+
| A tool that finished as the cancel landed is reported completed ([incomplete-tools:940](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/incomplete-tools.test.ts#L940-L947)) | common | rendered `[tool:done]`; the turn still seals `aborted` | open — not measured cancel-specific |
|
|
147
|
+
| A background subagent keeps running past a foreground cancel ([incomplete-tools:822](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/incomplete-tools.test.ts#L822-L833), [:974](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/incomplete-tools.test.ts#L974)) | Claude | background tasks are off at launch (`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1`, foreground-only lane) | not exposed |
|
|
148
|
+
| A queued prompt cancelled before insertion is removed from Claude Code's queue and never runs ([turn-events:312](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/turn-events.test.ts#L312-L418)) | common | one prompt per session at a time (`inFlightKeys`); queueing is pi's (`PI-QUEUE-ABORT-DROPS-QUEUED`) | not exposed |
|
|
149
|
+
| A force-cancel while an update is being sent still ends the turn ([turn-events:419](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/turn-events.test.ts#L419-L450)) | common | our own bound: `session/cancel` first, teardown after the grace (`ABORT-SENDS-PROTOCOL-CANCEL`, `ABORT-ESCALATION-BOUNDED`); a reuse turn the agent closes keeps its session (`ACP-REUSE-COOPERATIVE-CANCEL`, `ACP-CANCEL-NOTICE-REUSE-KEPT`) | covered for our own bound (fake), not upstream's internal force-cancel; n=1 settle 25–65 ms |
|
|
150
|
+
| Effort applies through flag settings, the answer carries `currentValue`, a rejected selection keeps the old state, `default` is `null`, no update notification ([session-config-options:552](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/session-config-options.test.ts#L552-L655)) | Claude | set + read back, refusal fails before the prompt and drops a reused session (`ACP-CLAUDE-EFFORT-READBACK`); an abort while the answer is pending settles at once (`ACP-THINKING-REUSE-ABORT`, `-LATE` — beyond upstream). We never send `default`: no level refuses | covered (fake) + readback n=1 |
|
|
151
|
+
| A real Bash turn cancelled mid-sleep settles every announced tool call ([live-capability-session-audit:367](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/live-capability-session-audit.test.ts#L367); opt-in `RUN_LIVE_CAPABILITY_AUDIT`) | Claude | our private ESC runs (reuse and first turn) also observe the file effect, the next input and the session kept/closed | open (LIVE n=1 each, #142; no fake QK) |
|
|
152
|
+
| Session load/resume lifecycle ([session-load:21](https://github.com/agentclientprotocol/claude-agent-acp/blob/51f514d73c8bfd786979c84adb7a8517dbc22695/src/tests/v1/session-load.test.ts#L21); opt-in `RUN_INTEGRATION_TESTS`) | common | persisted resume/load is off — bootstrap decides only `new`/`reuse` | not exposed (canonical resume after restart is separate design) |
|
|
153
|
+
|
|
89
154
|
### Typed Claude prompt rejection — no automatic whole-prompt replay (#127)
|
|
90
155
|
|
|
91
156
|
Claude ACP 0.85.1 can reject a prompt with the wire SDK's `RequestError`, code `-32603`,
|
|
@@ -130,29 +195,30 @@ equal the published 0.36.1–0.37.1 manifests (`claude-agent-acp 0.90.0`, `@agen
|
|
|
130
195
|
1.8.0`); 0.31.0–0.36.0 shipped `0.85.1` / `1.6.0` and 0.30.0 shipped `0.79.0` / `1.4.0`
|
|
131
196
|
(`[측정 2026-10-16]` `git show <tag>:package.json`). The transitive Claude Agent SDK `0.3.296`
|
|
132
197
|
row has no direct manifest entry; its bump is recorded in CHANGELOG 0.36.1 (#128) and the ROADMAP
|
|
133
|
-
Dep bump ledger. 0.38.0 (#145) changes only the pi SDK set; the ACP rows are unchanged from 0.37.1
|
|
198
|
+
Dep bump ledger. 0.38.0 (#145) changes only the pi SDK set; the ACP rows are unchanged from 0.37.1
|
|
199
|
+
through the published 0.52.0. #142 landed `0.106.0` / `2.6.0` / `0.3.322` on main; #163 moves the
|
|
200
|
+
adapter to `0.107.0` and the transitive SDK to `0.3.323` (wire SDK unchanged). No npm release carries
|
|
201
|
+
either set, so the rows below are not a shipped manifest.
|
|
134
202
|
|
|
135
203
|
| Surface | Declaration | Class | What a green actually says |
|
|
136
204
|
|---|---|---|---|
|
|
137
205
|
| Entwurf package | `package.json` `version` | checkout declaration, not a release receipt | names the package label these rows sit beside; whether that label shipped, and with which rows, is CHANGELOG's record |
|
|
138
|
-
| pi runtime | SDK exact `1.5.
|
|
139
|
-
| ACP wire SDK | `@agentclientprotocol/sdk
|
|
140
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
141
|
-
| Claude Agent SDK | `0.3.
|
|
206
|
+
| pi runtime | SDK exact `1.5.2`, host `>=1.5.2 <1.6` | **exact** oracle + **closed range** | the SDK set the published 0.52.0 ships (#158) and its derived host range (0.51.0 shipped SDK 1.5.0, #155), not an operating-host acceptance; an ordinary Pi host inside the range is accepted by the source's version predicate, and the ceiling moves only on measurement. The published 0.36.0 through 0.37.1 shipped the 1.2.0 set (#141); 0.38.0 (#145) is the first to carry 1.3.0. Historical: the published 0.33.0 through 0.35.1 ship SDK exact 1.1.0, 0.32.0 ships SDK exact 1.0.4 with the next-minor ceiling (ROADMAP's 2026-10-07 bump entry), and 0.31.0 declared the earlier peer floor 1.0.2 |
|
|
207
|
+
| ACP wire SDK | `@agentclientprotocol/sdk 2.6.0` | **exact** | the Claude wire oracle (protocol v1; the experimental `Usage` type comes from the `./unstable` entry) |
|
|
208
|
+
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.107.0` | **exact**, bundled | #163 bump over #142's 0.106.0, unshipped (0.90.0 shipped 0.36.1–0.52.0); resolved before any PATH fallback; declaration is not compatibility evidence |
|
|
209
|
+
| Claude Agent SDK | `0.3.323` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
142
210
|
| Anthropic SDK | `0.100.1` | **exact**, peer-resolution only | satisfies the Agent SDK peer floor (0.93.0+); never an API client here (gate L4) |
|
|
143
211
|
| Claude Code runtime | `>=2.1.306` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
|
|
144
212
|
| Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
|
|
145
|
-
| Cortex Code | operator-installed CLI | **
|
|
213
|
+
| Cortex Code | operator-installed CLI | **disabled** | No supported Cortex ACP model or turn; old host receipts remain historical. |
|
|
146
214
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
not a direct LIVE certification — no Cortex evidence record is created by writing it down. It is
|
|
150
|
-
recorded here only so the on-demand row names a concrete number instead of an abstraction.
|
|
215
|
+
Historical host observation: **`Cortex Code v1.1.52` on `oracle` (2026-08-19)**.
|
|
216
|
+
It is not a current installation, support or LIVE certification claim.
|
|
151
217
|
|
|
152
218
|
Reading rules that do not bend:
|
|
153
219
|
|
|
154
|
-
- **Claude PASS never certifies Cortex, and
|
|
155
|
-
|
|
220
|
+
- **Claude PASS never certifies Cortex, and an old Cortex SKIP never becomes a PASS.**
|
|
221
|
+
Cortex ACP is now disabled; historical receipts remain bound to their own subjects.
|
|
156
222
|
- **Exact** means one measured build. **Range** means a closed accepted interval. **Floor** means a
|
|
157
223
|
minimum entwurf enforces itself. **On-demand** means the operator supplies the runtime and the
|
|
158
224
|
evidence names its version — presence of an adapter certifies no external release.
|
|
@@ -201,6 +267,9 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
201
267
|
the effort selector that ride it are unreachable for a second, independent reason as well:
|
|
202
268
|
`[측정 2026-09-10]` `git grep -c configOptions pi-extensions/lib/acp/` is **0**, and entwurf
|
|
203
269
|
has never set `EFFORT_CONFIG_ID`, which is the other gate on the SDK-side `applyFlagSettings`.
|
|
270
|
+
That second reason no longer holds from #142: entwurf now sets the `effort` config option on every
|
|
271
|
+
Claude session (§Claude effort). `recommendedValue` itself stays unreachable through the empty
|
|
272
|
+
`clientCapabilities`.
|
|
204
273
|
The rest of 0.76.0 is one refactor: `resolveModelPreference` moved into a new
|
|
205
274
|
`dist/session-model.*` (diff is comments and formatting), `dist/session-effort.*` is new, and
|
|
206
275
|
`setSessionConfigOption` — our only model-forcing wire call — is byte-identical across
|
|
@@ -210,6 +279,9 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
210
279
|
`resolvedProvider` and up onto the unconditional `session/new` path. entwurf passes `settings`
|
|
211
280
|
as an OBJECT (`tool-surface.ts:153`), so the branch is never taken. That makes "entwurf never
|
|
212
281
|
hands `settings` as a path" a contract rather than an accident; it is stated at that call site.
|
|
282
|
+
- **Advertised but never called — 0.106.0 adds `session/options`** (#1592) and setup-request
|
|
283
|
+
`configOptions`. entwurf calls neither; effort goes through `session/set_config_option` and its
|
|
284
|
+
readback, because upstream skips a refused setup entry silently.
|
|
213
285
|
- **0.77.0's BREAKING change does not reach us, twice over.** `claudeCode.options.agent` is no
|
|
214
286
|
longer forwarded — the adapter now shallow-copies the options object and `delete`s `agent` on
|
|
215
287
|
the unconditional `session/new` path (`[측정 2026-09-18, 0.79.0 dist/acp-agent.js:5934-5941 직독]`).
|
|
@@ -396,6 +468,49 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
396
468
|
the agent (#1218); a new session's first prompt carries our augment ahead of the user text,
|
|
397
469
|
a reuse turn does not.
|
|
398
470
|
|
|
471
|
+
- **0.90.0 → 0.106.0 (#142) moves the wire SDK a major and changes what reaches us as follows.**
|
|
472
|
+
`[측정 2026-11-13, oracle aarch64, private candidate install, no session/prompt]` receipts are
|
|
473
|
+
host-local under `.tmp-verify/acp106-readback-20261113/` (stage1 no-auth; stage2–4 authenticated
|
|
474
|
+
through the production `ensureClaudeConfigOverlay(realDir, overlayDir)` whitelist into a sandbox
|
|
475
|
+
overlay; account email redacted).
|
|
476
|
+
- **Wire and launch.** The adapter depends on `@agentclientprotocol/sdk` 2.6.0 (major since 0.94);
|
|
477
|
+
the client moves with it so the L2c shared-wire contract holds. Protocol stays v1: `initialize`
|
|
478
|
+
answered `protocolVersion: 1`, `loadSession: true` and session capabilities `additionalDirectories,
|
|
479
|
+
close, delete, fork, list, options, resume, status, systemPrompt, title`, plus
|
|
480
|
+
`_meta.steering.supported`. 0.103.0 narrowed `exports` to `.` and `./package.json`; the production
|
|
481
|
+
launcher anchors on `package.json` and imports the bin by file URL, so it booted 0.106.0 unchanged.
|
|
482
|
+
Two LIVE probe scripts that deep-imported `dist/` were repointed the same way.
|
|
483
|
+
- **Production `_meta` passes 0.100's request validation.** Three `session/new` calls carrying
|
|
484
|
+
`model/tools/settingSources/settings/disallowedTools/extraArgs` and the shipped carrier succeeded.
|
|
485
|
+
- **Initial config options** (0.106 #1592) arrive on `session/new`: `mode` = `bypassPermissions`
|
|
486
|
+
(the overlay `defaultMode` still beats 0.101's built-in Auto default), `model`, `effort`
|
|
487
|
+
(category `thought_level`, values `default, low, medium, high, xhigh, max`), `fast`, `ultracode`,
|
|
488
|
+
and on a later session `agent`. `session/options` and setup-request `configOptions` are
|
|
489
|
+
advertised but never called here; a refused setup entry is skipped silently upstream, so effort is
|
|
490
|
+
set by `session/set_config_option` and read back instead.
|
|
491
|
+
- **Model rows (authenticated).** `claude-opus-5-5` → `opus`/"Opus 5.5", `claude-sonnet-5-5` →
|
|
492
|
+
`sonnet`/"Sonnet 5.5", `claude-fable-5-1` → its own row "Fable 5.1", `claude-fable-5-2` → its own
|
|
493
|
+
row "Fable 5.2" (the alias `fable` also names Fable 5.2). Unauthenticated, setting a non-alias id
|
|
494
|
+
fails `-32603` "Could not resolve authentication method", which is an auth fact, not support.
|
|
495
|
+
The offered list is not stable inside one adapter process: 5 rows on the first sessions, 14 on a
|
|
496
|
+
later one, and a later session listed `claude-fable-5-1[1m]` beside `claude-fable-5-2`. Only the
|
|
497
|
+
set-model answers above are measured support; an offered row is not.
|
|
498
|
+
- **Effort on a live session.** On one session `high → low → max` each read back exactly with the
|
|
499
|
+
model unchanged; `minimal` was refused `-32602 Invalid params` and the session stayed usable.
|
|
500
|
+
Readback is the adapter's acceptance, not evidence of applied reasoning depth.
|
|
501
|
+
- **Idle compaction** (0.101 #1522) can send the adapter's own `/compact` prompt on an idle session
|
|
502
|
+
(1h cache TTL, ≥100k–200k context, ~54 min idle). 0.106 #1555 makes the inline `settings`
|
|
503
|
+
`idleCompaction` key win when no managed tier sets it. Our retained-session callbacks drop
|
|
504
|
+
notifications that arrive with no active turn, so such a turn would be invisible to pi. Our
|
|
505
|
+
inline `idleCompaction: false` is held only as a `_meta` shape (`ACP-CLAUDE-NO-IDLE-COMPACTION`);
|
|
506
|
+
its runtime effect on an idle session is unmeasured, and a managed tier would win over it.
|
|
507
|
+
- **Inert for us:** #1430 replays pre-compaction history on `session/load` (a client-view replay,
|
|
508
|
+
not a prompt re-dispatch; we never call `session/load`); #1589 concerns the bun single-file
|
|
509
|
+
release executable, not the npm dist we import; auth-status/account description changes are
|
|
510
|
+
notifications we ignore. `bypassPermissions` also shadows `canUseTool`
|
|
511
|
+
(`CLAUDE_SDK_CAN_USE_TOOL_SHADOWED` on stderr), so the permission callback is not a gate for
|
|
512
|
+
ordinary tool calls; the declared `tools`/`disallowedTools` surface is.
|
|
513
|
+
|
|
399
514
|
- **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
|
|
400
515
|
synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
|
|
401
516
|
`Compact conversation`, and `_meta.contextCompaction` schema v1 — where it used to arrive as
|
|
@@ -416,8 +531,8 @@ contract. An optional upstream feature is not a core-value gap.
|
|
|
416
531
|
- accumulated inactive minors are best folded into the next meaningful certification.
|
|
417
532
|
|
|
418
533
|
Otherwise record and defer. Every certification separates declared pins, deterministic/package
|
|
419
|
-
evidence
|
|
420
|
-
|
|
534
|
+
evidence and Claude LIVE aggregate evidence. Cortex ACP is disabled; its old LIVE observations
|
|
535
|
+
remain historical. Per-bump measurements live in the ROADMAP **Dep bump(별도 트랙)** ledger.
|
|
421
536
|
|
|
422
537
|
### Configured Claude ACP permissions
|
|
423
538
|
|
|
@@ -444,11 +559,15 @@ operator's repair named.
|
|
|
444
559
|
|
|
445
560
|
## Shipped adapters
|
|
446
561
|
|
|
447
|
-
|
|
562
|
+
Claude alone is supported. The former Cortex column below is a historical comparison,
|
|
563
|
+
not an executable route; Cortex model admission and direct adapter use now refuse.
|
|
564
|
+
|
|
565
|
+
| Seam | Claude | Former Cortex Code (historical) |
|
|
448
566
|
|---|---|---|
|
|
449
|
-
| Model ids | unprefixed `claude-sonnet-5-5`, `claude-opus-5-5`, `claude-fable-5-1` | `cortex-auto`, `cortex-claude-opus-5`, `cortex-claude-sonnet-5`, `cortex-openai-gpt-5.4`; prefix stripped before set-model |
|
|
567
|
+
| Model ids | unprefixed `claude-sonnet-5-5`, `claude-opus-5-5`, `claude-fable-5-1`, `claude-fable-5-2` | `cortex-auto`, `cortex-claude-opus-5`, `cortex-claude-sonnet-5`, `cortex-openai-gpt-5.4`; prefix stripped before set-model |
|
|
450
568
|
| Launch | bundled `claude-agent-acp`; `CLAUDE_AGENT_ACP_COMMAND` override | `cortex acp serve`; optional connection; `CORTEX_ACP_COMMAND` override; never a launch-time `-m` |
|
|
451
569
|
| Model authority | per-turn ACP set-model, its answer read back before the prompt | per-turn ACP set-model; an unavailable curated id fails before prompt; the answer is not read back |
|
|
570
|
+
| Thinking level | pi's level → `effort`, set and read back before the prompt (§Claude effort) | not declared: no `thinkingLevelMap`, `reasoning` is not carried |
|
|
452
571
|
| Carrier | engraving in `_meta.systemPrompt` | no system-prompt carrier; engraving rides the first-user augment |
|
|
453
572
|
| Overlay | `CLAUDE_CONFIG_DIR` whitelist, configured-empty hooks, native memory hidden | session-scoped isolated HOME + `SNOWFLAKE_HOME`, private `cortex/mcp.json`, measured-minimum auth passthrough |
|
|
454
573
|
| MCP | explicit wire `mcpServers` | explicit declarations projected to private `mcp.json` because Cortex ignores the wire field |
|
|
@@ -459,11 +578,10 @@ operator's repair named.
|
|
|
459
578
|
|
|
460
579
|
The unprefixed Claude curation uses exact Opus/Sonnet 5.5 ids; the ACP adapter does
|
|
461
580
|
not route the retired exact ids `claude-opus-5` or `claude-sonnet-5`, alias them to
|
|
462
|
-
a newer model, or rewrite existing session records.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
not a vendor turn). So the launches Entwurf builds admit only an EXACT curated id
|
|
581
|
+
a newer model, or rewrite existing session records. Pi owns fuzzy CLI model selection
|
|
582
|
+
before the adapter: an older seven-row fixture matched old bare 5 patterns to Cortex
|
|
583
|
+
rows (measured with the pure upstream resolver, not a vendor turn). Cortex rows are
|
|
584
|
+
now disabled. The launches Entwurf builds admit only an EXACT curated id
|
|
467
585
|
(`pi-extensions/lib/acp/model-ids.js`): `entwurf_fresh_call` with backend `pi` and an
|
|
468
586
|
`entwurf/<id>` model, and `entwurf_resume_call` of a session whose emitted provider is
|
|
469
587
|
`entwurf`, refuse anything else as `acp-model-not-curated` before a pane opens. A human
|
|
@@ -473,8 +591,8 @@ The adapter reads its set-model answer back before the prompt. The driver resolv
|
|
|
473
591
|
requested id onto one of the rows it offers and answers with that row's `value` as the
|
|
474
592
|
model option's `currentValue` (0.85.1 `dist/acp-agent.js:5459-5499`). Measured on
|
|
475
593
|
2026-10-12 against 0.85.1, and again unchanged on 2026-10-14 against 0.90.0 (the second
|
|
476
|
-
time also with the production session meta),
|
|
477
|
-
|
|
594
|
+
time also with the production session meta), and on 2026-11-13 against 0.106.0 authenticated
|
|
595
|
+
(#142), with no prompt sent: `claude-fable-5-1` and `claude-fable-5-2` each answer their own row,
|
|
478
596
|
while `claude-sonnet-5-5` and `claude-opus-5-5` answer the alias rows `sonnet` and
|
|
479
597
|
`opus`, named `Sonnet 5.5` and `Opus 5.5`. The id an alias row resolves to is not on the
|
|
480
598
|
ACP wire, so `CLAUDE_SET_MODEL_ALIAS_ROWS` (`backend-adapter.ts`) declares, for each
|
|
@@ -485,6 +603,38 @@ its own measured answer before it ships. `check-acp-set-model-readback` holds th
|
|
|
485
603
|
contract offline; it does not certify the installed driver's runtime, and no 5.5 prompt
|
|
486
604
|
turn is claimed by this measurement.
|
|
487
605
|
|
|
606
|
+
#### Claude effort (#142)
|
|
607
|
+
|
|
608
|
+
Pi's thinking level is carried as the adapter's `effort` session config option. Each curated
|
|
609
|
+
Claude row registers a `thinkingLevelMap` (`claudeAcpThinkingLevelMap`, `models.ts`): `low`,
|
|
610
|
+
`medium`, `high`, `xhigh` and `max` map to the effort value of the same name, `off` and `minimal`
|
|
611
|
+
are `null`. So pi's own selector offers only `low..max`, and pi's normal clamp moves a saved `off`
|
|
612
|
+
or `minimal` to `low` before any turn. The registry still decides which levels a model has: a
|
|
613
|
+
level it marks `null` stays unsupported, and `xhigh`/`max` appear only where it maps them. The
|
|
614
|
+
effort value `default` is the vendor's own choice; no pi level maps to it.
|
|
615
|
+
|
|
616
|
+
At turn entry the adapter resolves the level before any process or session is touched. A turn
|
|
617
|
+
that arrives with no level, an unsupported level, or a map value outside the effort set refuses as
|
|
618
|
+
`acp-thinking-level-unsupported`, with nothing spawned. "No level" covers two cases pi does not
|
|
619
|
+
distinguish on the wire: the agent sends no `reasoning` for `off`, and none when no level is set.
|
|
620
|
+
Pi's own turn and compaction paths always carry a level for these rows after the clamp; the
|
|
621
|
+
refusal is for callers outside that path, and it says so rather than run at a depth nobody chose.
|
|
622
|
+
|
|
623
|
+
A new session sets effort right after set-model; a reused session sets it again only when the
|
|
624
|
+
level changed since the value it last read back. The level is not part of the reuse signature, so
|
|
625
|
+
changing it keeps the live conversation. The answer must read the value back as the `effort`
|
|
626
|
+
option's `currentValue`; a refused set (`-32602` for an unoffered value), a missing option or
|
|
627
|
+
another value fails the turn before the prompt, and a reused session is dropped, so the next turn
|
|
628
|
+
opens a new session and resends the transcript. The text we author reads as neither transient nor
|
|
629
|
+
overflow to pi; the sealed message still appends the session's stderr tail like any other
|
|
630
|
+
pre-prompt failure, and a bootstrap timeout keeps its `timed out` wording, so either can classify as
|
|
631
|
+
transient. A replay of that turn re-dispatches no prompt that ran. Each accepted set shows the operator a lifecycle notice `[acp: effort <value> read back]` (a new
|
|
632
|
+
session always, a reused one only when the value changed; a refused set shows none). Like the other
|
|
633
|
+
lifecycle notices it is excluded from the transcript a later turn sends and from the reuse signature.
|
|
634
|
+
Readback is the adapter's acceptance of the value, not evidence of the
|
|
635
|
+
reasoning depth applied or of turn speed. `check-acp-set-model-readback` holds the contract
|
|
636
|
+
offline (`backend.thinking.test.ts`, five effort mutants).
|
|
637
|
+
|
|
488
638
|
The bundled adapter resolves before any PATH fallback. Its overlay retains only the
|
|
489
639
|
auth/runtime state required by the Claude Agent SDK and hides operator memory, hooks,
|
|
490
640
|
agents, history, and local settings. Rich project/operator context rides the first-user
|
|
@@ -504,6 +654,9 @@ rendering contract, not a capability bit flip.
|
|
|
504
654
|
|
|
505
655
|
### Cortex Code audit (D1–D10)
|
|
506
656
|
|
|
657
|
+
**Historical:** this audit describes the former Cortex ACP adapter, not a supported
|
|
658
|
+
route. The current product refuses Cortex selection before launch.
|
|
659
|
+
|
|
507
660
|
The original audit labels remain useful coordinates for source comments and gates:
|
|
508
661
|
|
|
509
662
|
| Audit | Landed contract |
|
|
@@ -638,15 +791,11 @@ Live axes:
|
|
|
638
791
|
|
|
639
792
|
```bash
|
|
640
793
|
LIVE=1 ./run.sh release-gate /path/to/scratch --cut
|
|
641
|
-
LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> \
|
|
642
|
-
./run.sh smoke-acp-cortex-live
|
|
643
794
|
```
|
|
644
795
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
Per-cut counts, digests, versions, and host observations belong in BASELINE/CHANGELOG,
|
|
649
|
-
not this standing contract.
|
|
796
|
+
Cortex ACP is disabled; its former direct LIVE smoke refuses without probing a vendor.
|
|
797
|
+
This does not waive the aggregate's Claude, Codex or native-citizen axes. Per-cut
|
|
798
|
+
counts, digests, versions, and host observations belong in BASELINE/CHANGELOG.
|
|
650
799
|
|
|
651
800
|
## 11-8. Compaction is a tool lifecycle (0.75.0 onward)
|
|
652
801
|
|
|
@@ -719,8 +868,73 @@ The adapter keeps its own turn-settlement bookkeeping internally, and our event
|
|
|
719
868
|
update kinds it does not handle (`event-mapper.ts:377-378`), so nothing above is a claim that
|
|
720
869
|
Entwurf must replicate vendor internals. The reason to stay put is the five contracts, not a ledger.
|
|
721
870
|
|
|
871
|
+
## 11-10. Pi's own summary is a one-shot, tool-less turn (#163)
|
|
872
|
+
|
|
873
|
+
Pi summarizes for `/compact`, the compaction threshold, overflow recovery and `/tree` branch
|
|
874
|
+
summaries through one call, `completeSummarization` (pi-coding-agent 1.5.2
|
|
875
|
+
`dist/core/compaction/compaction.js:477-489`). That request declares no tools and carries
|
|
876
|
+
`cacheRetention: "none"`; an ordinary agent-loop turn carries no `cacheRetention`. Until #163 the
|
|
877
|
+
tool-surface preflight read the empty surface as "read/bash/edit/write excluded" and refused every
|
|
878
|
+
summary on a Claude ACP model.
|
|
879
|
+
|
|
880
|
+
- **Admission.** `isPiSummaryRequest` (`tool-surface.ts`) needs BOTH an empty replayed tool
|
|
881
|
+
surface and `cacheRetention === "none"`. An ordinary turn with every tool excluded keeps the
|
|
882
|
+
refusal. Pi's prompt text and session id are not read; Pi fills the id with a fresh `uuidv7()`.
|
|
883
|
+
- **The turn.** A new child under the turn-scoped policy, closed after its one prompt whatever the
|
|
884
|
+
process policy says. `tools`, `mcpServers`, skill plugins, the permission allow list and
|
|
885
|
+
`settingSources` are all empty, so no CLAUDE.md, hook or plugin is read from disk. The operator's engraving carrier stays, rendered with an empty MCP list. No first-message
|
|
886
|
+
augment. Pi's summarization instructions arrive as a system message, which the transcript
|
|
887
|
+
flattening drops, so their text (`getCurrentSystemPrompt`) is sent as the first prompt block.
|
|
888
|
+
No notice of any kind: the summary Pi stores is exactly the model's text, and the session file
|
|
889
|
+
is the embedding source. Pi keeps an `aborted` summary (it refuses only `error` and `length`,
|
|
890
|
+
pi-coding-agent 1.5.2 `compaction.js:454-462`), so a `cancelled` stop that Pi did not abort is
|
|
891
|
+
sealed as a non-transient error instead.
|
|
892
|
+
- **After compaction.** `acp-provider.ts` subscribes to `session_compact` and closes that Pi
|
|
893
|
+
session's retained child at once (GLG 2026-11-14), unless a turn is in flight. A session is busy
|
|
894
|
+
only inside its key's in-flight claim, so the in-flight check covers both. The next turn opens a
|
|
895
|
+
new child from the compacted transcript.
|
|
896
|
+
|
|
897
|
+
Proof: `pi-extensions/lib/acp/backend.summary-turn.test.ts` drives Pi's own `generateSummary` and
|
|
898
|
+
`generateBranchSummary` through `streamAcpTurn`, so the request shape is Pi's, not a fixture's. A Pi
|
|
899
|
+
upgrade that stops marking summaries with `cacheRetention: "none"` turns it red. Not covered here:
|
|
900
|
+
the adapter's own compaction inside a prompt we sent (§11-8), and the threshold value.
|
|
901
|
+
|
|
902
|
+
## 11-11. What a running turn shows (#162, #163)
|
|
903
|
+
|
|
904
|
+
The mapper's tool lines come in two kinds, and the kind is a contract with two readers outside
|
|
905
|
+
this file.
|
|
906
|
+
|
|
907
|
+
| Line | Signed | Replayed into a new session | Reuse signature |
|
|
908
|
+
|---|---|---|---|
|
|
909
|
+
| `[tool:start\|done\|failed\|cancelled] <title>[ — <summary>]`, `[permission:<d>] <title>` | no | yes | yes |
|
|
910
|
+
| `[tool:running] <title>[ — <elapsed>]`, `[tool:output] <title>` + preview, `[acp: …]` | yes | no | no |
|
|
911
|
+
|
|
912
|
+
- `[tool:running] <title>` without elapsed time names a call once its title is known: a Bash call
|
|
913
|
+
opens as "Terminal" and its command arrives as a title-only update. A beat in the same update
|
|
914
|
+
already names the title, so it prints alone.
|
|
915
|
+
- `[tool:output]` shows at most 5 lines and 400 UTF-8 bytes of a finished call's output: the head
|
|
916
|
+
on success, the tail on failure. A line keeps its indentation (tabs expand to three spaces), so a
|
|
917
|
+
stack trace or a diff reads as in Pi. When the whole output fits on the one-line summary, it is
|
|
918
|
+
not shown.
|
|
919
|
+
- Every line, one-line or preview, is cleaned the way Pi cleans tool output before showing it
|
|
920
|
+
(pi-coding-agent 1.5.2 `getTextOutput`: `stripAnsi`, then `sanitizeBinaryOutput`, then CR
|
|
921
|
+
dropped), with DEL and C1 also removed. Pi's own range would leave an 8-bit OSC (`0x9D` … `0x9C`)
|
|
922
|
+
intact. Pi does not export these helpers from its root, so the mapper carries the same
|
|
923
|
+
expression, and a test compares it with Pi's installed functions. pi-tui's public
|
|
924
|
+
`stripTerminalSequences` is not a substitute: it ends a CSI only on `m/G/K/H/J` and scans ahead
|
|
925
|
+
for one, so on tool output it deletes text (`"\x1b[2A keep this text then Hm"` → `"m"`, measured
|
|
926
|
+
2026-11-14 at pi-tui 1.5.2). No strip-ansi or ansi-regex package is in the dependency tree.
|
|
927
|
+
- `[acp: compaction <trigger>[ failed] <pre>[→<post>][ in <duration>]]` reports the backend's own
|
|
928
|
+
compaction from the `rawOutput` facts of its `compact` tool call, once per compaction.
|
|
929
|
+
|
|
930
|
+
The second reader is andenken's `control-v1` selection (andenken `session-selection.ts`, read at
|
|
931
|
+
`e5165ecc`, 2026-11-14). It drops every signed lifecycle block whatever its text, and drops an
|
|
932
|
+
unsigned block only when it is exactly one sanitized `[tool:start|done|failed|cancelled]` or
|
|
933
|
+
`[permission:*]` line. An unsigned line with a new label, or a changed unsigned grammar, is
|
|
934
|
+
embedded. So a new tool line is signed, and changing the unsigned grammar goes to GLG first;
|
|
935
|
+
andenken is not changed from here.
|
|
936
|
+
|
|
722
937
|
## Open work
|
|
723
938
|
|
|
724
939
|
- causal MCP-readiness diagnosis and, only with proof, a backend-invariant fence;
|
|
725
|
-
- broader installed-host and cross-machine Cortex evidence;
|
|
726
940
|
- persisted ACP resume/load, which is not implemented by today's in-memory reuse.
|