@junghanacs/entwurf 0.19.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/BASELINE.md +23 -5
- package/CHANGELOG.md +261 -0
- package/DELIVERY.md +59 -6
- package/README.md +41 -9
- package/VERIFY.md +9 -1
- package/docs/acp-backend-rail.md +41 -16
- package/docs/external-mcp-host.md +10 -5
- package/docs/fresh-cut-policy.md +3 -2
- package/docs/setup-clean-host.md +93 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
- package/package.json +3 -3
- package/pi-extensions/acp-provider.ts +1 -1
- 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 +3 -3
- package/pi-extensions/lib/acp/event-mapper.ts +4 -4
- package/pi-extensions/lib/acp/models.ts +3 -3
- package/pi-extensions/lib/acp/overlay.ts +6 -2
- package/pi-extensions/lib/acp/tool-surface.ts +6 -0
- package/pi-extensions/lib/meta-session.ts +25 -0
- package/run.sh +104 -33
- package/scripts/agy-bridge.sh +1 -1
- package/scripts/agy-imprint.sh +10 -7
- package/scripts/check-acp-carrier-augment.ts +6 -5
- package/scripts/check-acp-cortex.ts +41 -0
- package/scripts/check-acp-provider-surface.ts +9 -6
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-copilot-birth-hook.ts +3 -1
- package/scripts/check-copilot-receive-arm.ts +169 -21
- package/scripts/check-copilot-statusline.ts +3 -1
- package/scripts/check-gate-qualification.ts +11 -10
- package/scripts/check-hook-launch-topology.ts +6 -5
- package/scripts/check-install-surface.ts +25 -8
- package/scripts/check-meta-facts.ts +6 -4
- package/scripts/check-meta-hook-session-switch.ts +4 -3
- package/scripts/check-meta-identity-consumers.ts +8 -7
- package/scripts/check-meta-receiver-marker.ts +2 -1
- package/scripts/check-omp-birth-hook.ts +3 -2
- package/scripts/check-probe-bridge-command.ts +3 -1
- package/scripts/check-setup-qualification.sh +21 -0
- package/scripts/copilot-bridge-doctor.sh +15 -5
- package/scripts/copilot-bridge-install.sh +6 -2
- package/scripts/copilot-launch.sh +16 -2
- package/scripts/copilot-receive-bridge.sh +80 -13
- package/scripts/lib/reclaim-on-exit.ts +86 -0
- package/scripts/meta-bridge-doctor.sh +56 -15
- package/scripts/meta-bridge-install.sh +13 -7
- package/scripts/meta-bridge-uninstall.sh +6 -4
- package/scripts/mutants/acp-cortex.json +26 -2
- package/scripts/mutants/copilot-receive.json +26 -0
- package/scripts/mutants/omp-birth.json +23 -6
- package/scripts/mutants/pi-package-ownership.json +26 -0
- package/scripts/mutants/setup-verdict.json +13 -0
- package/scripts/omp-bridge-doctor.sh +110 -41
- package/scripts/omp-bridge-install.sh +6 -2
- package/scripts/omp-receive-install.sh +7 -2
- package/scripts/pi_settings_io.py +3 -1
- package/scripts/raw-async-delivery/README.md +10 -1
- package/scripts/raw-codex-measure/README.md +689 -0
- package/scripts/raw-codex-measure/source-audit.md +243 -0
- package/scripts/raw-macos-measure/README.md +148 -0
- package/scripts/raw-macos-measure/probe.sh +389 -0
- package/scripts/register-pi-package.py +38 -30
- package/scripts/register-pi-provider.py +3 -2
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-meta-async-drift.sh +7 -5
- package/scripts/smoke-meta-install-state.sh +70 -19
- package/scripts/smoke-omp-bridge-state.sh +38 -0
- package/scripts/smoke-setup-verdict.sh +100 -0
- package/scripts/smoke-user-scope-citizen.sh +199 -71
package/run.sh
CHANGED
|
@@ -201,7 +201,7 @@ Usage:
|
|
|
201
201
|
./run.sh smoke-copilot-mcp-state # Copilot MCP install/doctor/inverse regression. Offline/deterministic
|
|
202
202
|
./run.sh smoke-omp-bridge-state # OMP birth-extension install/doctor/inverse regression: placement, stale-writer detection, honest inverse, no-state refusal (structurally VALID as well as foreign), symlink refusal, ambiguous-agent-dir refusal (ledger M6), and a poisoned PI_CODING_AGENT_DIR that attracts no artifact. Fully sandboxed HOME/PI/XDG. Offline/deterministic
|
|
203
203
|
./run.sh smoke-agy-hooks-state # agy PreInvocation birth/sender hook install/doctor/inverse + direct stdin→meta-record regression. Offline/deterministic
|
|
204
|
-
./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + remove symmetry + fails loud, and the #86 C2 explicit ownership cells (project scope still normalizes ITS OWN stale entries; user scope refuses other owners and only takeover-user-scope moves the shared entry). Offline/hermetic (deps: bash+python3)
|
|
204
|
+
./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + remove symmetry + fails loud, and the #86 C2 explicit ownership cells (project scope still normalizes ITS OWN stale entries; user scope refuses other owners and only takeover-user-scope moves the shared entry), plus the #110 self-checkout cells: the real `run.sh install <checkout>` drive leaves the tracked .pi/settings.json byte-identical, writes NO project-scope packages[] self-registration, and REPORTS the skip; cell 15 boots the pinned devDep pi against two stand-in checkouts and requires the --entwurf-control collision to APPEAR before asserting its absence. Offline/hermetic (deps: bash+python3+pinned pi)
|
|
205
205
|
./run.sh smoke-meta-prune # 1.0.0 meta-bridge Phase 4: listing-only store janitor regression gate — classify keep/orphan/stale/ambiguous, delete nothing. Offline/deterministic (deps: bash+node)
|
|
206
206
|
./run.sh smoke-meta-keyset-guard # 0.10.0 meta-bridge: keyset-owner guard regression — check-keyset-overlap + managed-keys SSOT (disjoint passes, collisions fail). Offline/hermetic (deps: bash+python3)
|
|
207
207
|
./run.sh check-meta-manifest-schema # 0.12.2 meta-bridge: CLI-version-INDEPENDENT static guard — plugin manifests pinned to the minimal keyset that validates on the lowest supported Claude (closed-schema regression that broke 0.12.1 install on floor) + desired_mcp installed-vs-clone dual-mode. Offline (deps: python3)
|
|
@@ -209,7 +209,7 @@ Usage:
|
|
|
209
209
|
|
|
210
210
|
./run.sh install-meta-bridge # INTERNAL part of `setup` (native-harness plugin) + doctor recovery path — prefer `setup`; stateful GLOBAL install (plugin + USER MCP + settings keyset, honest uninstall state)
|
|
211
211
|
./run.sh uninstall-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL uninstall (restore only keys/items captured in install-state)
|
|
212
|
-
./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is
|
|
212
|
+
./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is NOT CERTIFIED and stays nonzero, on ONE narrowed reason — start-key and ppid are portable there (`ps` fallback, same string the TS core mints), so what holds the lane closed is per-process environment DISCOVERY alone, pending physical-host evidence. Detection power is held by check-meta-doctor-oracle
|
|
213
213
|
./run.sh copilot [args...] # #82 RAIL 7: the MANAGED Copilot launch. exec()s the vendor CLI in THIS terminal (cwd/pid/exit preserved) with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS set for that one invocation — entwurf owns no part of your shell and writes nothing to it, but it owns the process it is about to become. Without that flag Copilot skips the extension scan SILENTLY. Refuses to launch unless the receiver unit is actually installed. Injects `--model auto` when no --model was given and `--yolo` when no explicit permission/surface policy flag was given, both BEFORE any `--`; every argument you pass is forwarded byte-identical. RUNNING THIS IS YOUR CONSENT to that profile — use plain `copilot` for stock vendor behaviour. Not tmux, not fresh-call, mints no citizen (birth is still the first prompt)
|
|
214
214
|
./run.sh install-copilot-bridge # #82: GLOBAL install of the Copilot BIRTH plugin (own marketplace root; node+entry baked into the no-argv exec string). MCP wiring and the RECEIVER extension are separate install surfaces (install-copilot-mcp, install-copilot-receive). Also retires the stale Claude unit (--keep-stale-claude-unit opts out)
|
|
215
215
|
./run.sh uninstall-copilot-bridge # #86 C3a: honest inverse of install-copilot-bridge from the package-owned install-state (exact qualified id + exact marketplace name/path + recorded assembly only; state deleted LAST; a failing vendor list is UNKNOWN and refuses; never --force, never the stale Claude unit)
|
|
@@ -537,7 +537,28 @@ install_local_package() {
|
|
|
537
537
|
mkdir -p "$project_dir/.pi"
|
|
538
538
|
# packages[] registration via the shared SSOT — same is_entwurf_source
|
|
539
539
|
# predicate + idempotency as user-scope and remove (not a substring match).
|
|
540
|
-
|
|
540
|
+
#
|
|
541
|
+
# #110: entwurf's OWN checkout is the one project that never gets this entry.
|
|
542
|
+
# pi's package identity is `local:<resolved path>` and dedupePackages collapses
|
|
543
|
+
# only EQUAL identities (package-manager.js getPackageIdentity/dedupePackages),
|
|
544
|
+
# so a project entry naming this checkout and the user-scope entry naming the
|
|
545
|
+
# installed root are the same package only while they are the same directory.
|
|
546
|
+
# In a SECOND checkout (worktree, release scratch) they differ, pi keeps both,
|
|
547
|
+
# and both copies of pi-extensions/entwurf-control.ts declare
|
|
548
|
+
# `--entwurf-control`: detectExtensionConflicts gives the flag to whoever
|
|
549
|
+
# registered first — project scope — and fails the INSTALLED extension while
|
|
550
|
+
# printing the installed path as the loser, which reads exactly backwards.
|
|
551
|
+
# user-scope already owns self-registration from any cwd, so the project entry
|
|
552
|
+
# was only ever a duplicate of it; dropping it removes the second identity in
|
|
553
|
+
# written bytes, the way the tracked `.pi/settings.json` no longer carries one.
|
|
554
|
+
# A FOREIGN project still gets its entry — that is what `entwurf install
|
|
555
|
+
# <project>` is for, and an npm consumer's REPO_DIR is inside node_modules, so
|
|
556
|
+
# it can never take this branch.
|
|
557
|
+
if [ "$(cd -P -- "$project_dir" 2>/dev/null && pwd)" = "$REPO_DIR" ]; then
|
|
558
|
+
echo "install: project-scope packages[] skipped — entwurf's own checkout self-registers through user scope (#110)"
|
|
559
|
+
else
|
|
560
|
+
python3 "$REPO_DIR/scripts/register-pi-package.py" "$project_dir/.pi/settings.json" "$REPO_DIR"
|
|
561
|
+
fi
|
|
541
562
|
# entwurfProvider.mcpServers.entwurf-bridge (project scope — checkout-local, NO state; #46
|
|
542
563
|
# Task 2) via the shared register-pi-provider SSOT: normalize the command to the bare stable
|
|
543
564
|
# bin `entwurf-bridge` (ownership-classified: absent/managed-current/managed-legacy adopt, a
|
|
@@ -707,8 +728,11 @@ remove_local_package() {
|
|
|
707
728
|
# install, so remove never over-deletes a look-alike repo (entwurf-notes, …)
|
|
708
729
|
# that install would never have registered. Same rule, one shape further (#53 B):
|
|
709
730
|
# register only ever WRITES the absolute path, so a settings-RELATIVE entry naming
|
|
710
|
-
# this repo
|
|
711
|
-
#
|
|
731
|
+
# this repo is source an operator hand-wrote, not install state — remove leaves it
|
|
732
|
+
# and says so instead of editing bytes it never authored. entwurf's own checkout
|
|
733
|
+
# no longer ships such an entry (#110: user scope owns self-registration), so for
|
|
734
|
+
# THIS repo the call is a reported no-op; the predicate stays because the shape is
|
|
735
|
+
# still writable by hand and still must not be over-deleted.
|
|
712
736
|
python3 "$REPO_DIR/scripts/register-pi-package.py" "$project_dir/.pi/settings.json" "$REPO_DIR" --remove
|
|
713
737
|
# entwurfProvider.mcpServers.entwurf-bridge cleanup (project scope) via the shared SSOT: strip
|
|
714
738
|
# our-managed shapes (the bare stable bin AND the legacy repo start.sh path — a true user
|
|
@@ -2667,8 +2691,8 @@ check_acp_provider_surface() {
|
|
|
2667
2691
|
# provider lib modules AND compiles + drives the REAL entry against a fake pi,
|
|
2668
2692
|
# asserting: one surface name, the no-auth sentinel shape, full
|
|
2669
2693
|
# ProviderModelConfig rows on the curated claude anchor, the EXACT curated model
|
|
2670
|
-
# set both adapters contribute (claude
|
|
2671
|
-
# [QK:CORTEX-PROVIDER-
|
|
2694
|
+
# set both adapters contribute (claude 3 + cortex 4 —
|
|
2695
|
+
# [QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE], the claim that catches an entry which
|
|
2672
2696
|
# silently drops a whole backend), and that streamSimple is the real
|
|
2673
2697
|
# streamShellAcp backend (checked BY NAME, never invoked — invoking spawns an
|
|
2674
2698
|
# ACP child). Pure, no pi runtime, no API.
|
|
@@ -3564,6 +3588,7 @@ _check_pack_install_impl() {
|
|
|
3564
3588
|
local loader_ids loader_expected
|
|
3565
3589
|
loader_ids=$(awk '$1 == "entwurf" { print $2 }' <<<"$loader_out" | LC_ALL=C sort)
|
|
3566
3590
|
loader_expected=$(printf '%s\n' \
|
|
3591
|
+
claude-fable-5-1 \
|
|
3567
3592
|
claude-opus-5 \
|
|
3568
3593
|
claude-sonnet-5 \
|
|
3569
3594
|
cortex-auto \
|
|
@@ -3571,7 +3596,7 @@ _check_pack_install_impl() {
|
|
|
3571
3596
|
cortex-claude-sonnet-5 \
|
|
3572
3597
|
cortex-openai-gpt-5.4 | LC_ALL=C sort)
|
|
3573
3598
|
if [ "$loader_ids" != "$loader_expected" ]; then
|
|
3574
|
-
fail "[check-pack-install] installed provider model set drifted — expected exactly
|
|
3599
|
+
fail "[check-pack-install] installed provider model set drifted — expected exactly 7 curated ids (claude 3 + cortex 4):"
|
|
3575
3600
|
echo " expected:" >&2
|
|
3576
3601
|
echo "$loader_expected" | sed 's/^/ /' >&2
|
|
3577
3602
|
echo " actual (entwurf rows):" >&2
|
|
@@ -3582,8 +3607,8 @@ _check_pack_install_impl() {
|
|
|
3582
3607
|
fi
|
|
3583
3608
|
# Name the set on SUCCESS too, not only in the failure branch — same principle as
|
|
3584
3609
|
# the pinned-pi version assert above: a gate that cannot name what it proved has
|
|
3585
|
-
# proved nothing, and
|
|
3586
|
-
echo "[check-pack-install] pi loader smoke pass (entwurf registered; exact
|
|
3610
|
+
# proved nothing, and a row COUNT without the ids cannot be audited from a CI log.
|
|
3611
|
+
echo "[check-pack-install] pi loader smoke pass (entwurf registered; exact curated set: claude 3 + cortex 4):"
|
|
3587
3612
|
echo "$loader_ids" | sed 's/^/ /'
|
|
3588
3613
|
|
|
3589
3614
|
# npm-managed neutral install regression — the README's PRIMARY install path is
|
|
@@ -4815,6 +4840,44 @@ setup_result() { # $1=component $2=PASS|SKIP|FAIL $3=detail
|
|
|
4815
4840
|
esac
|
|
4816
4841
|
}
|
|
4817
4842
|
|
|
4843
|
+
# ── platform rail certification (#78 D1) ─────────────────────────────────────
|
|
4844
|
+
# ONE place answers "are entwurf's HARNESS RAILS certified on this platform?".
|
|
4845
|
+
# Every detected-harness branch in setup_all asks it through setup_harness_result
|
|
4846
|
+
# below instead of re-deciding it per backend — a verdict that is honest for one
|
|
4847
|
+
# harness and cosmetic for the next is its own defect, not half a fix.
|
|
4848
|
+
#
|
|
4849
|
+
# Today the answer is Linux and nothing else, and that is an EVIDENCE fact, not a
|
|
4850
|
+
# taste: every shipped rail proves live ownership by reading the per-process
|
|
4851
|
+
# ENVIRONMENT of live harness children, no portable Darwin equivalent has been
|
|
4852
|
+
# MEASURED, and BASELINE.md/README record each rail as `NOT CERTIFIED — pending
|
|
4853
|
+
# physical host`. The harness doctors own that axis and refuse on Darwin.
|
|
4854
|
+
#
|
|
4855
|
+
# What moves this line: a physical-host receipt — a real harness doctor exiting 0
|
|
4856
|
+
# on that platform, recorded in BASELINE.md. A green INSTALLER never moves it;
|
|
4857
|
+
# installation portability and rail/runtime support are separate evidence axes
|
|
4858
|
+
# (Hard Rule 17). Do not widen this to make a CI job green.
|
|
4859
|
+
harness_rail_certified_platform() {
|
|
4860
|
+
[ "$(uname -s)" = "Linux" ]
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4863
|
+
# setup_harness_result — the verdict for a detected harness unit whose INSTALL
|
|
4864
|
+
# COMPLETED. It exists because "the wiring was written" and "the rail works here"
|
|
4865
|
+
# are two different facts and setup measures only the first: setup runs no doctor
|
|
4866
|
+
# (that is the doctor commands' own job). On a platform whose rails carry no
|
|
4867
|
+
# receipt a completed install is therefore a detected-but-INCOMPLETE integration,
|
|
4868
|
+
# which Hard Rule 17 names non-green rather than cosmetic success. setup_result
|
|
4869
|
+
# has exactly three verdicts, so FAIL IS that named non-green — and the detail
|
|
4870
|
+
# must say so in the operator's own words: nothing failed to install, the rail is
|
|
4871
|
+
# simply uncertified here. The wording stays deliberately distinct from the
|
|
4872
|
+
# install-failure FAIL each branch keeps for a real refusal.
|
|
4873
|
+
setup_harness_result() { # $1=component $2=what got wired $3=doctor owning the rail axis
|
|
4874
|
+
if harness_rail_certified_platform; then
|
|
4875
|
+
setup_result "$1" PASS "$2 — verify: $3"
|
|
4876
|
+
return 0
|
|
4877
|
+
fi
|
|
4878
|
+
setup_result "$1" FAIL "$2 — the wiring WAS written and nothing failed to install, but this harness rail is NOT CERTIFIED — pending physical host on $(uname -s). setup runs no doctor; the rail axis is owned by '$3', which refuses on this platform. Tracking: #78."
|
|
4879
|
+
}
|
|
4880
|
+
|
|
4818
4881
|
# Installed-vs-source is decided by PACKAGE LOCATION — the same `*/node_modules/*`
|
|
4819
4882
|
# seam run_ts and the bridge launcher already branch on — never by which of
|
|
4820
4883
|
# pnpm/pi happens to be missing (an accidental failure is not a mode gate).
|
|
@@ -4955,7 +5018,7 @@ setup_all() {
|
|
|
4955
5018
|
if ! pi_version_in_range "${pi_ver:-0}" "$pi_range"; then
|
|
4956
5019
|
setup_result pi FAIL "detected pi ${pi_ver:-<unreadable version>} is outside the supported range $pi_range — Pi wiring not written; align pi, then re-run setup"
|
|
4957
5020
|
elif (install_local_package "$project_dir"); then
|
|
4958
|
-
|
|
5021
|
+
setup_harness_result pi "project + user-scope wiring complete (pi $pi_ver in $pi_range)" "./run.sh doctor-pi-provider"
|
|
4959
5022
|
else
|
|
4960
5023
|
setup_result pi FAIL "pi wiring did not complete (see above) — repair, then re-run setup"
|
|
4961
5024
|
fi
|
|
@@ -4965,16 +5028,20 @@ setup_all() {
|
|
|
4965
5028
|
# setup ALSO wires the native-harness meta-bridge so a relocate/clone needs ONE
|
|
4966
5029
|
# command. Detection-gated ("있으면 설정, 없으면 담아준다");
|
|
4967
5030
|
# meta-bridge-install.sh is idempotent and enforces the Claude floor + its own
|
|
4968
|
-
# named prerequisites itself, so a below-floor
|
|
4969
|
-
#
|
|
4970
|
-
#
|
|
5031
|
+
# named prerequisites itself, so a below-floor host lands here as a detected
|
|
5032
|
+
# FAIL. It no longer REFUSES ON PLATFORM NAME (0.20.0 opened Darwin: the
|
|
5033
|
+
# installer's own mechanism is portable), so the other half of that old
|
|
5034
|
+
# guarantee — "never a cosmetic green" — is now carried by setup_harness_result:
|
|
5035
|
+
# an install that completes where the RAIL has no receipt is the named
|
|
5036
|
+
# non-green, never a PASS pointing at a doctor that would refuse. CLAUDE_BIN
|
|
5037
|
+
# pins the probe for the hermetic smoke; production leaves it unset.
|
|
4971
5038
|
local claude_bin="${CLAUDE_BIN:-claude}"
|
|
4972
5039
|
if ! command -v "$claude_bin" >/dev/null 2>&1; then
|
|
4973
5040
|
setup_result claude SKIP "claude not on PATH — zero meta-bridge wiring written"
|
|
4974
5041
|
else
|
|
4975
5042
|
section "meta-bridge install (native harness detected: Claude Code)"
|
|
4976
5043
|
if (cd "$REPO_DIR" && bash scripts/meta-bridge-install.sh); then
|
|
4977
|
-
|
|
5044
|
+
setup_harness_result claude "meta-bridge wired" "./run.sh doctor-meta-bridge"
|
|
4978
5045
|
else
|
|
4979
5046
|
setup_result claude FAIL "detected Claude Code, but the meta-bridge integration did not complete (see above) — repair, then re-run setup"
|
|
4980
5047
|
fi
|
|
@@ -5017,19 +5084,19 @@ setup_all() {
|
|
|
5017
5084
|
else
|
|
5018
5085
|
agy_rc=0; wire_agy_bridge || agy_rc=$?
|
|
5019
5086
|
if [ "$agy_rc" -eq 0 ]; then
|
|
5020
|
-
|
|
5087
|
+
setup_harness_result agy-bridge "MCP bridge wired" "./run.sh doctor-agy-bridge"
|
|
5021
5088
|
else
|
|
5022
5089
|
setup_result agy-bridge FAIL "detected agy, but the MCP bridge integration did not complete (reason above) — repair, then re-run setup"
|
|
5023
5090
|
fi
|
|
5024
5091
|
agy_rc=0; wire_agy_statusline || agy_rc=$?
|
|
5025
5092
|
if [ "$agy_rc" -eq 0 ]; then
|
|
5026
|
-
|
|
5093
|
+
setup_harness_result agy-statusline "statusLine wired" "./run.sh doctor-agy-statusline"
|
|
5027
5094
|
else
|
|
5028
5095
|
setup_result agy-statusline FAIL "detected agy, but the statusLine integration did not complete (reason above) — repair, then re-run setup"
|
|
5029
5096
|
fi
|
|
5030
5097
|
agy_rc=0; wire_agy_hooks || agy_rc=$?
|
|
5031
5098
|
if [ "$agy_rc" -eq 0 ]; then
|
|
5032
|
-
|
|
5099
|
+
setup_harness_result agy-hooks "birth imprint wired" "./run.sh doctor-agy-hooks"
|
|
5033
5100
|
else
|
|
5034
5101
|
setup_result agy-hooks FAIL "detected agy, but the birth-imprint integration did not complete (reason above) — repair, then re-run setup"
|
|
5035
5102
|
fi
|
|
@@ -5053,13 +5120,13 @@ setup_all() {
|
|
|
5053
5120
|
section "copilot units (native harness detected: GitHub Copilot CLI)"
|
|
5054
5121
|
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-bridge-install.sh) || copilot_rc=$?
|
|
5055
5122
|
if [ "$copilot_rc" -eq 0 ]; then
|
|
5056
|
-
|
|
5123
|
+
setup_harness_result copilot-birth "birth plugin installed" "./run.sh doctor-copilot-bridge"
|
|
5057
5124
|
else
|
|
5058
5125
|
setup_result copilot-birth FAIL "detected copilot, but the birth plugin install did not complete (see above) — repair, then re-run setup"
|
|
5059
5126
|
fi
|
|
5060
5127
|
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-mcp-bridge.sh install) || copilot_rc=$?
|
|
5061
5128
|
if [ "$copilot_rc" -eq 0 ]; then
|
|
5062
|
-
|
|
5129
|
+
setup_harness_result copilot-mcp "MCP server registered" "./run.sh doctor-copilot-mcp"
|
|
5063
5130
|
else
|
|
5064
5131
|
setup_result copilot-mcp FAIL "detected copilot, but the MCP registration did not complete (see above) — repair, then re-run setup"
|
|
5065
5132
|
fi
|
|
@@ -5077,14 +5144,14 @@ setup_all() {
|
|
|
5077
5144
|
else
|
|
5078
5145
|
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-receive-bridge.sh install) || copilot_rc=$?
|
|
5079
5146
|
if [ "$copilot_rc" -eq 0 ]; then
|
|
5080
|
-
|
|
5147
|
+
setup_harness_result copilot-receive "receiver extension installed" "./run.sh doctor-copilot-receive"
|
|
5081
5148
|
else
|
|
5082
5149
|
setup_result copilot-receive FAIL "detected copilot, but the receiver extension install did not complete (see above) — repair, then re-run setup"
|
|
5083
5150
|
fi
|
|
5084
5151
|
fi
|
|
5085
5152
|
copilot_rc=0; (cd "$REPO_DIR" && bash scripts/copilot-statusline-bridge.sh install) || copilot_rc=$?
|
|
5086
5153
|
if [ "$copilot_rc" -eq 0 ]; then
|
|
5087
|
-
|
|
5154
|
+
setup_harness_result copilot-statusline "visible footer wired" "./run.sh doctor-copilot-statusline"
|
|
5088
5155
|
else
|
|
5089
5156
|
setup_result copilot-statusline FAIL "detected copilot, but the visible-footer integration did not complete (see above) — repair, then re-run setup"
|
|
5090
5157
|
fi
|
|
@@ -5107,13 +5174,13 @@ setup_all() {
|
|
|
5107
5174
|
section "omp units (native harness detected: oh-my-pi)"
|
|
5108
5175
|
omp_rc=0; (cd "$REPO_DIR" && bash scripts/omp-bridge-install.sh) || omp_rc=$?
|
|
5109
5176
|
if [ "$omp_rc" -eq 0 ]; then
|
|
5110
|
-
|
|
5177
|
+
setup_harness_result omp-birth "birth extension installed" "./run.sh doctor-omp-bridge"
|
|
5111
5178
|
else
|
|
5112
5179
|
setup_result omp-birth FAIL "detected omp, but the birth extension install did not complete (see above) — repair, then re-run setup"
|
|
5113
5180
|
fi
|
|
5114
5181
|
omp_rc=0; (cd "$REPO_DIR" && bash scripts/omp-mcp-bridge.sh install) || omp_rc=$?
|
|
5115
5182
|
if [ "$omp_rc" -eq 0 ]; then
|
|
5116
|
-
|
|
5183
|
+
setup_harness_result omp-mcp "MCP server registered" "./run.sh doctor-omp-mcp"
|
|
5117
5184
|
else
|
|
5118
5185
|
setup_result omp-mcp FAIL "detected omp, but the MCP registration did not complete (see above) — repair, then re-run setup"
|
|
5119
5186
|
fi
|
|
@@ -5124,13 +5191,13 @@ setup_all() {
|
|
|
5124
5191
|
# shipping a citizen whose tools nobody can call.
|
|
5125
5192
|
omp_rc=0; (cd "$REPO_DIR" && bash scripts/omp-config-xdev.sh install) || omp_rc=$?
|
|
5126
5193
|
if [ "$omp_rc" -eq 0 ]; then
|
|
5127
|
-
|
|
5194
|
+
setup_harness_result omp-config "tools.xdev: false written" "./run.sh doctor-omp-mcp"
|
|
5128
5195
|
else
|
|
5129
5196
|
setup_result omp-config FAIL "detected omp, but the tools.xdev operator setting did not land (see above) — resolve it, then re-run setup"
|
|
5130
5197
|
fi
|
|
5131
5198
|
omp_rc=0; (cd "$REPO_DIR" && bash scripts/omp-receive-install.sh) || omp_rc=$?
|
|
5132
5199
|
if [ "$omp_rc" -eq 0 ]; then
|
|
5133
|
-
|
|
5200
|
+
setup_harness_result omp-receive "receiver extension installed" "./run.sh doctor-omp-receive"
|
|
5134
5201
|
else
|
|
5135
5202
|
setup_result omp-receive FAIL "detected omp, but the receiver extension install did not complete (see above) — repair, then re-run setup"
|
|
5136
5203
|
fi
|
|
@@ -5732,9 +5799,11 @@ case "$cmd" in
|
|
|
5732
5799
|
check_shell_quote
|
|
5733
5800
|
;;
|
|
5734
5801
|
check-install-surface)
|
|
5735
|
-
#
|
|
5736
|
-
#
|
|
5737
|
-
#
|
|
5802
|
+
# structural half of the node_modules strip-types fence: run_ts is the only crossing,
|
|
5803
|
+
# every operator subcommand has a compiled twin, bin wrappers branch, dev gates stay out
|
|
5804
|
+
# of the tarball, offline smokes carry no OBVIOUS write to the real $HOME (a static
|
|
5805
|
+
# tripwire, never a sandbox proof), and S7 pins the candidate .pi/settings.json — the
|
|
5806
|
+
# shared release skill, the stable bridge bin, and NO packages key (#110).
|
|
5738
5807
|
# check-pack-install owns the dynamic half (it drives the installed commands).
|
|
5739
5808
|
run_ts scripts/check-install-surface.ts
|
|
5740
5809
|
;;
|
|
@@ -6202,10 +6271,12 @@ case "$cmd" in
|
|
|
6202
6271
|
# XDG data dir ($XDG_DATA_HOME/entwurf/meta-bridge/.assembled — dev clone and
|
|
6203
6272
|
# installed package alike, never the checkout) and runs marketplace add +
|
|
6204
6273
|
# install --scope user, so every native Claude Code session auto-loads it.
|
|
6205
|
-
# Idempotent
|
|
6206
|
-
#
|
|
6207
|
-
#
|
|
6208
|
-
#
|
|
6274
|
+
# Idempotent. Install accepts Linux | Darwin — the fence that remains inside the
|
|
6275
|
+
# installer is the toolchain check (python3/node/claude presence), not the platform
|
|
6276
|
+
# name. Rail certification stays with doctor-meta-bridge: Darwin is NOT CERTIFIED —
|
|
6277
|
+
# pending physical host, reason = per-process environment DISCOVERY. Uninstall
|
|
6278
|
+
# permits Darwin so an older macOS install is not stranded without an honest
|
|
6279
|
+
# inverse.
|
|
6209
6280
|
(cd "$REPO_DIR" && bash scripts/meta-bridge-install.sh "$@")
|
|
6210
6281
|
;;
|
|
6211
6282
|
uninstall-meta-bridge)
|
package/scripts/agy-bridge.sh
CHANGED
|
@@ -233,7 +233,7 @@ doctor_static_one() {
|
|
|
233
233
|
# entwurf does NOT repair a failed launcher here: it may be owned by a foreign file.
|
|
234
234
|
log " $label: configured → '$cmd' does NOT serve MCP with its configured args/env$link_note"
|
|
235
235
|
log " $BOOT_DETAIL"
|
|
236
|
-
log " Identify the launcher: command -v '$cmd';
|
|
236
|
+
log " Identify the launcher: command -v '$cmd'; ls -l \"\$(command -v '$cmd')\""
|
|
237
237
|
log " If it is entwurf's managed dev link, restore it with ./run.sh expose-dev-bin (it REFUSES a foreign link)."
|
|
238
238
|
log " If a foreign launcher owns the name, repair/remove it yourself or put a working one earlier on PATH."
|
|
239
239
|
return 1 ;;
|
package/scripts/agy-imprint.sh
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Stable bin wrapper for Antigravity PreInvocation birth imprint.
|
|
3
3
|
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
5
|
+
while [ -L "$SOURCE" ]; do
|
|
6
|
+
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
7
|
+
TARGET="$(readlink "$SOURCE")"
|
|
8
|
+
case "$TARGET" in
|
|
9
|
+
/*) SOURCE="$TARGET" ;;
|
|
10
|
+
*) SOURCE="$DIR/$TARGET" ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
HERE="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
11
14
|
ROOT="$(cd "$HERE/.." && pwd)"
|
|
12
15
|
|
|
13
16
|
# Node refuses --experimental-strip-types for raw .ts below node_modules. Dev
|
|
@@ -31,8 +31,9 @@ import { loadEngraving } from "../pi-extensions/lib/acp/engraving.ts";
|
|
|
31
31
|
import { bridgeConfigSignature, contextMessageSignatures } from "../pi-extensions/lib/acp/session-store.ts";
|
|
32
32
|
import { buildClaudeSessionMeta } from "../pi-extensions/lib/acp/tool-surface.ts";
|
|
33
33
|
import { ENTWURF_PROJECT_CONTEXT_OPEN_TAG } from "../protocol.js";
|
|
34
|
+
import { reclaimOnExit } from "./lib/reclaim-on-exit.ts";
|
|
34
35
|
|
|
35
|
-
const tmp = mkdtempSync(join(tmpdir(), "acp-carrier-augment-"));
|
|
36
|
+
const tmp = reclaimOnExit(mkdtempSync(join(tmpdir(), "acp-carrier-augment-")));
|
|
36
37
|
const REPO_DIR = fileURLToPath(new URL("..", import.meta.url));
|
|
37
38
|
const BRIDGE_MARK = "operating through entwurf";
|
|
38
39
|
|
|
@@ -241,8 +242,8 @@ function ctxWith(firstUser: string): Context {
|
|
|
241
242
|
// absent → keep it. Home AGENTS.md always survives.
|
|
242
243
|
// ===========================================================================
|
|
243
244
|
{
|
|
244
|
-
const home = mkdtempSync(join(tmpdir(), "acp-home-"));
|
|
245
|
-
const proj = mkdtempSync(join(tmpdir(), "acp-proj-"));
|
|
245
|
+
const home = reclaimOnExit(mkdtempSync(join(tmpdir(), "acp-home-")));
|
|
246
|
+
const proj = reclaimOnExit(mkdtempSync(join(tmpdir(), "acp-proj-")));
|
|
246
247
|
writeFileSync(join(home, "AGENTS.md"), "HOME-AGENTS-CONTENT");
|
|
247
248
|
writeFileSync(join(proj, "AGENTS.md"), "CWD-AGENTS-CONTENT");
|
|
248
249
|
|
|
@@ -295,7 +296,7 @@ function ctxWith(firstUser: string): Context {
|
|
|
295
296
|
assert.match(dateLine as string, /^Current date: \d{4}-\d{2}-\d{2}$/, "date is day-granularity only (no clock time)");
|
|
296
297
|
|
|
297
298
|
// > 50KB cwd AGENTS.md → augment truncated with the marker.
|
|
298
|
-
const bigHome = mkdtempSync(join(tmpdir(), "acp-big-"));
|
|
299
|
+
const bigHome = reclaimOnExit(mkdtempSync(join(tmpdir(), "acp-big-")));
|
|
299
300
|
writeFileSync(join(bigHome, "AGENTS.md"), "X".repeat(80 * 1024));
|
|
300
301
|
const big = buildPiContextAugment({ backend: "claude", cwd: tmp, mcpServerNames: [], homeDir: bigHome });
|
|
301
302
|
assert.ok(Buffer.byteLength(big, "utf8") <= 50 * 1024, "augment is truncated to the 50KB cap");
|
|
@@ -313,7 +314,7 @@ function ctxWith(firstUser: string): Context {
|
|
|
313
314
|
// remains an honest fallback; the maintained package prompt must not hit it.
|
|
314
315
|
// ===========================================================================
|
|
315
316
|
{
|
|
316
|
-
const budgetHome = mkdtempSync(join(tmpdir(), "acp-budget-home-"));
|
|
317
|
+
const budgetHome = reclaimOnExit(mkdtempSync(join(tmpdir(), "acp-budget-home-")));
|
|
317
318
|
writeFileSync(join(budgetHome, "AGENTS.md"), "H".repeat(12 * 1024));
|
|
318
319
|
const actual = buildPiContextAugment({
|
|
319
320
|
backend: "claude",
|
|
@@ -323,6 +323,47 @@ const enrichedServers: AcpMcpServer[] = [
|
|
|
323
323
|
/absolute realHome/,
|
|
324
324
|
"a non-absolute realHome must refuse (the child must never re-derive it)",
|
|
325
325
|
);
|
|
326
|
+
assert.throws(
|
|
327
|
+
() =>
|
|
328
|
+
ensureCortexDualHomeOverlay({
|
|
329
|
+
scopeKey: "pi:sess-empty",
|
|
330
|
+
mcpServers: [],
|
|
331
|
+
realHome: "",
|
|
332
|
+
overlaysRoot,
|
|
333
|
+
isPidAlive: () => true,
|
|
334
|
+
}),
|
|
335
|
+
/absolute realHome/,
|
|
336
|
+
"an empty realHome must refuse — the contract sentence is 'absolute', and '' is not",
|
|
337
|
+
);
|
|
338
|
+
// …and the guard states PATH-FLAVOR absoluteness, not the host it runs on:
|
|
339
|
+
// a drive/UNC realHome is absolute in the win32 flavor, so a POSIX host must
|
|
340
|
+
// NOT read it as relative. (This asserts nothing about native-Windows support
|
|
341
|
+
// — only that the refusal rule is decided by the path, not by process.platform.)
|
|
342
|
+
// realSnowflakeHome is pinned under tmp so the positive cells exercise the
|
|
343
|
+
// GUARD alone: the win32-flavored realHome never reaches a POSIX join for fs
|
|
344
|
+
// work, and every path the overlay materializes stays under overlaysRoot.
|
|
345
|
+
for (const winFlavored of ["C:\\Users\\x", "\\\\server\\share"]) {
|
|
346
|
+
let winOverlay: ReturnType<typeof ensureCortexDualHomeOverlay> | undefined;
|
|
347
|
+
assert.doesNotThrow(
|
|
348
|
+
() => {
|
|
349
|
+
winOverlay = ensureCortexDualHomeOverlay({
|
|
350
|
+
scopeKey: `pi:sess-win-${winFlavored.length}`,
|
|
351
|
+
mcpServers: [],
|
|
352
|
+
realHome: winFlavored,
|
|
353
|
+
realSnowflakeHome: realSnowflake,
|
|
354
|
+
overlaysRoot,
|
|
355
|
+
isPidAlive: () => true,
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
`a win32-flavored absolute realHome (${JSON.stringify(winFlavored)}) must PASS the guard — absoluteness is judged by PATH FLAVOR, never by the host the gate runs on [QK:CORTEX-REALHOME-PLATFORM-NEUTRAL]`,
|
|
359
|
+
);
|
|
360
|
+
assert.ok(
|
|
361
|
+
winOverlay !== undefined &&
|
|
362
|
+
winOverlay.scopeDir.startsWith(overlaysRoot) &&
|
|
363
|
+
existsSync(join(winOverlay.snowflakeHome, "cortex")),
|
|
364
|
+
"the accepted win32-flavored realHome must still materialize entirely under the overlays root (the guard is the only thing the flavor cell widens)",
|
|
365
|
+
);
|
|
366
|
+
}
|
|
326
367
|
} finally {
|
|
327
368
|
rmSync(root, { recursive: true, force: true });
|
|
328
369
|
}
|
|
@@ -138,19 +138,22 @@ try {
|
|
|
138
138
|
// Set comparison (sorted, exact) — not `includes`: a substring/anchor probe
|
|
139
139
|
// cannot see a row that VANISHED, and the cortex ids contain the claude ids as
|
|
140
140
|
// substrings. Expected is derived from the two source id constants so there is
|
|
141
|
-
// one SSOT; the literal
|
|
142
|
-
// curation list
|
|
143
|
-
// together
|
|
141
|
+
// one SSOT; the literal 7 below is the independent floor that catches a
|
|
142
|
+
// curation list that SHRANK — expected derives from the same constants, so a
|
|
143
|
+
// dropped row moves expected and captured together and the set comparison
|
|
144
|
+
// stays green. That is why the count is asserted separately and carries its
|
|
145
|
+
// own claim: the cortex list has `CORTEX-CURATED-FOUR-ROWS` for exactly this
|
|
146
|
+
// failure, and the claude list needs its own oracle rather than borrowing it.
|
|
144
147
|
const expectedIds = [...SUPPORTED_ANTHROPIC_MODEL_IDS, ...SUPPORTED_CORTEX_MODEL_IDS].slice().sort();
|
|
145
148
|
assert.equal(
|
|
146
149
|
expectedIds.length,
|
|
147
|
-
|
|
148
|
-
`curated id constants must total
|
|
150
|
+
7,
|
|
151
|
+
`[QK:CLAUDE-CURATED-THREE-ROWS] curated id constants must total 7 rows (claude 3 + cortex 4) — got ${expectedIds.length}: ${expectedIds.join(", ")}`,
|
|
149
152
|
);
|
|
150
153
|
assert.deepEqual(
|
|
151
154
|
capIds.slice().sort(),
|
|
152
155
|
expectedIds,
|
|
153
|
-
`[QK:CORTEX-PROVIDER-
|
|
156
|
+
`[QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE] compiled entry must register the EXACT curated set of both adapters (claude 3 + cortex 4) — got: ${capIds.join(", ") || "none"}`,
|
|
154
157
|
);
|
|
155
158
|
assert.equal(typeof cap.cfg.streamSimple, "function", "entry streamSimple must be a function");
|
|
156
159
|
// Regression guard: the entry must wire the REAL backend (streamShellAcp),
|
|
@@ -72,7 +72,7 @@ interface WireUsage {
|
|
|
72
72
|
* One `_meta.quota.token_count` row. The field NAMES deliberately differ from
|
|
73
73
|
* `PromptResponse.usage`: cache reads are `cachedInputTokens` here because the
|
|
74
74
|
* shape is shared with codex-acp, and `cachedWriteTokens` is Claude's extra
|
|
75
|
-
* sibling (read at claude-agent-acp 0.
|
|
75
|
+
* sibling (read at claude-agent-acp 0.76.0 `dist/acp-agent.js:6566-6575`).
|
|
76
76
|
* Reading a quota row with the `usage` field names silently yields zeros, so the
|
|
77
77
|
* fixture below spells the vendor's names out rather than reusing `WireUsage`.
|
|
78
78
|
*/
|
|
@@ -137,7 +137,7 @@ function makeFakeChild() {
|
|
|
137
137
|
*
|
|
138
138
|
* Each `prompt` call consumes the next TurnScript: it first pushes that turn's
|
|
139
139
|
* `usage_update` notification (the wire the running cost total actually arrives
|
|
140
|
-
* on — read at claude-agent-acp 0.
|
|
140
|
+
* on — read at claude-agent-acp 0.76.0 `dist/acp-agent.js:3471-3486`), then answers the
|
|
141
141
|
* prompt with that turn's `PromptResponse.usage` (the wire the turn aggregate
|
|
142
142
|
* arrives on). Both orderings are the real one: the notification precedes the
|
|
143
143
|
* response, because the SDK emits it from the `result` message that ENDS the turn.
|
|
@@ -393,8 +393,8 @@ try {
|
|
|
393
393
|
// AGENT LOOP only. `_meta.quota.model_usage` comes from `result.modelUsage` and
|
|
394
394
|
// also counts Task subagents, sidechains and INTERNAL CALLS SUCH AS COMPACTION;
|
|
395
395
|
// the vendor states its rows "can total more than `token_count`" and are "the
|
|
396
|
-
// fuller picture, not a decomposition of it" (read at claude-agent-acp 0.
|
|
397
|
-
// `dist/acp-agent.js:
|
|
396
|
+
// fuller picture, not a decomposition of it" (read at claude-agent-acp 0.76.0
|
|
397
|
+
// `dist/acp-agent.js:6541-6547`).
|
|
398
398
|
//
|
|
399
399
|
// The wide one is required, not merely nicer, because the DENOMINATOR already
|
|
400
400
|
// has that scope: turn cost is the adjacent diff of the backend's running total,
|
|
@@ -608,8 +608,8 @@ try {
|
|
|
608
608
|
// identity that produces the bound is a property of the MAIN AGENT LOOP's
|
|
609
609
|
// cache breakpoints. `_meta.quota.model_usage` is a WIDER scope: the vendor
|
|
610
610
|
// states those rows also count Task subagents, sidechains, and INTERNAL
|
|
611
|
-
// CALLS SUCH AS COMPACTION (read at claude-agent-acp 0.
|
|
612
|
-
// `dist/acp-agent.js:
|
|
611
|
+
// CALLS SUCH AS COMPACTION (read at claude-agent-acp 0.76.0
|
|
612
|
+
// `dist/acp-agent.js:6538-6558`). Mixing the two scopes inflates the bound
|
|
613
613
|
// through both remaining terms that mention cacheWrite:
|
|
614
614
|
// max(0, occupancy − cacheWrite) shrinks as wide cacheWrite grows, so
|
|
615
615
|
// less is subtracted; min(rawBound, cacheWrite) rises with it.
|
|
@@ -749,9 +749,9 @@ try {
|
|
|
749
749
|
// CELL 3 — a turn with NO cost notification holds the baseline.
|
|
750
750
|
//
|
|
751
751
|
// Measured upstream: the result-path `usage_update` carries cost (read at
|
|
752
|
-
// claude-agent-acp 0.
|
|
752
|
+
// claude-agent-acp 0.76.0 `dist/acp-agent.js:3471-3486`), while other
|
|
753
753
|
// `usage_update` paths can carry `used` without cost (for example the
|
|
754
|
-
// rate-limit path at `:
|
|
754
|
+
// rate-limit path at `:4273-4281`). A live thinkpad ledger shows such turns
|
|
755
755
|
// really occur. The honest handling is to HOLD the baseline so the amount lands
|
|
756
756
|
// in the NEXT diff: misattributed by turn, exact by session. Rebaselining to 0
|
|
757
757
|
// there would double-count the whole prefix.
|
|
@@ -831,7 +831,7 @@ try {
|
|
|
831
831
|
// CELL 4 — a DECREASING session total is never silently absorbed.
|
|
832
832
|
//
|
|
833
833
|
// `conversation_reset` switches the session to a fresh transcript (read at
|
|
834
|
-
// claude-agent-acp 0.
|
|
834
|
+
// claude-agent-acp 0.76.0 `dist/acp-agent.js:4286-4293`), but whether that
|
|
835
835
|
// changes `total_cost_usd` is an SDK-internal value we cannot observe here.
|
|
836
836
|
// A diff can therefore go negative in a session we are still holding.
|
|
837
837
|
// Absorbing it quietly would both misreport the turn and destroy the only
|
|
@@ -52,6 +52,7 @@ import { resolveFactList } from "../pi-extensions/lib/entwurf-facts.ts";
|
|
|
52
52
|
import { nativePushSupported } from "../pi-extensions/lib/entwurf-v2-contract.ts";
|
|
53
53
|
import { META_SENDER_BACKENDS, resolveTrustedMetaSenderIdentity } from "../pi-extensions/lib/meta-sender-identity.ts";
|
|
54
54
|
import { listAllMetaIdentitiesDir, processStartKey } from "../pi-extensions/lib/meta-session.ts";
|
|
55
|
+
import { reclaimOnExit } from "./lib/reclaim-on-exit.ts";
|
|
55
56
|
|
|
56
57
|
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
57
58
|
const PLUGIN = "entwurf-meta-receive-copilot";
|
|
@@ -63,7 +64,8 @@ function ok(label: string, cond: boolean): void {
|
|
|
63
64
|
passed++;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const root = mkdtempSync(path.join(tmpdir(), "entwurf-copilot-birth."));
|
|
67
|
+
const root = reclaimOnExit(mkdtempSync(path.join(tmpdir(), "entwurf-copilot-birth.")));
|
|
68
|
+
|
|
67
69
|
const asm = path.join(root, "asm");
|
|
68
70
|
|
|
69
71
|
// ── 1. the real assembler ────────────────────────────────────────────────────
|