@junghanacs/entwurf 0.30.0 → 0.30.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/CHANGELOG.md +117 -0
- package/README.md +6 -1
- package/VERIFY.md +6 -3
- package/docs/acp-backend-rail.md +111 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +43 -0
- package/package.json +4 -4
- package/pi-extensions/lib/acp/backend-adapter.ts +68 -0
- package/pi-extensions/lib/acp/backend.ts +41 -5
- package/run.sh +14 -3
- package/scripts/check-gate-qualification.ts +2 -1
- package/scripts/check-herdr-plugin-build.ts +159 -57
- package/scripts/fixtures/herdr-supply.json +8 -8
- package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
- package/scripts/mutants/acp-vendor-rejection.json +62 -0
- package/scripts/mutants/herdr-plugin-build.json +11 -0
- package/scripts/mutants/herdr-supply.json +3 -3
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +21 -7
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,123 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.30.1 - 2026-10-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Claude incomplete foreground-tool failures no longer invite a whole-prompt replay (#127).**
|
|
12
|
+
Only a non-aborted prompt rejection decoded as the root ACP SDK's `RequestError`, code
|
|
13
|
+
`-32603`, vendor `errorKind: incomplete_tool_call`, receives an authored unsafe-to-replay
|
|
14
|
+
verdict. Some tools may already have run. The original rejection and stderr/lifecycle/launch
|
|
15
|
+
evidence stay in existing diagnostics rather than tainting pi's retry/overflow classifier.
|
|
16
|
+
FIRST failure, abort precedence, exactly one error seal, and new/reuse session drop remain;
|
|
17
|
+
other failures and Cortex retain their legacy paths. This is not a global retry disable or
|
|
18
|
+
a claim about vendor-internal retries or real incomplete-tool frequency.
|
|
19
|
+
- **Herdr's existing plugin/runtime consumption boundary is aligned (#126).** The follow-up to
|
|
20
|
+
0.30.0 aligned the plugin manifest with its locked runtime, re-pinned the published npm bytes,
|
|
21
|
+
and measured Herdr 0.9.3 supply against the unchanged 0.9.0 admission floor. Sandbox path
|
|
22
|
+
normalization and the Pi-native callback oracle were corrected. Operating Herdr/config stayed
|
|
23
|
+
unchanged; no permanent plugin/package equality policy was introduced.
|
|
24
|
+
- **Herdr build verification covers both carriers and the real default lock reader.** Explicit
|
|
25
|
+
npm and checkout fixtures assert exact sequential acquisition counts and final artifact identity.
|
|
26
|
+
The uninjected default reader is checked against an independent committed JSON oracle, with a
|
|
27
|
+
dedicated wrong-directory mutant. This changes proof, not the production build/runtime/activation.
|
|
28
|
+
- **Published support, source observations and execution evidence stay distinct (#127).** The
|
|
29
|
+
ACP rail and dependency ledger separate conditional settings, capability-gated and ambient
|
|
30
|
+
changes from runtime certification, stock auth messages from sealed legacy stderr tails,
|
|
31
|
+
upstream comments from unread SDK internals, and native/platform/loader contexts. The typed
|
|
32
|
+
rejection row points only to its deterministic gate: normal raw/reuse LIVE turns do not induce
|
|
33
|
+
that rejection.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Claude ACP closure:** adapter `0.79.0` → `0.85.1`, shared direct/child ACP wire SDK `1.4.0` →
|
|
38
|
+
`1.6.0`, Agent SDK `0.3.274` → `0.3.286`; the existing shared-wire contract is retained.
|
|
39
|
+
New reachable upstream behaviour is classified in the ACP rail and ROADMAP ledger, not
|
|
40
|
+
silently adopted as new Entwurf policy. Pi 1.0's range and public bridge verbs are unchanged.
|
|
41
|
+
- **Unpublished Herdr verification candidate:** plugin metadata is 0.30.1 and uses the closed `herdr-checkout`
|
|
42
|
+
verification carrier while 0.30.1 is unpublished. No npm integrity is fabricated. After
|
|
43
|
+
publication, re-pin only the measured registry artifact and re-prove that acquisition source;
|
|
44
|
+
switching an installed source still requires explicit deactivate/activate.
|
|
45
|
+
|
|
46
|
+
### Verification
|
|
47
|
+
|
|
48
|
+
- Implementation/source review, related MUST LIVE and pre-version landing receipts are in
|
|
49
|
+
[#127](https://github.com/junghan0611/entwurf/issues/127), with #126's Herdr acceptance separate.
|
|
50
|
+
Main landing `f24820c` passed required four CI jobs, full411s and qualification841/841.
|
|
51
|
+
These are pre-version receipts, not this release's exact-SHA acceptance.
|
|
52
|
+
- The gate-only amendment landed separately at `61dbef7`: independent review closed with
|
|
53
|
+
zero Blockers/Defects; the frozen 0.30.0 source passed local qualification **842/842** and
|
|
54
|
+
full645s. Its [exact-SHA CI](https://github.com/junghan0611/entwurf/actions/runs/37181269379)
|
|
55
|
+
passed all four required jobs, full361s and qualification842/842. These are pre-version
|
|
56
|
+
implementation receipts, not 0.30.1 release acceptance. The original npm-only/version-pinned
|
|
57
|
+
HPB failure, old wrong-directory mutant survival, inventory RED, PARTIAL13/13 and PARTIAL14/14
|
|
58
|
+
receipts remain separate history; offline four positive/two negative controls are not registry
|
|
59
|
+
proof (`~/tmp/e0301-prepare/gate-fix/implementation-floor/`).
|
|
60
|
+
- **Fresh 0.30.1 working-candidate floor:** P4 full641s, followed by
|
|
61
|
+
`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.30.1.SLE9uA --cut`:
|
|
62
|
+
**MUST PASS=24 FAIL=0 SKIP=0**, **BEHAVIOR PASS=1 FAIL=0 SKIP=0**, exit0/cutOK;
|
|
63
|
+
its own full645s and qualification **842/842** passed. The versioned candidate on
|
|
64
|
+
`61dbef7` stayed frozen through both runs (seal
|
|
65
|
+
`541cc5a368315e6ecac4548a3492ce73024c6fe9dc5613077e14898d4fd76879`).
|
|
66
|
+
Complete log: `/tmp/entwurf-release-gate-0.30.1.SLE9uA/release-gate.log`, SHA256
|
|
67
|
+
`ea070c2ea019c05766e40e3442d1a31d68894e6cb3da7f7bbb80460c782bc020`.
|
|
68
|
+
- **Native Codex caller-seat acceptance:** 65 assertions, native `gpt-5.6-luna` and
|
|
69
|
+
Pi `openai-codex/gpt-6-luna`; personally launched app-server PID2143836 in session
|
|
70
|
+
A `$309` and initial Pi/fresh Codex/outbound Pi all in S `$316`, windows
|
|
71
|
+
`@810/@811/@812`. The outbound receipt names `codex-title-anchor`; both exact
|
|
72
|
+
source-role audits completed3/3. The complete pre-cleanup evidence is retained at
|
|
73
|
+
`.probe-artifacts/codex-fresh-live-fypXrI/`; decoded decisive receipts accompany
|
|
74
|
+
`~/tmp/e0301-prepare/fresh-prepare/p5-accepted.json`.
|
|
75
|
+
- **On-demand long-turn:** Sonnet5 completed the original three sequential240s
|
|
76
|
+
foreground waits in736147ms, with exactly one cold ACP bootstrap and no reported
|
|
77
|
+
retry/timeout; the same candidate seal remained MATCH. This normal-turn receipt does
|
|
78
|
+
not induce `incomplete_tool_call` or certify vendor-internal retry frequency.
|
|
79
|
+
- **Immediate P9:** census113 unused roots, four owned/aged/unused roots444K and one
|
|
80
|
+
reparented GLG Emacs daemon. All four roots were archived before four-fence-checked
|
|
81
|
+
reclamation; no process was killed. Post-census reports110 unused/zero reclaimable
|
|
82
|
+
roots, with Emacs untouched. Archive SHA256
|
|
83
|
+
`3132ab92d26702a5b1e3ddf9298e7e77c6abe63c276625ac2cd93b12558ad19a`;
|
|
84
|
+
per-root fences and before/after observations are in
|
|
85
|
+
`~/tmp/e0301-prepare/fresh-prepare/p9-reclaim/receipt.json`.
|
|
86
|
+
- **Claude checkout-provenance certification of the candidate tree:** the local
|
|
87
|
+
owner-managed installer was refreshed from this working0.30.1 checkout, then a NEW
|
|
88
|
+
native Claude2.1.289 session was opened without sending a prompt. Install finished
|
|
89
|
+
`2026-10-04T10:20:16.328Z`; fresh garden `20261004T192019-c50e8a` was created at
|
|
90
|
+
`10:20:19.625Z`. Exact launched PID3397500/startKey `linux:284430975` joins both
|
|
91
|
+
markers, its unique V3 record and registered bridge child3397563. Registry-authoritative
|
|
92
|
+
cached plugin0.1.0 and stable XDG assembly match the checkout's hook/writer/session-id/
|
|
93
|
+
registry bytes; plugin0.1.0 is not the root package version. Strict doctor exit0:
|
|
94
|
+
three live owner joins, source=assembled=installed writer `8558bb3a5aa8`, registry
|
|
95
|
+
`277c8dc2a183`, store v1=0/v2=0/v3=1476, PASS. The targeted leaf completed27s;
|
|
96
|
+
**full/BODY/P5 reruns=0**, leaf seal
|
|
97
|
+
`1f754eeb12ff800b2d7c1122d2b8fad030c9311e1784aab67166916ad3eb5364` MATCH.
|
|
98
|
+
Receipt `~/tmp/e0301-prepare/fresh-prepare/criterion4.json` SHA256
|
|
99
|
+
`0af3edc3d913eae2157d8229f7408c670f47b8bdd6363ff8e1f1c8a51e2b1fe6`;
|
|
100
|
+
doctor log SHA256 `6922043ddb500c34edaba5f70115d910cbdbfe7a1d01f8ff9ab28f2b3cf9a83f`.
|
|
101
|
+
This satisfies the pre-commit fresh-native criterion in dev-clone mode, not emitted-JS
|
|
102
|
+
package/bin-shim, final-artifact, registry, model-turn or fresh delivery/reply acceptance.
|
|
103
|
+
- **Explicit one-cut agy exception, authorized by GLG (2026-10-04):**
|
|
104
|
+
“명시 예외 승인 npm publish 전까지 쭉 가줘” authorizes this0.30.1 cut to proceed
|
|
105
|
+
without criterion7 acceptance. **agy native-push is NOT CERTIFIED on the1.2 line**:
|
|
106
|
+
measured on oracle/agy1.2.16, the answering LS port requires a CSRF nonce the external
|
|
107
|
+
sender does not legitimately hold; authenticated current host/CID join and same-gid
|
|
108
|
+
round-trip are absent. HTTP200/grpc16 `missing CSRF token` is not successful delivery.
|
|
109
|
+
The requirement's introduction date is unknown. The checked adapter/imprint/raw-send/
|
|
110
|
+
LIVE/pin subjects are unchanged from0.30.0; last native acceptance remains1.1.0
|
|
111
|
+
(2026-07-14), `PIN_AGY_MINOR=1.1` and DELIVERY's certification row are unchanged.
|
|
112
|
+
Follow-up [#128](https://github.com/junghan0611/entwurf/issues/128) is prerequisite
|
|
113
|
+
research: own tool-shell key presence/current CID/garden first, no nonce extraction,
|
|
114
|
+
discovery/cache/manager or unapproved implementation. This exception does not label
|
|
115
|
+
agy green, modify the permanent floor, or authorize npm publication.
|
|
116
|
+
- **No duplicate local floor:** GLG requested retaining the existing green evidence
|
|
117
|
+
and resolving only the two native leaves. P4/full, qualification BODY, P5 and duplicate
|
|
118
|
+
local M0/full are not restarted for the post-verdict evidence/policy prose. Exact
|
|
119
|
+
prepared-SHA CI (four required jobs plus its qualification BODY) and one preserved
|
|
120
|
+
final-artifact consumer acceptance remain separate mandatory make evidence. The
|
|
121
|
+
working-candidate receipts above do not certify those later axes or registry bytes;
|
|
122
|
+
verification prose added after each frozen verdict is not the bytes that run consumed.
|
|
123
|
+
|
|
7
124
|
## 0.30.0 - 2026-10-02
|
|
8
125
|
|
|
9
126
|
### Changed
|
package/README.md
CHANGED
|
@@ -184,7 +184,12 @@ compiled bridge after record birth, under the citizen's identity, and Pi supervi
|
|
|
184
184
|
that MCP child. **MCP is ingress; pi receive remains the record-addressed Unix
|
|
185
185
|
control socket.** Garden identity and dispatch stay Entwurf's. What `pi-durable`
|
|
186
186
|
or Pi codemode should mean here remains undecided: this cut neither adopts nor
|
|
187
|
-
certifies them.
|
|
187
|
+
certifies them. The 0.30.0 follow-up separately measured Herdr supply 0.9.3 and
|
|
188
|
+
re-pinned the plugin runtime to published npm 0.30.0 with manifest 0.30.0; its
|
|
189
|
+
public consumer and CI receipts live in the #126 thread. That dated alignment is
|
|
190
|
+
not a permanent version-equality rule. Unpublished 0.30.1 uses the closed
|
|
191
|
+
`herdr-checkout` verification carrier; the [plugin source contract](./plugins/herdr/README.md)
|
|
192
|
+
owns the measured npm re-pin and source-specific proof owed after publication.
|
|
188
193
|
|
|
189
194
|
Entwurf is the 0.12+ successor to
|
|
190
195
|
[`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp):
|
package/VERIFY.md
CHANGED
|
@@ -96,7 +96,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
|
|
|
96
96
|
|
|
97
97
|
> **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
|
|
98
98
|
|
|
99
|
-
> **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0
|
|
99
|
+
> **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`). The **unpublished 0.30.1 preparation** now uses that closed verification carrier again, without inventing a registry integrity. The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies a future 0.30.1 registry acquisition. After publication and a measured re-pin, that source owes its own public-ref run; checkout candidate evidence does not transfer. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
|
|
100
100
|
|
|
101
101
|
> **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
|
|
102
102
|
|
|
@@ -251,8 +251,10 @@ every focused gate green at the time:
|
|
|
251
251
|
`FRESHCALL-CODEX-PREMUTATION-MCP` in a manifest the lane never touched; qualification reported
|
|
252
252
|
it `MUTANT-STALE` (find matched 0×). **A stale mutant is a claim nobody is testing** — its
|
|
253
253
|
assertion keeps passing while the mutation that gives it meaning is never applied — which is why
|
|
254
|
-
this is a red rather than a warning. `check-gate-manifests`
|
|
255
|
-
|
|
254
|
+
this is a red rather than a warning. `check-gate-manifests` also reads each subject and
|
|
255
|
+
validates the joined `find` exactly once (`[QK:MUTANT-FIND-MATCHES-SUBJECT]`): #127's moved
|
|
256
|
+
reuse-catch anchor was caught by this HEAD. It executes zero committed mutants. Only the
|
|
257
|
+
qualification body proves that a matching corruption is killed at its claimed signature.
|
|
256
258
|
- **Any `mcp/entwurf-bridge/src/**` edit owes `pnpm run build-bridge` plus
|
|
257
259
|
`./run.sh check-bridge-delivery` in the same inner loop.** `[측정 2026-09-16]` the same candidate
|
|
258
260
|
reached `check:full` with a stale compiled entry and failed in 22 seconds on "artifact is not
|
|
@@ -398,6 +400,7 @@ The single-turn / multi-turn / cross-process / persistence-boundary / shutdown i
|
|
|
398
400
|
| Lifecycle policy — turn-scoped is always new; process-scoped may reuse only the live in-memory session; persisted records are not a resume/load path today | `check-acp-session-store`, `check-acp-session-reuse` |
|
|
399
401
|
| Tool-call / event mapping | `check-acp-event-mapper`, `smoke-acp-provider-live` |
|
|
400
402
|
| Prompt lifecycle — no wall-clock cutoff on a running turn; abort ends it by ACP `session/cancel` with bounded cleanup; a child death is reported with exit status + stderr on new AND reuse turns; our prompt-phase error text is not classified transient by pi | `check-acp-prompt-lifecycle`, `check-probe-ordering` |
|
|
403
|
+
| Claude typed `incomplete_tool_call` prompt failure — real pi retry/overflow classifiers reject the fixed verdict, raw evidence stays in existing diagnostics, exactly one seal and drop on new/reuse; other failures retain the legacy stderr path, cancel stays separate. A deterministic receipt does not certify the target vendor runtime. No LIVE gate induces this rejection; the raw-turn and reuse smokes certify only their own rows above. | `check-acp-vendor-rejection` (four mutants) |
|
|
401
404
|
| Operator mcpServers / skills reach the live session | `smoke-acp-mcp-live`, `smoke-acp-skill-live`, `check-acp-config` |
|
|
402
405
|
| Overlay isolation + memory containment | `check-acp-overlay`, `smoke-acp-memory-containment-live`, `check-acp-tool-surface` |
|
|
403
406
|
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -48,6 +48,7 @@ Source of truth: `pi-extensions/lib/acp/backend-adapter.ts`.
|
|
|
48
48
|
| `buildSessionMeta` | Build optional `newSession._meta` from the already-loaded carrier. |
|
|
49
49
|
| `enforceModel` | Make the requested native model authoritative before the prompt. |
|
|
50
50
|
| `configSignatureFields` | Return a stable primitive map whose changes invalidate reuse. |
|
|
51
|
+
| `readPromptRejection?` | Name a backend-owned typed prompt rejection and its fixed app-policy verdict; absent means the existing verbatim-first failure path. |
|
|
51
52
|
|
|
52
53
|
`backend.ts` resolves the adapter once at turn entry. Common config never branches on
|
|
53
54
|
backend-specific keys; `adapterSettings` remains opaque until handed back to its owner.
|
|
@@ -85,21 +86,58 @@ turned one long turn into four in 0.13.0. Gates: `check-acp-prompt-lifecycle` (b
|
|
|
85
86
|
with pi's own classifier as the oracle), `check-probe-ordering` (no production prompt
|
|
86
87
|
cutoff in source).
|
|
87
88
|
|
|
89
|
+
### Typed Claude prompt rejection — no automatic whole-prompt replay (#127)
|
|
90
|
+
|
|
91
|
+
Claude ACP 0.85.1 can reject a prompt with the wire SDK's `RequestError`, code `-32603`,
|
|
92
|
+
`data.errorKind === "incomplete_tool_call"`, after marking unfinished foreground tools
|
|
93
|
+
failed. Some tools may already have run. This is an RPC failure, not a successful
|
|
94
|
+
`stopReason`, and a failed tool notification alone is not a turn settlement. The code
|
|
95
|
+
is JSON-RPC's internal error; the kind is Claude's extension vocabulary, not a standard
|
|
96
|
+
retry permission. AIR session-failure metadata remains capability-gated off by
|
|
97
|
+
`clientCapabilities: {}`.
|
|
98
|
+
|
|
99
|
+
Only the Claude adapter declares `readPromptRejection`. The common loop passes the
|
|
100
|
+
once-resolved adapter at the two prompt catches, not at bootstrap failures, and skips
|
|
101
|
+
this reader when the operator's abort wins. A recognized rejection seals once as
|
|
102
|
+
`error/error`, drops the new or reused session under the existing policy, and sets a
|
|
103
|
+
fixed semantic `errorMessage` which pi's real retry **and** overflow classifiers must
|
|
104
|
+
reject. No global retry disable, substring sanitizer, or new failure ledger is added.
|
|
105
|
+
|
|
106
|
+
The first raw failure remains in pi's existing `AssistantMessage.diagnostics`: original
|
|
107
|
+
name/message/stack/code, serialized data, and applicable stderr tail, lifecycle, launch
|
|
108
|
+
signal and overflow hint. None is remixed into the semantic verdict. Actual wire data
|
|
109
|
+
is parsed JSON; a hand-built non-wire value that cannot serialize gets an explicit
|
|
110
|
+
unserializable marker rather than escaping before the stream seal. Assistant content,
|
|
111
|
+
context replay and reuse signatures remain unchanged; persisted message JSON gains
|
|
112
|
+
these diagnostics. Cortex, other error kinds/codes, preprompt failures and aborts keep
|
|
113
|
+
the existing path. No warm reuse after this failure is newly claimed.
|
|
114
|
+
|
|
115
|
+
`check-acp-vendor-rejection` owns the source-adjacent adapter/pipeline assertions and
|
|
116
|
+
four defect mutants. Its real classifier oracles, raw positive controls, failed-tool
|
|
117
|
+
ordering, exact-once seal/drop, raw evidence and separate cooperative-cancel cells do
|
|
118
|
+
not certify a real vendor runtime. Target closure installation, focused receipts,
|
|
119
|
+
qualification, full floor and LIVE compatibility remain separate in the
|
|
120
|
+
[#127 thread](https://github.com/junghan0611/entwurf/issues/127).
|
|
121
|
+
|
|
88
122
|
## Support contract (#81)
|
|
89
123
|
|
|
90
124
|
What "supported" means here, per declaration class. The classes are kept apart on purpose: one
|
|
91
125
|
undifferentiated "supported" column is what let a Claude PASS read as if it also certified Cortex.
|
|
92
126
|
The rows are THIS checkout's declarations, not a shipped release: what a published version carried
|
|
93
127
|
is that version's CHANGELOG entry, and a candidate's own evidence is its issue thread, LIVE receipts
|
|
94
|
-
and exact-SHA CI
|
|
128
|
+
and exact-SHA CI. The pi row shipped in 0.30.0 (#125). The ACP wire SDK, Claude adapter and Claude
|
|
129
|
+
Agent SDK rows are the unpublished #127 candidate: published 0.30.0 still declares
|
|
130
|
+
`claude-agent-acp 0.79.0` and `@agentclientprotocol/sdk 1.4.0`
|
|
131
|
+
(`[측정 2026-10-04]` `npm view @junghanacs/entwurf@0.30.0`; per-bump record in the ROADMAP Dep bump
|
|
132
|
+
ledger, 2026-10-03).
|
|
95
133
|
|
|
96
134
|
| Surface | Declaration | Class | What a green actually says |
|
|
97
135
|
|---|---|---|---|
|
|
98
136
|
| 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 |
|
|
99
|
-
| pi runtime | devDep exact `1.0.0`, peer `>=1.0.0 <1.1` | **exact** oracle + **closed range** | the source pin this checkout builds and gates against; hosts inside the range are accepted, and the ceiling moves only on measurement.
|
|
100
|
-
| ACP wire SDK | `@agentclientprotocol/sdk 1.
|
|
101
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
102
|
-
| Claude Agent SDK | `0.3.
|
|
137
|
+
| pi runtime | devDep exact `1.0.0`, peer `>=1.0.0 <1.1` | **exact** oracle + **closed range** | the source pin this checkout builds and gates against; hosts inside the range are accepted, and the ceiling moves only on measurement. Shipped in 0.30.0 (`[측정 2026-10-04]` npm `latest` `0.30.0` declares this same peer range); its acceptance record is CHANGELOG 0.30.0, with #125 as the branch evidence |
|
|
138
|
+
| ACP wire SDK | `@agentclientprotocol/sdk 1.6.0` | **exact** | the shared wire oracle both adapters speak |
|
|
139
|
+
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.85.1` | **exact**, bundled | checkout candidate; resolved before any PATH fallback; declaration is not compatibility evidence |
|
|
140
|
+
| Claude Agent SDK | `0.3.286` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
103
141
|
| 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) |
|
|
104
142
|
| Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
|
|
105
143
|
| Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
|
|
@@ -181,7 +219,7 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
181
219
|
imported nowhere: `[측정 2026-09-18]` `git grep -c` over `pi-extensions/`, `test/`, `scripts/`,
|
|
182
220
|
`mcp/` is **0**. We never imported the adapter as a library at all — we spawn its binary.
|
|
183
221
|
- **0.77.0's `allowDangerouslySkipPermissions` opt-out is a new lever we deliberately do not
|
|
184
|
-
pull, and our effective permission mode
|
|
222
|
+
pull, and at 0.79.0 our effective permission mode was unchanged.** 0.76.0 sent
|
|
185
223
|
`allowDangerouslySkipPermissions: ALLOW_BYPASS` unconditionally and computed
|
|
186
224
|
`initialPermissionMode = creationOpts.permissionMode ?? resolvePermissionMode(settings…)`.
|
|
187
225
|
0.79.0 computes `allowBypass = ALLOW_BYPASS && sessionMeta?.claudeCode?.options?.allowDangerouslySkipPermissions !== false`
|
|
@@ -192,7 +230,9 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
192
230
|
versions (`dist/permissions/modes.js` 직독). Our overlay pins
|
|
193
231
|
`permissions.defaultMode: "bypassPermissions"` (`overlay.ts:122`), which resolves the same
|
|
194
232
|
under both. The lever now EXISTS for a host that wants a non-bypass sibling; declaring it is a
|
|
195
|
-
separate axis, not a one-line flip, and nothing in this bump takes it.
|
|
233
|
+
separate axis, not a one-line flip, and nothing in this bump takes it. That is the 0.79.0
|
|
234
|
+
measurement; at 0.85.1 project or managed settings can drop bypass (the 0.79.0 → 0.85.1 entry
|
|
235
|
+
below).
|
|
196
236
|
- **0.77.0's system-reminder strip never touches our first-user-message augment.**
|
|
197
237
|
`INJECTED_CONTEXT_MARKERS = ["system-reminder"]` joins the local-command markers in
|
|
198
238
|
`stripMarkerTags`, and `stripLocalCommandMetadata` has exactly two call sites
|
|
@@ -247,6 +287,51 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
247
287
|
is byte-identical (`md5 cfd031d0…` both versions). The `settings`-as-STRING-PATH branch our
|
|
248
288
|
call-site contract names is still on the unconditional `session/new` path
|
|
249
289
|
(`dist/acp-agent.js:6010-6012`).
|
|
290
|
+
- **0.79.0 → 0.85.1 (#127) is a source classification, not a runtime certification.**
|
|
291
|
+
`[측정 2026-10-03, tag v0.85.1 = 686c0c99, src and dist read directly; nothing executed]`.
|
|
292
|
+
Coordinates and evidence classes are in the ROADMAP Dep bump entry for 2026-10-03. The typed
|
|
293
|
+
rejection above is the only failure-policy change Entwurf wrote for this bump. The reachable
|
|
294
|
+
upstream changes below are classified, not new Entwurf contracts:
|
|
295
|
+
- **Permission mode is conditional (#1165, reachable on a host/project basis).** The adapter
|
|
296
|
+
builds its own `SettingsManager(params.cwd, …)` over `claudeConfigDir/settings.json`, the
|
|
297
|
+
cwd's `.claude/settings.json` and `settings.local.json`, and the managed path, then calls
|
|
298
|
+
`resolveSettings({ cwd })` without a `settingSources` argument. That call flow is separate
|
|
299
|
+
from our SDK query's `settingSources: []` (`tool-surface.ts:152`). When those settings carry
|
|
300
|
+
`disableBypassPermissionsMode: "disable"`, `allowBypass` drops, more permission requests reach
|
|
301
|
+
our approve-all-by-kind handler, and `[permission:approved]` notices appear. The SDK's
|
|
302
|
+
default `resolveSettings` source set is UNREAD, and no gate covers this case.
|
|
303
|
+
- **Reachable, classified from source only.**
|
|
304
|
+
- `createSession` starts a background `getContextUsage()` refresh (call at tag `85:8317`), and
|
|
305
|
+
does so again after a model change. Upstream comments (`85:8301`, `85:8350-8352`) describe
|
|
306
|
+
SDK control requests as serialized. The SDK internals are UNREAD, so whether our bounded
|
|
307
|
+
set-model and early-abort grace queue behind it is a lead, and its timing is UNRUN.
|
|
308
|
+
- A turn made only of `<synthetic>` frames emits no `usage_update` (#1132), and our
|
|
309
|
+
keep-prior occupancy seals the prior value.
|
|
310
|
+
- `tool_call_update` omits unchanged fields for every client (#1153). No producer was found
|
|
311
|
+
that sends a pre-terminal `rawOutput.isError`, so a tool failure still arrives as
|
|
312
|
+
`status: "failed"`.
|
|
313
|
+
- #1216's `teardownSession` stops awaiting the interrupt reply. It is reached only
|
|
314
|
+
indirectly, through dispose on stdin EOF or a signal: we never send `session/close`, and
|
|
315
|
+
our `session/cancel` still awaits.
|
|
316
|
+
- A sign-out during an unsettled turn fails it with `RequestError.authRequired()` and no
|
|
317
|
+
extra message (#1179, tag `85:4420`). The stock text is the SDK default
|
|
318
|
+
`Authentication required` (sdk 1.6.0 `jsonrpc.js:1032-1033`), and that message ALONE is not
|
|
319
|
+
transient to pi's real classifier. The sealed legacy `errorMessage` also carries the
|
|
320
|
+
stderr tail, lifecycle and hint (`backend.ts:1463-1490`), and a tail can still match the
|
|
321
|
+
retry pattern. `[측정 2026-10-04, coordinator classifier probe]`: `"Authentication required"`
|
|
322
|
+
→ `retryable: false`; the same text plus `\n--- backend stderr (tail) ---\n500` →
|
|
323
|
+
`retryable: true`. #127 does not sanitize this path and certifies no auth runtime.
|
|
324
|
+
- The nine other vendor `internalError` sites keep the same legacy path. Their
|
|
325
|
+
`Internal error:` prefix can match the classifier, and that pre-existing policy is
|
|
326
|
+
unchanged here. This is a source and classifier reading, not a frequency claim and not a
|
|
327
|
+
complete-callee identity claim.
|
|
328
|
+
- **Capability-gated:** `notice`, the one new `sessionUpdate` literal (17 → 18 kinds).
|
|
329
|
+
- **Ambient only:** `CLAUDE_AGENT_ACP_EXPERIMENTAL_V2=1` selects a v1/v2 router. Entwurf
|
|
330
|
+
neither sets nor scrubs it, and the router's v1 path for a v1 client is UNRUN.
|
|
331
|
+
- **Entry identity is not settlement identity.** `newSession`, `prompt`,
|
|
332
|
+
`setSessionConfigOption`, `requestPermission`, `closeSession`, `sessionUsage`,
|
|
333
|
+
`turnQuotaMeta` and `quotaTokenCount` are byte-identical as extracted entry bodies. Turn
|
|
334
|
+
settlement moved inside the changed `runConsumer`.
|
|
250
335
|
|
|
251
336
|
- **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
|
|
252
337
|
synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
|
|
@@ -373,10 +458,15 @@ caller-session `_meta`, and cross-machine certification.
|
|
|
373
458
|
|
|
374
459
|
A backend can return `newSession` before its declared MCP server is callable. This was
|
|
375
460
|
observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
|
|
376
|
-
path.
|
|
377
|
-
readiness fence over a session's declared MCP servers
|
|
378
|
-
|
|
379
|
-
|
|
461
|
+
path. Entwurf's common loop calls `mcpServerStatus()` nowhere; it does not add a
|
|
462
|
+
client-side readiness fence over a session's declared MCP servers. The historical
|
|
463
|
+
0.76.0 → 0.79.0 source measurement below is dated evidence, not a current-version
|
|
464
|
+
runtime claim. For the 0.85.1 candidate, #127 re-measured the MCP-authentication
|
|
465
|
+
window as byte-identical and ran the MCP/self/send LIVE samples; neither source
|
|
466
|
+
identity nor green samples establish the cause of the intermittent readiness gap
|
|
467
|
+
or close #72. Changed SDK internals and session initialization remain separate
|
|
468
|
+
risk surfaces.
|
|
469
|
+
(Historical measurement at the 0.76.0 → 0.79.0 bump, not inherited — the previous bump's argument is
|
|
380
470
|
not reused, the way the 0.75.1 → 0.76.0 entry did not reuse 0.73.0 → 0.75.1's.
|
|
381
471
|
`mcpServerStatus` call sites in `src/acp-agent.ts` are **2 at v0.76.0 and 2 at v0.79.0**
|
|
382
472
|
`[측정 2026-09-18, upstream v0.79.0/src/acp-agent.ts read directly, grep -n]`; they first
|
|
@@ -392,7 +482,8 @@ empty) and the region moved +11 while the file shrank 10,405 → 10,329 lines. T
|
|
|
392
482
|
agent-picker removal, the 0.78.0 compaction/checkpoint/AIR work and the 0.79.0 permission
|
|
393
483
|
presentation touch no part of this path, so the other reachable-surface findings stand as
|
|
394
484
|
re-measured in the capability-posture section above.
|
|
395
|
-
|
|
485
|
+
That historical source measurement closes no part of #72; it is not a claim that
|
|
486
|
+
all effective readiness behavior stayed unchanged in the 0.85.1 runtime.)
|
|
396
487
|
|
|
397
488
|
### 11-7-a/b. Instrument and first measurement
|
|
398
489
|
|
|
@@ -500,12 +591,14 @@ nothing here is promised and then quietly not delivered. `[QK:PI-QUEUE-NO-MIDFLI
|
|
|
500
591
|
while a turn is in flight never joins that turn and arrives as a later one. The delay's SIZE is
|
|
501
592
|
that turn's duration, which the same cell fixes by construction.
|
|
502
593
|
|
|
503
|
-
**Forwarding is not adopted here.** `[측정 2026-09-22]`
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
`_session/steering` in
|
|
507
|
-
(`dist/acp-agent.js:163`, handler at `:7792`),
|
|
508
|
-
`InitializeResponse._meta.steering.supported`.
|
|
594
|
+
**Forwarding is not adopted here.** The dated `[측정 2026-09-22]` SDK-method enumeration
|
|
595
|
+
and private-extension observation below belong to the then-installed oracle, not a
|
|
596
|
+
0.85.1 runtime measurement: `AGENT_METHODS` and `CLIENT_METHODS` contained zero entries
|
|
597
|
+
matching `steer`; `_session/steering` was a PRIVATE vendor extension in
|
|
598
|
+
`@agentclientprotocol/claude-agent-acp@0.79.0` (`dist/acp-agent.js:163`, handler at `:7792`),
|
|
599
|
+
client→agent, advertised through `InitializeResponse._meta.steering.supported`.
|
|
600
|
+
The current boundary is our client's lack of a steering path, not a claim that the
|
|
601
|
+
old vendor coordinates describe the current pin. Our client sends `initialize`, `session/new`,
|
|
509
602
|
`session/prompt`, `session/set_config_option` and `session/cancel` (`acp-client.ts:138-149`) and
|
|
510
603
|
has no steering path at all.
|
|
511
604
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { createRequire } from "node:module";
|
|
21
21
|
import { homedir } from "node:os";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { RequestError } from "@agentclientprotocol/sdk";
|
|
23
24
|
import { enrichMcpServersWithEnvelope } from "./config.js";
|
|
24
25
|
import { loadEngraving } from "./engraving.js";
|
|
25
26
|
import { CORTEX_MODEL_PREFIX, curatedClaudeModels, curatedCortexModels, SUPPORTED_ANTHROPIC_MODEL_IDS, SUPPORTED_CORTEX_MODEL_IDS, } from "./models.js";
|
|
@@ -34,10 +35,46 @@ import { buildClaudeSessionMeta } from "./tool-surface.js";
|
|
|
34
35
|
function shellQuote(value) {
|
|
35
36
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
36
37
|
}
|
|
38
|
+
/** The pi diagnostic type under which backend.ts preserves a recognised rejection verbatim. */
|
|
39
|
+
export const ACP_PROMPT_REJECTION_DIAGNOSTIC = "entwurf_acp_prompt_rejection";
|
|
40
|
+
/** The verdict for claude-agent-acp's `incomplete_tool_call` (#127; upstream #1212, 0.85.0+). */
|
|
41
|
+
export const INCOMPLETE_TOOL_CALL_VERDICT = "ACP turn failed because foreground tool results are incomplete. Some tools may already have run; automatic replay is unsafe. Inspect the preserved vendor rejection before continuing.";
|
|
37
42
|
// ---------------------------------------------------------------------------
|
|
38
43
|
// claude adapter — the first implementation (the rail's reference backend)
|
|
39
44
|
// ---------------------------------------------------------------------------
|
|
40
45
|
const SUPPORTED_CLAUDE_IDS = new Set(SUPPORTED_ANTHROPIC_MODEL_IDS);
|
|
46
|
+
/** JSON-RPC internal error — the code claude-agent-acp rejects an unfinished-tool turn with. */
|
|
47
|
+
const JSONRPC_INTERNAL_ERROR = -32603;
|
|
48
|
+
/** claude-agent-acp's own kind for a turn that hit `end_turn` with foreground tools unanswered
|
|
49
|
+
* (read at v0.85.1 `src/acp-agent.ts:4286-4340`, `errorKindData("incomplete_tool_call")`). */
|
|
50
|
+
const CLAUDE_INCOMPLETE_TOOL_CALL = "incomplete_tool_call";
|
|
51
|
+
/**
|
|
52
|
+
* The claude reader for `readPromptRejection` (#127). Only the root wire SDK's own decode
|
|
53
|
+
* counts: its client rejects a JSON-RPC error response with `new RequestError(code, message,
|
|
54
|
+
* data)`, `data` being the parsed `error.data` (sdk 1.4.0 `dist/jsonrpc.js:842-844`). A
|
|
55
|
+
* look-alike object, another code, or another `errorKind` is not this failure and keeps the
|
|
56
|
+
* verbatim-first path.
|
|
57
|
+
*/
|
|
58
|
+
function readClaudePromptRejection(err) {
|
|
59
|
+
if (!(err instanceof RequestError) || err.code !== JSONRPC_INTERNAL_ERROR)
|
|
60
|
+
return undefined;
|
|
61
|
+
const data = err.data;
|
|
62
|
+
if (typeof data !== "object" || data === null || !("errorKind" in data))
|
|
63
|
+
return undefined;
|
|
64
|
+
if (data.errorKind !== CLAUDE_INCOMPLETE_TOOL_CALL)
|
|
65
|
+
return undefined;
|
|
66
|
+
return { errorKind: CLAUDE_INCOMPLETE_TOOL_CALL, verdict: INCOMPLETE_TOOL_CALL_VERDICT, dataJson: jsonText(data) };
|
|
67
|
+
}
|
|
68
|
+
/** Evidence text for a wire value. A value that came off the wire serializes; one that cannot
|
|
69
|
+
* (a hand-built fake) is named as such, so recording evidence never breaks the turn's seal. */
|
|
70
|
+
function jsonText(value) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.stringify(value) ?? String(value);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return `[unserializable: ${err instanceof Error ? err.name : typeof err}]`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
41
78
|
/**
|
|
42
79
|
* Resolve the claude launch — an ENTWURF-OWNED launcher, or the env override for debug.
|
|
43
80
|
* This is the single source for the claude launch spec; backend.ts holds no private copy.
|
|
@@ -139,6 +176,9 @@ export const claudeAdapter = {
|
|
|
139
176
|
// The token partition is NOT declared here: ACP's only token carrier is a
|
|
140
177
|
// per-turn round-trip aggregate, which is not what pi's four fields mean.
|
|
141
178
|
sealsTurnAccounting: true,
|
|
179
|
+
// #127: claude-agent-acp 0.85.0+ rejects an `end_turn` with unanswered foreground tools as
|
|
180
|
+
// -32603 `incomplete_tool_call` (upstream #1212). Its raw text reads as transient to pi.
|
|
181
|
+
readPromptRejection: readClaudePromptRejection,
|
|
142
182
|
};
|
|
143
183
|
// ---------------------------------------------------------------------------
|
|
144
184
|
// Registry — modelId → adapter
|
|
@@ -285,6 +325,9 @@ export const cortexAdapter = {
|
|
|
285
325
|
// backend.ts seals nothing for a backend without this flag, so cortex's
|
|
286
326
|
// emitted usage is byte-identical to what it was before #93. Declaring this
|
|
287
327
|
// with a guess would mint exactly the silent misaccounting #93 exists to end.
|
|
328
|
+
// NO readPromptRejection — also deliberate. Nobody has read which failures cortex's ACP
|
|
329
|
+
// server rejects a prompt with, so every cortex failure keeps the verbatim-first
|
|
330
|
+
// errorMessage. Borrowing claude's `incomplete_tool_call` reader would be a guess.
|
|
288
331
|
};
|
|
289
332
|
const ADAPTERS = [claudeAdapter, cortexAdapter];
|
|
290
333
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, Antigravity, and Codex CLI; Claude and Snowflake Cortex Code ACP backends.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
"claudeCodeFloor": ">=2.1.217"
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
|
-
"@agentclientprotocol/claude-agent-acp": "0.
|
|
88
|
-
"@agentclientprotocol/sdk": "1.
|
|
87
|
+
"@agentclientprotocol/claude-agent-acp": "0.85.1",
|
|
88
|
+
"@agentclientprotocol/sdk": "1.6.0",
|
|
89
89
|
"@anthropic-ai/sdk": "0.100.1",
|
|
90
90
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
91
91
|
"smol-toml": "^1.8.0",
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"check:core": "pnpm run check:toolchain && pnpm run check:vitest && pnpm run check:contracts",
|
|
178
178
|
"check:toolchain": "pnpm lint && pnpm typecheck",
|
|
179
179
|
"check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior",
|
|
180
|
-
"check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push",
|
|
180
|
+
"check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-acp-vendor-rejection && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push",
|
|
181
181
|
"check:hermetic": "./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-hook-launch-topology && ./run.sh check-meta-hook-session-switch && ./run.sh check-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-launch && ./run.sh check-pi-launch && ./run.sh check-codex-app-server-launch && ./run.sh check-meta-identity-consumers && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh check-meta-doctor-oracle && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-copilot-statusline-state && ./run.sh smoke-copilot-mcp-state && ./run.sh smoke-omp-bridge-state && ./run.sh smoke-omp-mcp-state && ./run.sh smoke-omp-receive-state && ./run.sh check-omp-birth-hook && ./run.sh check-omp-receive-arm && ./run.sh check-omp-fresh-preflight && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-bridge-boot && ./run.sh check-probe-bridge-command && ./run.sh check-meta-facts && ./run.sh check-peer-facts && ./run.sh check-herdr-plugin && ./run.sh check-herdr-plugin-profile && ./run.sh check-herdr-plugin-build && ./run.sh check-herdr-runtime-bootstrap && ./run.sh check-herdr-activation && ./run.sh check-agy-sender-identity && ./run.sh check-acp-provider-surface && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-usage-accounting && ./run.sh check-acp-launch-namespace && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-typing-call-fence && ./run.sh check-gate-manifests && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex && ./run.sh check-codex-bridge-identity && ./run.sh check-pi-bridge-sender && ./run.sh check-pi-mcp-register && ./run.sh check-pi-mcp-bridge && ./run.sh check-codex-birth-hook && ./run.sh smoke-codex-config-state && ./run.sh smoke-codex-birth && ./run.sh check-herdr-sandbox",
|
|
182
182
|
"check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh smoke-setup-verdict && ./run.sh check-pack-pin-matcher && ./run.sh check-pack-consumer-roots && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
|
|
183
183
|
},
|