@junghanacs/entwurf 0.23.0 → 0.23.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 +3 -1
- package/BASELINE.md +1 -0
- package/CHANGELOG.md +134 -0
- package/VERIFY.md +41 -0
- package/docs/acp-backend-rail.md +96 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +2 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +1 -1
- package/package.json +8 -7
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +4 -4
- package/pi-extensions/lib/acp/event-mapper.ts +5 -5
- package/pi-extensions/lib/acp/tool-surface.ts +2 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-lock.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +1 -1
- package/run.sh +192 -196
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-gate-qualification.ts +1 -0
- package/scripts/check-pi-launch.ts +358 -0
- package/scripts/check-release-gate-outcomes.ts +7 -0
- package/scripts/inventory-verification-surface.ts +86 -10
- package/scripts/mutants/herdr-plugin-build.json +1 -1
- package/scripts/mutants/pi-launch.json +113 -0
- package/scripts/mutants/resume-args.json +6 -6
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +17 -17
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-entwurf-v2-matrix-live.ts +1 -1
- package/scripts/smoke-herdr-raw-install-live.sh +849 -0
- package/scripts/tsconfig.json +5 -0
- package/scripts/check-entwurf-facts.ts +0 -391
- package/scripts/check-entwurf-resume-args.ts +0 -122
- package/scripts/check-entwurf-v2-decider.ts +0 -657
- package/scripts/check-entwurf-v2-lock.ts +0 -371
- package/scripts/check-entwurf-v2-mailbox.ts +0 -261
- package/scripts/check-entwurf-v2-matrix.ts +0 -401
- package/scripts/check-entwurf-v2-native-push.ts +0 -221
- package/scripts/check-entwurf-v2-release.ts +0 -151
- package/scripts/check-entwurf-v2-runner.ts +0 -448
- package/scripts/check-entwurf-v2-send-fallback.ts +0 -350
- package/scripts/check-entwurf-v2-send.ts +0 -458
- package/scripts/check-entwurf-v2-visible-resume.ts +0 -445
- package/scripts/check-socket-discovery.ts +0 -401
package/AGENTS.md
CHANGED
|
@@ -82,6 +82,8 @@ Keep at most ten open issues, including at most five implementation issues. Clas
|
|
|
82
82
|
|
|
83
83
|
Two independent axes are required: deterministic/package gates and opt-in LIVE evidence. Exact commands, evidence levels, and release acceptance are owned by [VERIFY.md](./VERIFY.md); recorded host evidence by [BASELINE.md](./BASELINE.md). Run LIVE gates from scratch with ambient identity carriers stripped so artifacts and callbacks cannot bleed from the operator session.
|
|
84
84
|
|
|
85
|
+
A new contract's test goes beside the behavior it certifies — `pi-extensions/**/*.test.ts` or `plugins/herdr/**/*.test.mjs` — and `check-tests-beside-behavior` discovers it by glob, so landing one edits no filename list. `scripts/check-*` stays the home of LIVE, install, spawning and mixed gates; a new hand-built gate there needs a reason the vitest lane could not carry it.
|
|
86
|
+
|
|
85
87
|
### Scheduling
|
|
86
88
|
|
|
87
89
|
```text
|
|
@@ -97,7 +99,7 @@ implement → affected focused gates → independent review → one amendment bu
|
|
|
97
99
|
|
|
98
100
|
## Type and Working Boundaries
|
|
99
101
|
|
|
100
|
-
- Each `.ts` file belongs to one declared typecheck fence;
|
|
102
|
+
- Each `.ts` file belongs to one declared typecheck fence; `exclude` may move a file between fences but never out of all of them, so an exclude states the receiving `include` in the same change. Root pi extensions use TypeBox through `@earendil-works/pi-ai`; MCP/scripts use explicit `.ts` imports required by Node strip-types. Installed surfaces route to compiled JS. Behavior-adjacent `.test.ts` takes the fence `test/` takes — `scripts/tsconfig.json`, never the emit-capable root program — and is kept out of the tarball by a files-array negation whose tripwire is `check-pack`.
|
|
101
103
|
- Make surgical, one-contract changes. Removal repairs source and its proof together; a green gate never proves retired behavior.
|
|
102
104
|
- Before commit, sweep repository-wide for retired authority vocabulary and landed-plan future tense. Historical tombstones may remain; live docs, source, gates, and usage must agree.
|
|
103
105
|
- Prefer capability-domain language over identity rank. Use tabs unless the project formatter requires otherwise. GLG decides commit, push, and release gates.
|
package/BASELINE.md
CHANGED
|
@@ -34,6 +34,7 @@ not **UNSUPPORTED** — the deliberate never, which today is native Windows only
|
|
|
34
34
|
| Maintainer NixOS installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 registry install → doctor exit 0 (HISTORY) |
|
|
35
35
|
| Secondary Ubuntu installed package | **certified** for `0.12.8-repair.1` | 2026-07-25 same artifact, isolated agent dir → doctor exit 0 (HISTORY) |
|
|
36
36
|
| macOS Claude meta-bridge | NOT CERTIFIED — pending physical host | No physical-Mac doctor yet; a CI runner has no Claude login. Physical-host rental remains unscheduled; no cut has carried it. |
|
|
37
|
+
| 0.23.1 release acceptance (observer-amended) | accepted by GLG | `pnpm run check:full` P4 exit 0 (577 s); second aggregate `release-gate --cut` at `/tmp/entwurf-release-gate-0.23.1-retry.UIFctR` reached MUST 23/1/0 and BEHAVIOR 1/0, with only `smoke-codex-fresh-live` red because luna dropped `$`/`@` sigils in its relay (`release-gate.log:6156-6157`). The observer-amended standalone cell reran that exact leg with Codex `gpt-5.6-terra`, Pi `openai-codex/gpt-5.6-luna`: 66 assertions, exit 0; A app-server PID `3969303` `$227` ≠ fixture S `$231`; artifact `.probe-artifacts/codex-fresh-live-eZmf6p/`; pipe-pane stdout sha256 `56ee73e046e16fa95ef093fccf037670bf28b53a054bb5eec2a3b3b3ff08d93a`. Luna sigil handling is a post-cut issue, not a production-byte change. |
|
|
37
38
|
| Native Codex Linux caller-DIRECTORY axis (current; #95 lane C) | **certified** | 2026-09-16, Codex 0.153.4: 65-assertion LIVE, exit 0, at `b3e07dc` with a clean worktree. Same host and topology as the lane B row below (app-server `737636` in A `$30/@41/%45`; initial Pi `20260916T154902-556eb5` `$2/@58`, Codex `20260916T155037-10ddaa` `$2/@59`, Codex-opened Pi `20260916T155102-9add8a` `$2/@61`, `seat-source=codex-title-anchor`). **hop 1 (cwd requested):** pane `#{pane_current_path}` = rollout `session_meta.cwd` = record cwd = requested scratch = `/tmp/entwurf-codex-fresh-live-db65N2`, against app-server cwd `/home/junghan/repos/gh/entwurf` read from `/proc/737636/cwd`. **hop 2 (cwd NOT requested):** the outbound Pi's pane, its own birth-written record and the Codex caller's record all read that same scratch, and its receipt names the caller-record rule. Artifact `.probe-artifacts/codex-fresh-live-w4yJBw/`; stdout `.probe-artifacts/lane-c-live-20260916T154900.log` sha256 `712050e7a8cf03ece98e7f34029ae98a92e9b2a7fd96aff87d0c1e6ba2ee9af2`. Cleanup reclaimed `@58`/`@59`/`@61` with no failure; records and transcripts preserved; app-server and `$30` untouched. 65 and 56 are different contracts — lane C added the cwd axis on top of lane B. Long floors (qualification body, frozen `check:full`) NOT run at this SHA; GLG decides host vs CI. |
|
|
38
39
|
| Native Codex Linux caller-seat topology (current; #95 lane B) | **certified** | 2026-09-16, Codex 0.153.4: 56-assertion LIVE, exit 0. App-server `737636` alone in session A `$30/@41/%45`; initial Pi `20260916T144205-e876ff` `$2/@46`, omitted-placement Codex `20260916T144230-d050d7` `$2/@47`, Codex-opened Pi `$2/@48`. A ≠ S is the decisive half: the app-server env names A, so S can only have come from the caller's pane title — receipt `seat-source=codex-title-anchor`. Artifact `.probe-artifacts/codex-fresh-live-nYcGC1/run-stdout.log` sha256 `ed60c2bd…`; the 43-assertion hop-1 run that measured the D1 retirement is kept at `codex-fresh-live-4aFCDD/run-stdout.log` sha256 `8392a603…`. Cleanup reclaimed `@46`/`@47`/`@48` with no failure; app-server and `$30` untouched. |
|
|
39
40
|
| Native Codex Linux home topology (PAST CONTRACT — #95 D1 retired the fixed home 2026-09-16) | **certified** for 0.21.0 as shipped | 2026-09-12, Codex 0.153.4: 57-assertion explicit-home LIVE. Initial Pi `$150/@397`; operator app-server `$158/@390/%390`; omitted-placement Codex `$158/@398`; Codex-opened Pi `$158/@399`; exact callbacks and addressed delivery both ways. Receipt and digest: `DELIVERY.md`. Deterministic closure of that axis: qualification 460/460 across 43 lanes with origin/snapshot purity green, then frozen full floor exit 0 in 506s — both pre-amendment. **Observer-amended acceptance, 2026-09-12:** standalone LIVE 48 assertions exit 0, source audit `initial-pi=3/3`/`codex=3/3` completed exact, initial Pi `$150/@431`, Codex `$158/@432`, Codex-opened Pi `$158/@433`, all three reclaimed by their own source receipts; artifact `.probe-artifacts/codex-fresh-live-fZccoK/`. The 48 and 57 counts are different contracts, not a regression. **Release acceptance at that same fingerprint:** `check:full` exit 0 502s, LIVE release gate `--cut` MUST 24/0/0 · BEHAVIOR 1/0/0 · `cut: OK`, `check-gate-qualification` 475/475 across 43 lanes with origin purity green, and the aggregate's Codex leg repeating the 48-assertion acceptance (artifact `codex-fresh-live-2oId4C`). Unrestricted attached-TUI placement remains unsupported and unclaimed. |
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,140 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.23.1 - 2026-09-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **The plugin runtime lock names `herdr-checkout` again, for the length of the 0.23.1 candidate
|
|
12
|
+
window.** This is a state, not a direction: an `npm` lock must name a version that is ALREADY
|
|
13
|
+
published and coherent with this checkout's `package.json`
|
|
14
|
+
(`certifyLockCoherence`, asserted on the committed lock by `check-herdr-runtime-bootstrap` 8a,
|
|
15
|
+
which sits inside `check:hermetic`), and the version bump necessarily lands before the publish.
|
|
16
|
+
So a cut cannot ride an npm lock naming the version it is about to create. `dd84ac0` is the
|
|
17
|
+
precedent and the exit: 0.23.0 was tagged on the checkout carrier and pinned to npm afterwards,
|
|
18
|
+
and 0.23.1 follows the same two steps. While this window is open, `smoke-herdr-raw-install-live`
|
|
19
|
+
reports the npm acquisition axis as a NAMED SKIP rather than a failure, and still asserts that
|
|
20
|
+
the runtime came from the source this ref locks.
|
|
21
|
+
|
|
22
|
+
**A pin and its inverse move the lock AND `check-herdr-plugin-build` together**, which is a fact
|
|
23
|
+
worth writing down because it has now been rediscovered twice. That gate reads the COMMITTED lock
|
|
24
|
+
in three places — the two-stage journey expects one acquisition and the locked `kind@version`, the
|
|
25
|
+
activation-authority cell seeds its ledger from the committed kind, and the progress cell pins the
|
|
26
|
+
long-step wording — so `dd84ac0` changed 90 lines of it alongside the eight lines of lock, and
|
|
27
|
+
this commit is the measured inverse of both halves (14/14 assertions, exit 0). No mutant is
|
|
28
|
+
disturbed: every claim in `scripts/mutants/herdr-plugin-build.json` names a production file
|
|
29
|
+
(`herdr-plugin.toml`, `lib/build.mjs`, `lib/build-progress.mjs`), none names the gate, and the
|
|
30
|
+
`HPB-PROGRESS-NAMED-SEQUENCE` needle re-aimed by `37b81e7` still occurs exactly once in
|
|
31
|
+
`lib/build.mjs`, which this commit does not touch. Teaching the gate to read either lock kind,
|
|
32
|
+
so neither direction needs it flipped again, is deferred to its own issue after the cut.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **A test written beside the behaviour it certifies is run by public `pnpm check`, and nobody
|
|
37
|
+
names the file** (#119 V2). `vitest.config.ts` include grows two globs —
|
|
38
|
+
`pi-extensions/**/*.test.ts` and `plugins/herdr/**/*.test.mjs` — and `check-tests-beside-behavior`
|
|
39
|
+
expands them every run and hands the resulting paths to `run_vitest`. Both halves were measured
|
|
40
|
+
the hard way. The first cut passed `pi-extensions plugins/herdr` as vitest POSITIONALS, which are
|
|
41
|
+
path substrings, so `test/pi-extensions-probe.test.ts` was selected too (measured: it ran);
|
|
42
|
+
expanding first keeps the selected set exactly the two globs while the shim still contains no
|
|
43
|
+
filename. And an empty expansion RETURNS rather than calling vitest with no arguments, because a
|
|
44
|
+
vitest run with no filter runs the whole include. The literal cross-check against
|
|
45
|
+
`vitest.config.ts` stays, because the shell cannot read vitest's resolved config and a deleted
|
|
46
|
+
glob would otherwise pass. Proven by three still photos in one public `pnpm check`: two failing
|
|
47
|
+
beside-behaviour tests turn it red (`selected 2`), and a third file planted under `test/` is not
|
|
48
|
+
selected.
|
|
49
|
+
- **`entwurf pi`** (#118 hop 2) — `exec pi --entwurf-control "$@"`, a dispatcher case with no new
|
|
50
|
+
bin. Two things it deliberately does NOT do, both measured on pi 0.85.1: there is no
|
|
51
|
+
precondition check, because an unregistered host already gets `Error: Unknown option:
|
|
52
|
+
--entwurf-control` and exit 1 from pi itself; and the flag is added, never deduplicated, because
|
|
53
|
+
passing it twice was measured byte-identical to passing it once. One recursion fence, not
|
|
54
|
+
copilot's two — the sentinel closes the real loop, and the case copilot's second fence covers is
|
|
55
|
+
measured here to be a fail-closed unknown verb rather than a loop. `check-pi-launch` reuses
|
|
56
|
+
`check-copilot-launch`'s fake-vendor posture and carries the `--entwurf-control` literal
|
|
57
|
+
cross-check, since the shell cannot import `ENTWURF_CONTROL_FLAG`. Nine mutants, each killed by
|
|
58
|
+
exactly its own claim; exit-status passthrough is asserted without one, because `set -euo
|
|
59
|
+
pipefail` couples it to `exec` and a mutant that kills two claims attributes to neither.
|
|
60
|
+
- **`smoke-herdr-raw-install-live`** (#118 hop 1) — the plugin's first user path, on a clean
|
|
61
|
+
`node:24` container with no host socket/config/cache mounted, no git `insteadOf`, and the public
|
|
62
|
+
remote spelled as a user would type it. It closes the one npm re-proof axis VERIFY.md demanded
|
|
63
|
+
and the production lock had left empty since `dd84ac0`. Receipts: **(a)** `--ref main` →
|
|
64
|
+
`37b81e725cde4d0a548f1c0faab4fcb5c62942b2`, **(b)** `--ref set/119-verify-herdr` →
|
|
65
|
+
`11ec0c38af36d5cd4a59614c3c8e07c5b5b0f9ae`, both `kind=npm @junghanacs/entwurf@0.23.0` with the
|
|
66
|
+
ledger widening `["pi"]` → `["pi","claude-code"]` and the pi wiring byte-identical across the
|
|
67
|
+
reinstall. Three facts it measured rather than assumed: herdr's offline-persist path carries
|
|
68
|
+
0.9.1, so `plugin install --yes` completes with no server; `herdr integration install` refuses a
|
|
69
|
+
harness that has never run, because it creates `~/.pi/agent/extensions` only when the parent
|
|
70
|
+
exists and refuses Claude without `~/.claude`; and `--ref` takes a REMOTE REF, not an arbitrary
|
|
71
|
+
commit (`--ref <sha>` fails as `couldn't find remote ref`).
|
|
72
|
+
|
|
73
|
+
### Changed
|
|
74
|
+
|
|
75
|
+
- **`plugins/herdr` 0.3.0 — the Install path, rewritten as the nine steps a raw box needs.** The
|
|
76
|
+
eighth and ninth are the ones nobody guesses and they come from the container measurement above:
|
|
77
|
+
each harness must be started once (`pi --help`, `claude mcp list`) before herdr will integrate
|
|
78
|
+
it. The release says what it does NOT give you, because the honest combination is this plugin
|
|
79
|
+
plus the runtime its lock still names: `entwurf pi` exists in the repository and is not in
|
|
80
|
+
`@junghanacs/entwurf@0.23.0`, so the user command stays `pi --entwurf-control` until the next
|
|
81
|
+
cut carries the launcher. **`runtime-lock.json` is untouched on purpose** — a pin can only move
|
|
82
|
+
after the version it names is published, so the 0.23.1 pin is a follow-up commit after that
|
|
83
|
+
release, the same two-step `dd84ac0` took for 0.23.0.
|
|
84
|
+
- **Thirteen v2-spine gates moved out of `scripts/` and beside the modules they certify** (#119
|
|
85
|
+
V3), assertion for assertion, with the inventory's denominator fixed FIRST so the move reads as
|
|
86
|
+
a move. `inventory-verification-surface.ts` now counts both vitest locations; without that, the
|
|
87
|
+
migration would have subtracted 2,799 lines from the one number that exists to make migration
|
|
88
|
+
honest. What actually left is 133 lines of plumbing — per-file `ok()` scaffolding, `main()`
|
|
89
|
+
wrappers, summary trailers — and the combined file count is unchanged, seven out and seven in
|
|
90
|
+
per slice.
|
|
91
|
+
- The six mutant-carrying lanes needed four rules the mutant-free seven did not, each learned by
|
|
92
|
+
getting it wrong: a QK claim must BE an `it` title (attribution reads the failed test title);
|
|
93
|
+
the token must appear exactly once per file (title keeps the token, assertion keeps the
|
|
94
|
+
sentence); the `run.sh` case survives as a mutant EXECUTION COORDINATE and keeps its
|
|
95
|
+
`check:contracts` entry (branch CI caught the removal — a gate a mutant names must run inside
|
|
96
|
+
`check:full`); and attribution is containment, not exclusivity (vitest runs every test, so one
|
|
97
|
+
mutation surfaces 1–15 failed titles). 25/25 mutants verified killed and attributed by hand;
|
|
98
|
+
733 mutants across 58 lanes unchanged, `signatureSource` moved, gate argv did not.
|
|
99
|
+
- `entwurf-v2-visible-resume.test.ts` grew 445 → 908 lines and the growth is the design:
|
|
100
|
+
seventeen claims that shared four drives of the subject now each replay their own, because
|
|
101
|
+
nearly every one is an "X happened BEFORE Y" reading a call-order trace, and a trace polluted
|
|
102
|
+
by a neighbouring test answers wrongly while staying green.
|
|
103
|
+
- Three gates stay under `scripts/` with a reason: `check-entwurf-v2-contract` and
|
|
104
|
+
`check-entwurf-v2-production` are cross-lane (`mux-boundary.json` and `codex-native.json` call
|
|
105
|
+
them as gate argv), and `check-entwurf-v2-surface` generates its seven QK labels inside shared
|
|
106
|
+
helpers called twice from `await` blocks, so the established method would either lose
|
|
107
|
+
attribution or require an async `describe`.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
- **The bundled Claude ACP adapter is `@agentclientprotocol/claude-agent-acp` 0.79.0** (from
|
|
111
|
+
0.76.0, crossing 0.77.0/0.78.0/0.79.0; `@anthropic-ai/claude-agent-sdk` 0.3.257 → 0.3.274,
|
|
112
|
+
ACP wire SDK 1.4.0 and `@anthropic-ai/sdk` 0.100.1 unchanged). Three upstream releases, three
|
|
113
|
+
changes with a reachable shape, and **no entwurf code change** — each "inert" is a fresh
|
|
114
|
+
measurement, not the previous bump's argument reused:
|
|
115
|
+
- 0.77.0's BREAKING removal of `claudeCode.options.agent` misses us twice: our
|
|
116
|
+
`buildClaudeSessionMeta` never set that key, and we spawn the adapter's binary rather than
|
|
117
|
+
importing the removed agent-picker exports (repo-wide grep: 0 hits).
|
|
118
|
+
- 0.77.0's `allowDangerouslySkipPermissions` host opt-out is a NEW lever we do not pull. We set
|
|
119
|
+
the option nowhere, `ALLOW_BYPASS` is byte-identical across both versions, and the overlay's
|
|
120
|
+
`permissions.defaultMode: "bypassPermissions"` resolves the same under each, so a sibling's
|
|
121
|
+
effective permission mode is unchanged.
|
|
122
|
+
- 0.78.0's `compaction_update` / `compaction_summary_chunk` are the first new `sessionUpdate`
|
|
123
|
+
kinds since the pin moved, and they are gated off by our `clientCapabilities: {}` — with the
|
|
124
|
+
mapper's forward-compatible `default` arm as an independent second reason. The `stopReason`
|
|
125
|
+
axis, where "unknown is an error" actually applies, is unchanged.
|
|
126
|
+
- 0.79.0's shell-command permission prompts reorder the option array reject-first under the new
|
|
127
|
+
`defaultToNo` hint. Our approve-all policy selects by option KIND, not position, and every
|
|
128
|
+
upstream option builder emits an allow option, so the order-sensitive fallback is unreachable.
|
|
129
|
+
Re-measured and unchanged: the #96 `mcpServerStatus` evidence (2 call sites, now at
|
|
130
|
+
`v0.79.0 src/acp-agent.ts:1773`/`:1866`, surrounding 200-line window byte-identical) and all four
|
|
131
|
+
model-forcing/accounting wire calls (`setSessionConfigOption`, `sessionUsage`, `turnQuotaMeta`,
|
|
132
|
+
`resolveModelPreference` — byte-identical). Full per-change classification with evidence state:
|
|
133
|
+
[ROADMAP.md](./ROADMAP.md) 2026-09-18 bump entry and
|
|
134
|
+
[docs/acp-backend-rail.md](./docs/acp-backend-rail.md) capability posture.
|
|
135
|
+
|
|
136
|
+
- **The Herdr plugin is 0.2.0 and pins its runtime to npm `@junghanacs/entwurf@0.23.0`.** Its
|
|
137
|
+
committed lock carries the published sha512
|
|
138
|
+
`sha512-ZR2VCui7JjK3w56rQSDs3AuAJMMuiXCNWH7HB52SQ3E/7p0oPhcxD+fb6Gdzi0VcBnheqxPzvJHMPQQcdYtNiw==`;
|
|
139
|
+
the 0.1.0 `herdr-checkout` source remains the candidate-verification path.
|
|
140
|
+
|
|
7
141
|
## 0.23.0 - 2026-09-18
|
|
8
142
|
|
|
9
143
|
### Added
|
package/VERIFY.md
CHANGED
|
@@ -44,6 +44,45 @@ When you write a new entry, mark its rung. "L1 only" is honest; "L2 reached" is
|
|
|
44
44
|
|
|
45
45
|
Verification here is not a benchmark. In production we exchange short turns and stop immediately to isolate a cause before resuming when something looks off. This document records **verification intent (what we look at) and pass criteria (how to judge)**; the execution shape is the agent's choice as long as the criteria are met.
|
|
46
46
|
|
|
47
|
+
### Where a verification lives — two locations, and what decides
|
|
48
|
+
|
|
49
|
+
A new contract's test goes BESIDE the behaviour it certifies: `pi-extensions/**/*.test.ts` or
|
|
50
|
+
`plugins/herdr/**/*.test.mjs`. Nothing names those files. `check-tests-beside-behavior` expands
|
|
51
|
+
the two globs every run and hands the resulting paths to `run_vitest`, so landing a test edits no
|
|
52
|
+
list — and a failure is attributed from the JSON test TITLE (`run.sh:104-113`), never from the
|
|
53
|
+
code frame, which quotes an adjacent passing test's own title.
|
|
54
|
+
|
|
55
|
+
`scripts/check-*` is not a legacy pile; it is the home of what a vitest lane cannot hold. What
|
|
56
|
+
stays there, and why: LIVE lanes that need real accounts, models, tmux or a Docker daemon;
|
|
57
|
+
install and pack gates that consume a tarball; gates that spawn a subprocess or stand a server up;
|
|
58
|
+
gates that read product source as text; and the mutant EXECUTION COORDINATES described below. A
|
|
59
|
+
new hand-built gate needs a reason one of those five applies.
|
|
60
|
+
|
|
61
|
+
**Four rules bind a migrated gate that carries mutants**, and each of them was measured rather
|
|
62
|
+
than assumed (#119 V3 slice 2):
|
|
63
|
+
|
|
64
|
+
1. **Every QK claim is its own `it` title.** Attribution reads the failed test title, so a QK
|
|
65
|
+
that lives only in an assertion message leaves its mutant unattributable.
|
|
66
|
+
2. **The token appears exactly once in the file.** Putting it in the title AND the assertion
|
|
67
|
+
label is refused by the manifest set-integrity contract — the title keeps the token, the
|
|
68
|
+
assertion keeps the sentence.
|
|
69
|
+
3. **The `run.sh` case survives as a mutant execution coordinate, and keeps its `check:contracts`
|
|
70
|
+
entry.** The case is not a discovery path: the door already finds the test. It exists because
|
|
71
|
+
the qualification runner re-invokes a gate's argv once per mutant, and a gate argv calling
|
|
72
|
+
vitest directly would skip the reporter attribution depends on. Its file filter is narrow,
|
|
73
|
+
because a mutant pointed at the glob shim would re-run every beside-behaviour test once per
|
|
74
|
+
mutant. It stays in `check:contracts` because `check-release-gate-outcomes` requires a gate a
|
|
75
|
+
mutant names to run inside `check:full` — reachable only through qualification, its going red
|
|
76
|
+
on a clean tree would be noticed by nothing but the mutant body.
|
|
77
|
+
4. **Attribution is containment, not exclusivity.** `assert.ok` aborted at the first broken claim,
|
|
78
|
+
so exactly one label was ever reported; vitest runs every test, so one mutation surfaces
|
|
79
|
+
several failed titles (measured: 1–15). A kill is attributed when the claim's token is AMONG
|
|
80
|
+
them.
|
|
81
|
+
|
|
82
|
+
Migration moves lines BETWEEN the two axes `inventory-verification-surface.ts` counts, and only a
|
|
83
|
+
drop in the COMBINED number is subtraction. That is why the inventory reads both locations: a gate
|
|
84
|
+
that moved out of `scripts/` and into a file beside its subject would otherwise read as deleted.
|
|
85
|
+
|
|
47
86
|
### The canonical floor — two entry points
|
|
48
87
|
|
|
49
88
|
- **Deterministic floor — two tiers (#70):** `pnpm check` is the everyday CORE — toolchain (lint + typecheck), the vitest lanes, and the pure-unit / behavioral-contract / source-topology gates plus the cheap static coherence checks. It prints its own total wall time; acceptance is ≤60s on the reference host `oracle` (an operator measurement, never a hard wall-clock gate on arbitrary hosts). `pnpm run check:full` is the FULL deterministic floor — the core plus the hermetic-integration and package/install tiers — and is what the frozen-candidate protocol, push CI, release-gate, and `prepublishOnly` run. Exact membership is the named `check:*` group scripts in `package.json` (the executable SSOT — this document records meaning and principles, not the command list); a gate changes tier by semantic-class decision, never by getting faster or slower. The FULL tier carries the qualification HEAD (`check-gate-manifests`, through `check:hermetic`); the everyday core does not, and neither tier carries the separately scheduled mutant-executing body (`check-gate-qualification`).
|
|
@@ -56,6 +95,8 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
56
95
|
|
|
57
96
|
> **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.
|
|
58
97
|
|
|
98
|
+
> **The plugin's FIRST USER PATH is its own on-demand axis, and until it runs the npm acquisition has no receipt (#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"`. The production lock has said `npm` since `dd84ac0`, and the re-proof this same document demands above (§ source switch) still has an empty acquisition row. `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 — whether 0.9.1 carries that fallback is unmeasured, and a "no" 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 seven-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.
|
|
99
|
+
|
|
59
100
|
> **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.
|
|
60
101
|
|
|
61
102
|
> **Switching the plugin's production acquisition source is a re-proof, not a configuration change (#116 M3-b3).** `runtime-lock.json` carries a closed `source` discriminant: `npm` (exact `name@version` plus the published sha512, compared against the tarball's own bytes) is the production authority, and `herdr-checkout` (the exact commit Herdr checked out, from the fixed remote) is a VERIFICATION-ONLY carrier that exists so a candidate needs no npm release. Evidence does not transfer between them. Before a release that moves the production source — to npm, or to a GitHub Release tarball plus its sha512 — the exact acquisition and integrity comparison, the installed runtime (name@version, compiled entry, three executable bins, a real `check-bridge`), the swap and torn-swap recovery, activation and deactivation, and the package-consumer proof must all be re-run against THAT source. A candidate proof green on the checkout carrier says nothing about a registry artifact, and the reverse is equally true. **Records from before this contract are refused, not migrated:** a v1 runtime journal or activation ledger fails certification by name, and the way forward on such a host is an explicit `entwurf herdr-plugin-deactivate` (or a hand-cleared runtime/ledger state) before the next install — there is no reader that accepts both schemas, because the older shape cannot say WHICH artifact it was.
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -92,11 +92,11 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
|
|
|
92
92
|
|
|
93
93
|
| Surface | Declaration | Class | What a green actually says |
|
|
94
94
|
|---|---|---|---|
|
|
95
|
-
| Entwurf package | `0.
|
|
95
|
+
| Entwurf package | `0.23.0` | shipped baseline | the package contract these rows belong to |
|
|
96
96
|
| pi runtime | devDep exact `0.85.1`, peer `>=0.85.1 <0.86` | **exact** oracle + **closed range** | built and certified against 0.85.1; hosts inside the range are accepted, and the ceiling moves only on measurement |
|
|
97
97
|
| ACP wire SDK | `@agentclientprotocol/sdk 1.4.0` | **exact** | the shared wire oracle both adapters speak |
|
|
98
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
99
|
-
| Claude Agent SDK | `0.3.
|
|
98
|
+
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.79.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
|
|
99
|
+
| Claude Agent SDK | `0.3.274` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
100
100
|
| 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) |
|
|
101
101
|
| Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
|
|
102
102
|
| Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
|
|
@@ -168,6 +168,83 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
168
168
|
`resolvedProvider` and up onto the unconditional `session/new` path. entwurf passes `settings`
|
|
169
169
|
as an OBJECT (`tool-surface.ts:153`), so the branch is never taken. That makes "entwurf never
|
|
170
170
|
hands `settings` as a path" a contract rather than an accident; it is stated at that call site.
|
|
171
|
+
- **0.77.0's BREAKING change does not reach us, twice over.** `claudeCode.options.agent` is no
|
|
172
|
+
longer forwarded — the adapter now shallow-copies the options object and `delete`s `agent` on
|
|
173
|
+
the unconditional `session/new` path (`[측정 2026-09-18, 0.79.0 dist/acp-agent.js:5934-5941 직독]`).
|
|
174
|
+
entwurf's `buildClaudeSessionMeta` never sets that key (`tool-surface.ts:145-199` 직독: the
|
|
175
|
+
literal carries `model`/`tools`/`settingSources`/`settings` plus optional `plugins`/
|
|
176
|
+
`disallowedTools`/`extraArgs`, and nothing else), and the removed agent-picker exports
|
|
177
|
+
(`DEFAULT_AGENT_ID`, `AGENT_CONFIG_ID`, `BUILTIN_AGENT_NAMES`, `discoverCustomAgents`) are
|
|
178
|
+
imported nowhere: `[측정 2026-09-18]` `git grep -c` over `pi-extensions/`, `test/`, `scripts/`,
|
|
179
|
+
`mcp/` is **0**. We never imported the adapter as a library at all — we spawn its binary.
|
|
180
|
+
- **0.77.0's `allowDangerouslySkipPermissions` opt-out is a new lever we deliberately do not
|
|
181
|
+
pull, and our effective permission mode is unchanged.** 0.76.0 sent
|
|
182
|
+
`allowDangerouslySkipPermissions: ALLOW_BYPASS` unconditionally and computed
|
|
183
|
+
`initialPermissionMode = creationOpts.permissionMode ?? resolvePermissionMode(settings…)`.
|
|
184
|
+
0.79.0 computes `allowBypass = ALLOW_BYPASS && sessionMeta?.claudeCode?.options?.allowDangerouslySkipPermissions !== false`
|
|
185
|
+
and routes the whole thing through `resolvePermissionMode(…, logger, allowBypass)`
|
|
186
|
+
(`[측정 2026-09-18, 0.79.0 dist/acp-agent.js:5932-5933 직독]`). entwurf sets that option
|
|
187
|
+
nowhere, so `undefined !== false` holds and `allowBypass === ALLOW_BYPASS`;
|
|
188
|
+
`ALLOW_BYPASS = !IS_ROOT || !!process.env.IS_SANDBOX` is **byte-identical** between the two
|
|
189
|
+
versions (`dist/permissions/modes.js` 직독). Our overlay pins
|
|
190
|
+
`permissions.defaultMode: "bypassPermissions"` (`overlay.ts:122`), which resolves the same
|
|
191
|
+
under both. The lever now EXISTS for a host that wants a non-bypass sibling; declaring it is a
|
|
192
|
+
separate axis, not a one-line flip, and nothing in this bump takes it.
|
|
193
|
+
- **0.77.0's system-reminder strip never touches our first-user-message augment.**
|
|
194
|
+
`INJECTED_CONTEXT_MARKERS = ["system-reminder"]` joins the local-command markers in
|
|
195
|
+
`stripMarkerTags`, and `stripLocalCommandMetadata` has exactly two call sites
|
|
196
|
+
(`[측정 2026-09-18, 0.79.0 dist/acp-agent.js grep -n]`): `:4129`, gated on the message content
|
|
197
|
+
containing `<local-command-stdout>`, and `:5061`, in the `session/load` transcript replay. Both
|
|
198
|
+
run agent→client on text coming BACK from the transcript; our augment rides client→agent on the
|
|
199
|
+
first `session/prompt` and is never re-emitted, because entwurf calls `session/load` nowhere
|
|
200
|
+
(`[측정 2026-09-18]` `git grep loadSession` in `pi-extensions/lib/acp/` hits only the
|
|
201
|
+
`session-store.ts` capability TYPE, never a wire call). The augment also emits no
|
|
202
|
+
`<system-reminder>` tag of its own (`augment.ts` 직독), so no prose of ours is strippable.
|
|
203
|
+
- **0.78.0's compaction update is a NEW `sessionUpdate` kind and is inert for us, twice over.**
|
|
204
|
+
`compaction_update` and `compaction_summary_chunk` are new in `dist/context-compaction.js`
|
|
205
|
+
(`[측정 2026-09-18]` `sessionUpdate: "…"` literal sweep across both dists: 0.76.0 has 15 distinct kinds,
|
|
206
|
+
0.79.0 has 17, and the two new ones are exactly these). Gate one: the lifecycle's
|
|
207
|
+
`presentation` is `clientSupportsCompactionUpdates(this.clientCapabilities) ? "compaction_update"
|
|
208
|
+
: "tool_call"` (`dist/acp-agent.js:1875-1879`), and that predicate reads
|
|
209
|
+
`capabilities?.session?.compaction` — entwurf sends `clientCapabilities: {}` (`backend.ts:1758`),
|
|
210
|
+
so we keep the 0.75.0 `tool_call` presentation §11-8 already measured. The replay path
|
|
211
|
+
(`:5082`) is guarded by the same predicate at `:4915`. Gate two, independent: our mapper's
|
|
212
|
+
update switch has a `default: break` — "unknown update kinds are ignored (forward-compatible)"
|
|
213
|
+
(`event-mapper.ts:311-313`, `:377-378`). The "map every terminal reason, unknown is an error" rule
|
|
214
|
+
is about ACP **stopReason**, a different axis; `stopReason` literals are unchanged across the
|
|
215
|
+
two dists (`[측정 2026-09-18]` sweep: `"cancelled"` only, both versions). No code needed.
|
|
216
|
+
- **0.78.0's checkpoint file-change report and AIR diff counts are both behind the AIR gate.**
|
|
217
|
+
`supportsAgentFileChangeReport` is `clientSupportsAirCapability(capabilities, "agentFileChangeReport")`
|
|
218
|
+
(`dist/file-change-audit.js:27-29` 직독), and `air-extension.js`'s only delta is one added
|
|
219
|
+
constant `AIR_DIFF_STATS_KEY` (full-file `diff`, one line). Same `clientCapabilities: {}`
|
|
220
|
+
argument as the 0.76.0 `recommendedValue` entry — re-measured, not inherited.
|
|
221
|
+
- **0.79.0's shell-command permission prompts reach our permission handler's INPUT and change no
|
|
222
|
+
decision.** The change reorders options: when the CLI hints `defaultToNo` (new in 0.79.0 —
|
|
223
|
+
`[측정 2026-09-18]` `grep -rn defaultToNo` over the 0.76.0 dist is **0 hits**), the option array
|
|
224
|
+
now sorts reject-first (`dist/permissions/options.js:5-10`). entwurf's approve-all policy
|
|
225
|
+
selects `options.find((o) => o.kind === "allow_once" || o.kind === "allow_always")` and only
|
|
226
|
+
falls back to `options[0]` when that find fails (`backend.ts:824-831`) — a find by KIND, so
|
|
227
|
+
order cannot flip it. The fallback is unreachable besides: every builder in
|
|
228
|
+
`dist/permissions/options/` routes through `withOptionalUpdate`/`withGeneratedUpdate`, both of
|
|
229
|
+
which lead with `allowOnce()`, and the hand-rolled `tools.js` sets each carry an `allow_once` or
|
|
230
|
+
`allow_always` (직독 of `shared.js` + `tools.js`). The title change (Bash/PowerShell titles now
|
|
231
|
+
bypass `humanText` compaction) lands on a field we never read.
|
|
232
|
+
- **0.77.0–0.79.0's remaining fixes are unreachable under our capability posture.** The
|
|
233
|
+
AskUserQuestion multi-select/custom-text fixes (#1031, #1131) require form elicitation:
|
|
234
|
+
the adapter computes `disallowedTools = elicitationSupport.form ? [] : ["AskUserQuestion"]`
|
|
235
|
+
from `clientCapabilities.elicitation.form`, which our `{}` leaves false, so AskUserQuestion is
|
|
236
|
+
disabled on every session we open. The TaskList regex fix (#1006) is internal parsing. #1128's
|
|
237
|
+
tool names land on `presentation.toolCall._meta.claudeCode` on the permission-request path
|
|
238
|
+
(`dist/acp-agent.js:5317-5325`), which our handler ignores; our `titleForTool` already read
|
|
239
|
+
`_meta.claudeCode.toolName` as a fallback behind `update.title`, so it is additive at worst.
|
|
240
|
+
- **Our model-forcing and accounting wire calls are byte-identical across 0.76.0 → 0.79.0.**
|
|
241
|
+
`[측정 2026-09-18, brace-matched extraction from both dists, md5]`: `setSessionConfigOption`
|
|
242
|
+
(4,858 B, identical), `sessionUsage` (373 B, identical), `turnQuotaMeta` and `quotaTokenCount`
|
|
243
|
+
(identical md5), and `resolveModelPreference` lives in `dist/session-model.js`, whose whole file
|
|
244
|
+
is byte-identical (`md5 cfd031d0…` both versions). The `settings`-as-STRING-PATH branch our
|
|
245
|
+
call-site contract names is still on the unconditional `session/new` path
|
|
246
|
+
(`dist/acp-agent.js:6010-6012`).
|
|
247
|
+
|
|
171
248
|
- **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
|
|
172
249
|
synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
|
|
173
250
|
`Compact conversation`, and `_meta.contextCompaction` schema v1 — where it used to arrive as
|
|
@@ -292,23 +369,25 @@ caller-session `_meta`, and cross-machine certification.
|
|
|
292
369
|
|
|
293
370
|
A backend can return `newSession` before its declared MCP server is callable. This was
|
|
294
371
|
observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
|
|
295
|
-
path. Neither `claude-agent-acp` 0.
|
|
372
|
+
path. Neither `claude-agent-acp` 0.79.0 nor the Cortex landing adds a client-side
|
|
296
373
|
readiness fence over a session's declared MCP servers, and entwurf's common loop
|
|
297
374
|
calls `mcpServerStatus()` nowhere.
|
|
298
|
-
(Re-measured at the 0.
|
|
299
|
-
not reused, the way the 0.
|
|
300
|
-
`mcpServerStatus` call sites in `src/acp-agent.ts` are **2 at v0.
|
|
301
|
-
`[측정 2026-09-
|
|
375
|
+
(Re-measured at the 0.76.0 → 0.79.0 bump, not inherited — the previous bump's argument is
|
|
376
|
+
not reused, the way the 0.75.1 → 0.76.0 entry did not reuse 0.73.0 → 0.75.1's.
|
|
377
|
+
`mcpServerStatus` call sites in `src/acp-agent.ts` are **2 at v0.76.0 and 2 at v0.79.0**
|
|
378
|
+
`[측정 2026-09-18, upstream v0.79.0/src/acp-agent.ts read directly, grep -n]`; they first
|
|
302
379
|
appeared in 0.71.0 via `0cbbaf3` (MCP OAuth, LLM-25012), so the ADAPTER calls it where it
|
|
303
|
-
once did not. Both were re-read at `v0.
|
|
304
|
-
(v0.75.1: `:1736` / `:1829`; v0.73.0: `:1618` / `:1711`): the
|
|
305
|
-
`authenticateMcpServers` behind `supportsMcpOAuth(query)` and skips every
|
|
306
|
-
`needs-auth`; the second polls a SINGLE named server to `connected` under
|
|
307
|
-
Neither waits on every declared server before `newSession` returns. That
|
|
308
|
-
handshake, not a readiness fence, so the boundary below is unchanged. The
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
380
|
+
once did not. Both were re-read at `v0.79.0 src/acp-agent.ts:1773` and `:1866`
|
|
381
|
+
(v0.76.0: `:1762` / `:1855`; v0.75.1: `:1736` / `:1829`; v0.73.0: `:1618` / `:1711`): the
|
|
382
|
+
first sits inside `authenticateMcpServers` behind `supportsMcpOAuth(query)` and skips every
|
|
383
|
+
status that is not `needs-auth`; the second polls a SINGLE named server to `connected` under
|
|
384
|
+
an OAuth deadline. Neither waits on every declared server before `newSession` returns. That
|
|
385
|
+
is an auth handshake, not a readiness fence, so the boundary below is unchanged. The
|
|
386
|
+
surrounding 200-line window is byte-identical (`diff v0.76.0:1662-1862 v0.79.0:1673-1873`,
|
|
387
|
+
empty) and the region moved +11 while the file shrank 10,405 → 10,329 lines. The 0.77.0
|
|
388
|
+
agent-picker removal, the 0.78.0 compaction/checkpoint/AIR work and the 0.79.0 permission
|
|
389
|
+
presentation touch no part of this path, so the other reachable-surface findings stand as
|
|
390
|
+
re-measured in the capability-posture section above.
|
|
312
391
|
This bump changes no readiness behavior and closes no part of #72.)
|
|
313
392
|
|
|
314
393
|
### 11-7-a/b. Instrument and first measurement
|
|
@@ -123,8 +123,8 @@ export const claudeAdapter = {
|
|
|
123
123
|
},
|
|
124
124
|
// MEASURED (2026-09-02, #93): claude-agent-acp's `usage_update` carries a
|
|
125
125
|
// session-cumulative ESTIMATED cost (`cost.amount = message.total_cost_usd`)
|
|
126
|
-
// and a context-occupancy scalar (`used`; both read at 0.
|
|
127
|
-
// `dist/acp-agent.js:
|
|
126
|
+
// and a context-occupancy scalar (`used`; both read at 0.79.0
|
|
127
|
+
// `dist/acp-agent.js:3504-3519`). Both are measured, so backend.ts seals them.
|
|
128
128
|
// The token partition is NOT declared here: ACP's only token carrier is a
|
|
129
129
|
// per-turn round-trip aggregate, which is not what pi's four fields mean.
|
|
130
130
|
sealsTurnAccounting: true,
|
|
@@ -124,7 +124,8 @@ export function buildClaudeSessionMeta(params, normalizedSystemPrompt) {
|
|
|
124
124
|
// claude-agent-acp also accepts a STRING PATH, and at 0.76.0 the readFile +
|
|
125
125
|
// JSON.parse for that form moved out of `resolvedProvider` up onto the
|
|
126
126
|
// unconditional `session/new` path — a shape that WOULD reach us, and does not,
|
|
127
|
-
// only because we never hand it a path.
|
|
127
|
+
// only because we never hand it a path. Still on that path at 0.79.0
|
|
128
|
+
// (`dist/acp-agent.js:6010-6012`). Keep it inline: a path would put file
|
|
128
129
|
// IO, and its failure modes, inside every session creation.
|
|
129
130
|
settings: {
|
|
130
131
|
permissions: {
|
|
@@ -102,7 +102,7 @@ export const DISPATCH_LIVENESSES = ["live", "dormant", "indeterminate"];
|
|
|
102
102
|
// unreachable (the native-push rail measures it — entwurf-v2-decider.ts). Widening
|
|
103
103
|
// THIS set is a deliberate future decision, gated by a real compatible
|
|
104
104
|
// control-socket predicate — never by silently mapping sessions to dead/indeterminate
|
|
105
|
-
// (R1 핵심).
|
|
105
|
+
// (R1 핵심). pi-extensions/lib/entwurf-facts.test.ts pins this == ["pi"] and asserts the native-push
|
|
106
106
|
// domain is disjoint from it.
|
|
107
107
|
export const LIVENESS_DOMAIN_BACKENDS = ["pi"];
|
|
108
108
|
export function isLivenessSupported(backend) {
|
|
@@ -113,7 +113,7 @@ export function isLivenessSupported(backend) {
|
|
|
113
113
|
// live vendor app-server target probe), NOT a control socket. This domain is DISJOINT
|
|
114
114
|
// from LIVENESS_DOMAIN_BACKENDS: an Antigravity or Codex session is `unsupported` on
|
|
115
115
|
// the socket FACT axis (entwurf_peers) yet independently measured on the native-push
|
|
116
|
-
// axis. The two are separate rails on purpose —
|
|
116
|
+
// axis. The two are separate rails on purpose — pi-extensions/lib/entwurf-facts.test.ts pins both sets
|
|
117
117
|
// and asserts their intersection is ∅ (a backend can never be in both domains).
|
|
118
118
|
export const NATIVE_PUSH_BACKENDS = ["antigravity", "codex"];
|
|
119
119
|
export function nativePushSupported(backend) {
|
|
@@ -79,7 +79,7 @@ export const ENTWURF_V2_LOCK_DIR = path.join(os.homedir(), ".pi", "entwurf-v2-lo
|
|
|
79
79
|
export const LOCK_SUFFIX = ".lock";
|
|
80
80
|
export const LOCK_OWNER = "entwurf_v2";
|
|
81
81
|
/** The reject reason a lock conflict maps to. Kept as a literal here (the lock
|
|
82
|
-
* primitive stays decoupled from the full contract); `
|
|
82
|
+
* primitive stays decoupled from the full contract); `pi-extensions/lib/entwurf-v2-lock.test.ts`
|
|
83
83
|
* cross-checks it against the contract's ENTWURF_V2_REJECT_REASONS so the two
|
|
84
84
|
* cannot drift. */
|
|
85
85
|
export const LOCK_CONFLICT_REASON = "target-locked";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.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": {
|
|
@@ -73,7 +73,8 @@
|
|
|
73
73
|
"CONTRIBUTING.md",
|
|
74
74
|
"CHANGELOG.md",
|
|
75
75
|
"!**/__pycache__",
|
|
76
|
-
"!**/*.pyc"
|
|
76
|
+
"!**/*.pyc",
|
|
77
|
+
"!pi-extensions/**/*.test.ts"
|
|
77
78
|
],
|
|
78
79
|
"engines": {
|
|
79
80
|
"node": ">=24.0.0"
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
"claudeCodeFloor": ">=2.1.217"
|
|
83
84
|
},
|
|
84
85
|
"dependencies": {
|
|
85
|
-
"@agentclientprotocol/claude-agent-acp": "0.
|
|
86
|
+
"@agentclientprotocol/claude-agent-acp": "0.79.0",
|
|
86
87
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
87
88
|
"@anthropic-ai/sdk": "0.100.1",
|
|
88
89
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
@@ -127,7 +128,6 @@
|
|
|
127
128
|
"check-meta-capability-source": "./run.sh check-meta-capability-source",
|
|
128
129
|
"check-socket-probe": "./run.sh check-socket-probe",
|
|
129
130
|
"check-project-trust-handler": "./run.sh check-project-trust-handler",
|
|
130
|
-
"check-entwurf-v2-lock": "./run.sh check-entwurf-v2-lock",
|
|
131
131
|
"check-model-lock": "./run.sh check-model-lock",
|
|
132
132
|
"check-dep-versions": "./run.sh check-dep-versions",
|
|
133
133
|
"check-node-floor-coherence": "./run.sh check-node-floor-coherence",
|
|
@@ -135,6 +135,7 @@
|
|
|
135
135
|
"check-copilot-birth-hook": "./run.sh check-copilot-birth-hook",
|
|
136
136
|
"check-copilot-statusline": "./run.sh check-copilot-statusline",
|
|
137
137
|
"check-copilot-launch": "./run.sh check-copilot-launch",
|
|
138
|
+
"check-pi-launch": "./run.sh check-pi-launch",
|
|
138
139
|
"smoke-copilot-statusline-state": "./run.sh smoke-copilot-statusline-state",
|
|
139
140
|
"smoke-copilot-mcp-state": "./run.sh smoke-copilot-mcp-state",
|
|
140
141
|
"smoke-omp-bridge-state": "./run.sh smoke-omp-bridge-state",
|
|
@@ -173,9 +174,9 @@
|
|
|
173
174
|
"check:full": "bash scripts/check-elapsed.sh check:full check:core check:hermetic check:package",
|
|
174
175
|
"check:core": "pnpm run check:toolchain && pnpm run check:vitest && pnpm run check:contracts",
|
|
175
176
|
"check:toolchain": "pnpm lint && pnpm typecheck",
|
|
176
|
-
"check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface",
|
|
177
|
-
"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-
|
|
178
|
-
"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-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-codex-birth-hook && ./run.sh smoke-codex-config-state && ./run.sh smoke-codex-birth && ./run.sh check-herdr-sandbox",
|
|
177
|
+
"check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-tests-beside-behavior",
|
|
178
|
+
"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-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-codex-sender-identity && ./run.sh check-codex-native-push",
|
|
179
|
+
"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-codex-birth-hook && ./run.sh smoke-codex-config-state && ./run.sh smoke-codex-birth && ./run.sh check-herdr-sandbox",
|
|
179
180
|
"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-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
|
|
180
181
|
},
|
|
181
182
|
"pi": {
|
|
@@ -46,8 +46,8 @@ import type { AcpTextBlock } from "./context.js";
|
|
|
46
46
|
* shape) and never onto those four. A field rename upstream fails the typecheck
|
|
47
47
|
* here rather than silently zeroing the operator's cache-effect badge.
|
|
48
48
|
*/
|
|
49
|
-
/** One `_meta.quota.token_count` row (claude-agent-acp 0.
|
|
50
|
-
* `dist/acp-agent.js:
|
|
49
|
+
/** One `_meta.quota.token_count` row (claude-agent-acp 0.79.0
|
|
50
|
+
* `dist/acp-agent.js:6579-6588`). `cachedInputTokens` is cache READS — the name
|
|
51
51
|
* differs from `usage.cachedReadTokens` because the shape is shared with
|
|
52
52
|
* codex-acp; `cachedWriteTokens` is Claude's extra sibling. */
|
|
53
53
|
export type AcpQuotaTokenCount = {
|
|
@@ -64,7 +64,7 @@ export type AcpPromptResponse = {
|
|
|
64
64
|
/** Vendor-private, version-pinned. `_meta` is a standard ACP extension slot
|
|
65
65
|
* whose values a client may not assume, and `quota` is NOT in
|
|
66
66
|
* claude-agent-acp's exported types — it is produced by the private
|
|
67
|
-
* `turnQuotaMeta()` (read at 0.
|
|
67
|
+
* `turnQuotaMeta()` (read at 0.79.0 `dist/acp-agent.js:6562-6571`). Read
|
|
68
68
|
* defensively, never structurally required, and re-measure on a pin move. */
|
|
69
69
|
_meta?: {
|
|
70
70
|
quota?: {
|
|
@@ -150,7 +150,7 @@ export interface AcpAdapterSettingsParams {
|
|
|
150
150
|
*
|
|
151
151
|
* During stream handling the vendor keeps a per-message snapshot in
|
|
152
152
|
* `lastAssistantUsage`, but its `usage_update` publishes only the scalar `used`
|
|
153
|
-
* value (read at claude-agent-acp 0.
|
|
153
|
+
* value (read at claude-agent-acp 0.79.0 `dist/acp-agent.js:3920-3945`). Until
|
|
154
154
|
* that partition is carried on the wire, NOTHING honest can go in pi's four
|
|
155
155
|
* fields, so entwurf writes none of them.
|
|
156
156
|
*/
|
|
@@ -342,8 +342,8 @@ export const claudeAdapter: AcpBackendAdapter = {
|
|
|
342
342
|
|
|
343
343
|
// MEASURED (2026-09-02, #93): claude-agent-acp's `usage_update` carries a
|
|
344
344
|
// session-cumulative ESTIMATED cost (`cost.amount = message.total_cost_usd`)
|
|
345
|
-
// and a context-occupancy scalar (`used`; both read at 0.
|
|
346
|
-
// `dist/acp-agent.js:
|
|
345
|
+
// and a context-occupancy scalar (`used`; both read at 0.79.0
|
|
346
|
+
// `dist/acp-agent.js:3504-3519`). Both are measured, so backend.ts seals them.
|
|
347
347
|
// The token partition is NOT declared here: ACP's only token carrier is a
|
|
348
348
|
// per-turn round-trip aggregate, which is not what pi's four fields mean.
|
|
349
349
|
sealsTurnAccounting: true,
|
|
@@ -137,7 +137,7 @@ function finiteOrZero(value: unknown): number {
|
|
|
137
137
|
* "the accounting-grade figure per the SDK" — it also counts Task subagents,
|
|
138
138
|
* sidechains, and INTERNAL CALLS SUCH AS COMPACTION, so its rows "can total more
|
|
139
139
|
* than `token_count`" and are "the fuller picture, not a decomposition of it"
|
|
140
|
-
* (read at claude-agent-acp 0.
|
|
140
|
+
* (read at claude-agent-acp 0.79.0 `dist/acp-agent.js:6551-6571`). The narrower
|
|
141
141
|
* `PromptResponse.usage` (== `quota.token_count`) is the MAIN AGENT LOOP only.
|
|
142
142
|
*
|
|
143
143
|
* The wider one is the right numerator because the denominator already has that
|
|
@@ -1237,7 +1237,7 @@ export function streamAcpTurn(
|
|
|
1237
1237
|
// Writing zeros is not a placeholder for a better number we could compute:
|
|
1238
1238
|
// the per-request partition is genuinely absent from the wire. The vendor
|
|
1239
1239
|
// builds it in `lastAssistantUsage` and sends only its scalar sum (read at
|
|
1240
|
-
// claude-agent-acp 0.
|
|
1240
|
+
// claude-agent-acp 0.79.0 `dist/acp-agent.js:3920-3945`) — #96.
|
|
1241
1241
|
//
|
|
1242
1242
|
// But silence is NOT the resting state. A cache miss the operator never sees
|
|
1243
1243
|
// is a false reading, not a modest one: a session can run for hours believing
|
|
@@ -1356,9 +1356,9 @@ export function streamAcpTurn(
|
|
|
1356
1356
|
// between them is exactly why the notice below names a MECHANISM and
|
|
1357
1357
|
// never a cause: claude-agent-acp's `conversation_reset` handler only
|
|
1358
1358
|
// switches the SDK to a fresh conversation and touches no cost at all
|
|
1359
|
-
// (read at 0.
|
|
1359
|
+
// (read at 0.79.0 `dist/acp-agent.js:4349-4356`), while claude-agent-sdk
|
|
1360
1360
|
// separately documents that "a mid-session /clear resets the running
|
|
1361
|
-
// total" (read at 0.3.
|
|
1361
|
+
// total" (read at 0.3.274 `sdk.d.ts:5342`). A reset therefore PLAUSIBLY
|
|
1362
1362
|
// explains a backwards total, but nothing here has MEASURED that it did,
|
|
1363
1363
|
// and asserting the cause would be the same unmeasured claim this lane
|
|
1364
1364
|
// exists to end.
|