@junghanacs/entwurf 0.16.0 → 0.16.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/CHANGELOG.md +125 -0
- package/README.md +8 -11
- package/demo/README.md +1 -1
- package/docs/setup-clean-host.md +24 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +19 -10
- package/package.json +8 -8
- package/pi-extensions/lib/acp/backend-adapter.ts +19 -9
- package/pi-extensions/lib/acp/backend.ts +125 -7
- package/pi-extensions/lib/acp/claude-acp-launch.js +100 -0
- package/run.sh +108 -32
- package/scripts/check-acp-launch-namespace.ts +127 -0
- package/scripts/check-acp-prompt-lifecycle.ts +145 -2
- package/scripts/check-copilot-birth-hook.ts +28 -1
- package/scripts/check-gate-qualification.ts +3 -2
- package/scripts/check-omp-fresh-preflight.ts +27 -0
- package/scripts/check-setup-qualification.sh +40 -2
- package/scripts/copilot-bridge-oracle.sh +14 -6
- package/scripts/fake-copilot-vendor.sh +4 -2
- package/scripts/mutants/acp-launch-namespace.json +34 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +67 -2
- package/scripts/mutants/copilot-birth.json +3 -5
- package/scripts/mutants/pack-install.json +2 -2
- package/scripts/mutants/setup-verdict.json +35 -0
- package/scripts/omp-config-xdev.py +310 -0
- package/scripts/omp-config-xdev.sh +76 -0
- package/scripts/omp-tool-surface.py +61 -10
- package/scripts/raw-acp-child-exit-measure/README.md +285 -0
- package/scripts/raw-acp-child-exit-measure/acp-turn-population.py +89 -0
- package/scripts/raw-acp-child-exit-measure/reaper-correlation.py +47 -0
- package/scripts/smoke-setup-verdict.sh +48 -3
package/AGENTS.md
CHANGED
|
@@ -182,7 +182,7 @@ implement → affected focused gates → independent review → one amendment bu
|
|
|
182
182
|
- Every `.ts` file belongs to one typecheck fence: root emit-capable config, MCP strip-types config, or scripts strip-types config. Do not hide files with `exclude`.
|
|
183
183
|
- Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
|
|
184
184
|
- MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
|
|
185
|
-
- pi runtime range is `>=0.84.
|
|
185
|
+
- pi runtime range is `>=0.84.4 <0.85` with devDep exact `0.84.4`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
|
|
186
186
|
- ACP pins are recorded in `package.json` and checked by `check-dep-versions`/`check-acp-sdk-surface`; do not describe a dependency bump as a behavioral fix without evidence.
|
|
187
187
|
|
|
188
188
|
## Working Style
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,131 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.16.1 - 2026-09-01
|
|
8
|
+
|
|
9
|
+
### Verification
|
|
10
|
+
|
|
11
|
+
Each receipt carries its own scope; none of them is transferable to another commit or host.
|
|
12
|
+
|
|
13
|
+
- **`pnpm run check:full`** — PASS, exit 0, **454s** inside the gate, on the 0.16.1
|
|
14
|
+
versioned tree (HEAD `2e7ceb4` + the uncommitted changelog/version edits).
|
|
15
|
+
- **`./run.sh check-gate-qualification`** — **335/335 KILLED, NOT KILLED 0**, as the
|
|
16
|
+
release-gate MUST step. The first run of this cut reported **334/335**: the single
|
|
17
|
+
miss was `PACK-INSTALL-PIN-MATCHER-BOUNDED` reading `MUTANT-STALE` because `5c1bda5`
|
|
18
|
+
moved `run.sh`'s pin matcher to the 0.84.4 floor and left that mutant's anchor on
|
|
19
|
+
0.84.3, so it matched 0× and the gate had silently stopped asking its question.
|
|
20
|
+
Repaired in `2e7ceb4` and re-run green. A rotted anchor is recorded here rather than
|
|
21
|
+
quietly fixed, because "N killed" only means something when N is the whole set.
|
|
22
|
+
- **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.16.1.m0LUjr --cut`** —
|
|
23
|
+
**MUST: PASS=23 FAIL=0 SKIP=0**, **BEHAVIOR: PASS=1 FAIL=0 SKIP=0**. Log at
|
|
24
|
+
`/tmp/entwurf-release-gate-0.16.1.m0LUjr/release-gate.log`. `smoke-mux-lifecycle-live`
|
|
25
|
+
failed the first run on a pi-native nonce callback that never arrived inside its 300s
|
|
26
|
+
window (the codex rail was measured healthy at the time: 5h 23%, weekly 77%), and
|
|
27
|
+
passed on re-run — recorded because a LIVE step that needed a retry is not the same
|
|
28
|
+
evidence as one that passed first time.
|
|
29
|
+
- **#72 launch/observation receipts** — the launcher's two field claims are not
|
|
30
|
+
unit-provable, so they were measured on the host that produced the failure:
|
|
31
|
+
`/proc/<pid>/cmdline` carries no vendor name, the janitor's own Phase 1 selector run
|
|
32
|
+
against the real `ps` row with its age threshold forced to 0 selects nothing, and a
|
|
33
|
+
real SIGTERM still ends at exit 0 with the frame on stderr. Commands and output in
|
|
34
|
+
`scripts/raw-acp-child-exit-measure/README.md`.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **`setup` composes OMP — the fifth backend is now in the one command.** v0.16.0 admitted OMP
|
|
39
|
+
as a D6 citizen with installers, doctors and inverses for every unit, but `setup_all` had no
|
|
40
|
+
`omp` branch: on an operator host the released package printed a **green** setup summary while
|
|
41
|
+
OMP had nothing installed — no extension, no `mcp.json` entry, no garden id on the status line.
|
|
42
|
+
Presence-driven now, same shape as Copilot: `omp` on PATH composes four independent named
|
|
43
|
+
components (birth → MCP hand → the `tools.xdev` operator setting → receiver), absent is one
|
|
44
|
+
zero-state SKIP, and a detected-but-incomplete unit is a named FAIL owning a nonzero exit.
|
|
45
|
+
Gate: `smoke-setup-verdict` S-8 drives the real composition against a stub vendor and asserts
|
|
46
|
+
every install-state, both extension units in the sandbox agent dir, the effective `xdev-off`
|
|
47
|
+
reading, and idempotence on a second run; S-1 pins `OMP_BIN` absent and requires the SKIP row.
|
|
48
|
+
- **`install-omp-config` / `uninstall-omp-config` — the operator setting is a unit, not a
|
|
49
|
+
documentation step.** `tools: xdev: false` was a hand-edit in `docs/setup-clean-host.md`, so a
|
|
50
|
+
host that skipped it registered every entwurf tool behind the vendor's `xd://` wrapper and the
|
|
51
|
+
doorbell announced a tool the model could not call. The writer owns exactly the lines it adds
|
|
52
|
+
(recorded in its install-state), refuses a symlinked config, refuses a config it cannot parse,
|
|
53
|
+
and refuses an EXPLICIT operator `tools: xdev: true` **by name** rather than overwriting a
|
|
54
|
+
decision — setup surfaces that as a component FAIL for the operator to resolve. The inverse
|
|
55
|
+
takes back only the recorded lines and refuses when the file changed since install.
|
|
56
|
+
- **`docs/adding-a-harness.md` step 10 — an onboarding is not finished until `setup` composes
|
|
57
|
+
it.** The rule the OMP gap forced, with the five conditions a new harness owes (presence-only
|
|
58
|
+
trigger, one independent component row per unit, compose only what an inverse can undo,
|
|
59
|
+
operator settings are units with a refusing writer, pin the probe seam and add an aggregate
|
|
60
|
+
cell) and the shape to watch for: two closed parity loops with no gate owning the edge between
|
|
61
|
+
them.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **pi floor moved to 0.84.4.** devDependency exact pin, the three peer ranges (`>=0.84.4 <0.85`),
|
|
66
|
+
the `check-pack-install` runtime pins, the pin-leak matcher's version boundary with its synthetic
|
|
67
|
+
fixture, and the pnpm lockfile all move together — the floor is one fact with several spellings,
|
|
68
|
+
and a partial bump is how they drift apart.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- **#72 — the ACP Claude child is no longer selected by the host janitor that was killing it, and
|
|
73
|
+
a caught signal survives the vendor erasing it.** The child was not crashing: it was being **SIGTERMed from outside**. A
|
|
74
|
+
janitor installed on the operator's host for a *different* harness (openclaw's acpx, upstream
|
|
75
|
+
PR #245) selects the vendor process name `claude-agent-acp` by **argv substring** and kills
|
|
76
|
+
anything older than 900s. entwurf **retains** its child across turns, so that child's age is the
|
|
77
|
+
age of the SESSION, not of a turn — every session past 15 minutes was shot at every 5 minutes.
|
|
78
|
+
The vendor's own handler then turned the signal into `dispose(); process.exit(0)`, so the death
|
|
79
|
+
reached entwurf as exit code 0 with no signal, indistinguishable from a clean shutdown. That is
|
|
80
|
+
why three diagnosis passes retired four candidate causes and still missed it. Measured: **12 of
|
|
81
|
+
12** anomalous terminations across two boots correlate with a reap, pid- and timestamp-locked —
|
|
82
|
+
including the sample that opened the issue (2026-07-30) and the original field report
|
|
83
|
+
(2026-08-16); receipts in `scripts/raw-acp-child-exit-measure/`. Two changes, both inside the
|
|
84
|
+
issue's repair fence (no timeout, no replay, no watcher/supervisor/hidden retry): (a) the default
|
|
85
|
+
launch is now an **entwurf-owned launcher** (`pi-extensions/lib/acp/claude-acp-launch.js`) that
|
|
86
|
+
imports the vendor **in-process** — same process, so there is no child to restart and it cannot
|
|
87
|
+
become a supervisor — carrying a name no vendor-name scanner matches, consuming no argv (the
|
|
88
|
+
vendor's own `argv.slice(1)` self-reinvocation still works), standing down if it would otherwise
|
|
89
|
+
be the only signal listener (an observer that suppressed default termination would make the
|
|
90
|
+
process signal-immune), and dying nonzero without retry if the import fails; (b) a caught
|
|
91
|
+
SIGTERM/SIGINT is recorded as a **typed lifecycle fact** on its own line
|
|
92
|
+
(`launch observed SIGTERM before child exit (sender not attributed)`) via an exact full-line
|
|
93
|
+
control frame that is consumed out of the vendor stderr tail — the tail stays vendor evidence,
|
|
94
|
+
the observation is entwurf's, and sender attribution is explicitly NOT claimed (that needs the
|
|
95
|
+
host journal). `CLAUDE_AGENT_ACP_COMMAND` stays verbatim: an operator who names their own command
|
|
96
|
+
owns the result. **Known trade:** after the name split a host janitor can no longer collect
|
|
97
|
+
entwurf's children even when they genuinely leak, so entwurf now owns that cleanup story.
|
|
98
|
+
**Read the order right: the cause was the host's janitor, and its owner has retired it**
|
|
99
|
+
(`nixos-config e283d92`, 2026-09-01 — `acp-zombie-reaper.timer` disabled). Measured there:
|
|
100
|
+
PR #245 is CLOSED, not merged; that deployment runs `acp.enabled=false` with no ACP agent;
|
|
101
|
+
acpx is not even installed in the running image (`/app/node_modules/acpx` absent, its `.bin`
|
|
102
|
+
entries dangling symlinks); and every reap since the 2026-06-10 ACP removal was a single-process
|
|
103
|
+
misfire matching our pid table. So the change here is **not what stops the symptom** — the
|
|
104
|
+
retirement is. It is defense-in-depth against the class, and that class is wider than this
|
|
105
|
+
issue: the selector reads the WHOLE `ps` line, so on a harness that puts the prompt in argv, a
|
|
106
|
+
session merely *discussing* `claude-agent-acp` matched. Measured 2026-09-01 with zero ACP
|
|
107
|
+
processes alive, the script's own counter read `alive_acp=3` — three ordinary agent sessions,
|
|
108
|
+
one of them the session investigating it. A launcher whose name we own survives the next such
|
|
109
|
+
janitor too. Gates:
|
|
110
|
+
new `check-acp-launch-namespace` (2 mutants) plus `check-acp-prompt-lifecycle` CELLs 12–13,
|
|
111
|
+
where 13 is the negative sibling holding that vendor prose mentioning `SIGTERM` can never forge
|
|
112
|
+
the observation (3 mutants). The primary repair still belongs to the janitor's owner — a cleaner
|
|
113
|
+
should scope by a positive marker it owns, not by a name anyone may share.
|
|
114
|
+
|
|
115
|
+
- **A vendor-written `config.yml` no longer reads as `unreadable`.** omp's own settings writer
|
|
116
|
+
emits `modelRoles:` followed by an indented `{}`, and `scripts/omp-tool-surface.py`'s
|
|
117
|
+
block-only reader returned None for the WHOLE file on that shape — so an untouched operator
|
|
118
|
+
config classified as unreadable and `doctor-omp-mcp` went RED for a reason unrelated to
|
|
119
|
+
`tools.xdev` (measured on a real host, omp 18.0.0). The reader now parses flow collections in
|
|
120
|
+
value position and as a whole child block. `check-omp-fresh-preflight` gains the vendor shapes
|
|
121
|
+
plus `[QK:OMP-XDEV-VENDOR-SHAPE-READABLE]` direct assertions — agreement between the two
|
|
122
|
+
readers could never catch this, since both collapse `unreadable` and `true` into "not false".
|
|
123
|
+
- **The Copilot plugin-row grammar admits the vendor's state token.** Copilot CLI 1.0.81 prints
|
|
124
|
+
`<qualified> (v0.1.0) (enabled)` plus an indented `from <path>` continuation line;
|
|
125
|
+
`copilot_exact_row_version` read the version as `0.1.0) (enabled`, called the row malformed,
|
|
126
|
+
and refused the birth install on a host whose plugin was installed and enabled — `entwurf
|
|
127
|
+
setup` reported `copilot-birth: FAIL`. The grammar now admits exactly one optional
|
|
128
|
+
`(enabled)`/`(disabled)` token; any other trailing token is still malformed. The fake vendors
|
|
129
|
+
in `scripts/fake-copilot-vendor.sh` and `check-copilot-birth-hook.ts` emit the 1.0.81 shape,
|
|
130
|
+
with `[QK:COPILOT-ROW-STATE-TOKEN-ADMITTED]` holding both directions.
|
|
131
|
+
|
|
7
132
|
## 0.16.0 - 2026-08-31
|
|
8
133
|
|
|
9
134
|
This release admits **OMP (`omp`) as the fifth garden backend** and closes the admission
|
package/README.md
CHANGED
|
@@ -60,8 +60,8 @@ native Antigravity / agy
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Claude's `install-meta-bridge`, Copilot's four `install-copilot-*` surfaces, agy's
|
|
63
|
-
`install-agy-{bridge,statusline,hooks}` and OMP's
|
|
64
|
-
|
|
63
|
+
`install-agy-{bridge,statusline,hooks}` and OMP's four `install-omp-{bridge,mcp,config,receive}`
|
|
64
|
+
units are distinct because their lifecycle and delivery transports are
|
|
65
65
|
genuinely different. Codex remains verified probe evidence, not a shipped managed native-citizen
|
|
66
66
|
lane; see [DELIVERY.md](./DELIVERY.md).
|
|
67
67
|
|
|
@@ -157,7 +157,7 @@ cd ~/repos/gh/entwurf
|
|
|
157
157
|
|
|
158
158
|
The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are
|
|
159
159
|
optional-by-presence: `setup` runs the frozen dependency install, then composes what the operator
|
|
160
|
-
already installed — a compatible `pi` (`>=0.84.
|
|
160
|
+
already installed — a compatible `pi` (`>=0.84.4 <0.85`), Claude Code, agy, and the Copilot CLI
|
|
161
161
|
each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP,
|
|
162
162
|
and a detected harness that cannot be completed (including a below-floor `pi`) is a named FAIL
|
|
163
163
|
that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
|
|
@@ -188,7 +188,7 @@ gitignored and may be absent or stale immediately after a checkout or pull.
|
|
|
188
188
|
### Pi adapter / ACP plugin lane
|
|
189
189
|
|
|
190
190
|
To use the `entwurf` provider inside pi, install a compatible pi binary
|
|
191
|
-
separately (`@earendil-works/pi-coding-agent >=0.84.
|
|
191
|
+
separately (`@earendil-works/pi-coding-agent >=0.84.4 <0.85`). Then point pi at
|
|
192
192
|
the npm-installed package or development clone:
|
|
193
193
|
|
|
194
194
|
```bash
|
|
@@ -213,17 +213,14 @@ SKIP while the detected harnesses are composed.
|
|
|
213
213
|
|
|
214
214
|
A plain MCP registration exposes the bridge tools; a **garden-native** session also
|
|
215
215
|
needs entwurf's lifecycle hook and identity marker. `setup` already composes all of that
|
|
216
|
-
for every harness it detects — you do not paste this list to install.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
wired. Otherwise this is the repair surface: each unit has its own installer, its own
|
|
220
|
-
doctor with a named refusal, and its own inverse, so a single broken unit can be redone
|
|
221
|
-
without touching the rest.
|
|
216
|
+
for every harness it detects — you do not paste this list to install. This is the repair
|
|
217
|
+
surface: each unit has its own installer, its own doctor with a named refusal, and its own
|
|
218
|
+
inverse, so a single broken unit can be redone without touching the rest.
|
|
222
219
|
|
|
223
220
|
- **Claude Code** (Linux-certified axis) — `install-meta-bridge`, `doctor-meta-bridge`.
|
|
224
221
|
- **Antigravity / agy** — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
|
|
225
222
|
- **GitHub Copilot CLI** — four independent units, four independent failure modes: `install-copilot-bridge` (birth: garden id + who-sent, on the first prompt), `install-copilot-mcp` (the entwurf tool hand, where `entwurf_inbox_read` lives), `install-copilot-receive` (the receiver extension: doorbell + receiver marker), `install-copilot-statusline` (optional for a manual citizen, required for supported fresh) — each with a matching `doctor-copilot-*` and `uninstall-copilot-*`.
|
|
226
|
-
- **OMP (`omp`)** —
|
|
223
|
+
- **OMP (`omp`)** — four units, in-process extensions rather than launchers: `install-omp-bridge` (birth: the `mode === "tui"` visible host, its garden id on the status line, and who-sent), `install-omp-mcp` (the omp-native `entwurf-bridge` entry), `install-omp-config` (the one operator setting `tools: xdev: false`, without which the vendor mounts MCP tools as `xd://` devices the model cannot call), `install-omp-receive` (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching `uninstall-omp-*`, and a `doctor-omp-*` for all but the setting, whose runtime axis `doctor-omp-mcp` owns. The setting writer owns exactly the lines it adds and refuses an explicit operator `tools: xdev: true` by name rather than overwriting it.
|
|
227
224
|
|
|
228
225
|
Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
|
|
229
226
|
walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
|
package/demo/README.md
CHANGED
|
@@ -139,7 +139,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
|
|
|
139
139
|
|
|
140
140
|
## Prerequisites
|
|
141
141
|
|
|
142
|
-
- `pi` on PATH (current floor 0.84.
|
|
142
|
+
- `pi` on PATH (current floor 0.84.4)
|
|
143
143
|
- `entwurf` provider configured + auth ready for the selected sender/peer models
|
|
144
144
|
- `asciinema` installed
|
|
145
145
|
- `agg` installed (optional — only for GIF conversion)
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -12,10 +12,10 @@ only on Linux because its strict live-owner join uses `/proc`.
|
|
|
12
12
|
| npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
|
|
13
13
|
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot wiring writers |
|
|
14
14
|
| entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
|
|
15
|
-
| pi | optional-by-presence, `>=0.84.
|
|
15
|
+
| pi | optional-by-presence, `>=0.84.4 <0.85` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
|
|
16
16
|
| Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
|
|
17
17
|
| GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
|
|
18
|
-
| OMP (`omp`) | optional, operator-installed;
|
|
18
|
+
| OMP (`omp`) | optional-by-presence, operator-installed — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/`tools.xdev` setting/receiver) | self-fetch citizen and visible fresh (accepted on one host — see §4b) |
|
|
19
19
|
| Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
|
|
20
20
|
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
21
21
|
|
|
@@ -72,11 +72,12 @@ cd ~/repos/gh/entwurf
|
|
|
72
72
|
|
|
73
73
|
This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
|
|
74
74
|
link is foreign, outside PATH, or shadowed by another command. It detects and wires
|
|
75
|
-
pi/Claude/agy/Copilot by presence and prints a computed per-component PASS/SKIP/FAIL summary — a
|
|
75
|
+
pi/Claude/agy/Copilot/OMP by presence and prints a computed per-component PASS/SKIP/FAIL summary — a
|
|
76
76
|
detected harness that cannot be completed makes setup exit nonzero. A detected `copilot`
|
|
77
77
|
composes all four native units (birth → MCP → receiver → visible footer) with independent
|
|
78
|
-
per-unit verdicts (#86 C3b)
|
|
79
|
-
|
|
78
|
+
per-unit verdicts (#86 C3b), and a detected `omp` composes its own four (birth → MCP →
|
|
79
|
+
`tools.xdev` setting → receiver) the same way; §4 and §4b keep the explicit per-unit
|
|
80
|
+
install/doctor/inverse surfaces for repair.
|
|
80
81
|
|
|
81
82
|
### 1.1 User-scope ownership (one shared registration, one recorded owner)
|
|
82
83
|
|
|
@@ -109,7 +110,7 @@ packageRoot↔installerRoot coupling mismatch and a package/provider managed-pat
|
|
|
109
110
|
Install the exact release floor, then wire the project:
|
|
110
111
|
|
|
111
112
|
```bash
|
|
112
|
-
npm install -g @earendil-works/pi-coding-agent@0.84.
|
|
113
|
+
npm install -g @earendil-works/pi-coding-agent@0.84.4
|
|
113
114
|
pi --version
|
|
114
115
|
|
|
115
116
|
cd ~/entwurf-smoke
|
|
@@ -117,7 +118,7 @@ entwurf install .
|
|
|
117
118
|
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
The supported range is `>=0.84.
|
|
121
|
+
The supported range is `>=0.84.4 <0.85`. It is a hard minimum: installing this
|
|
121
122
|
release onto a 0.83.x pi host upgrades the runtime rather than keeping the older
|
|
122
123
|
minor. A host using only the external MCP bridge can skip pi until it needs a
|
|
123
124
|
control socket; no delivery rail launches a pi process.
|
|
@@ -213,16 +214,26 @@ sibling launched onto a default-config host would start, look healthy, and be un
|
|
|
213
214
|
the callback tool at all. A refusal there names `omp-callback-tool-uncallable` and opens
|
|
214
215
|
nothing.
|
|
215
216
|
|
|
217
|
+
`setup` composes all four omp units when `omp` is on PATH, and the setting is one of them —
|
|
218
|
+
`entwurf setup` writes `tools: xdev: false` itself. The verbs below are the REPAIR path, not
|
|
219
|
+
the install:
|
|
220
|
+
|
|
216
221
|
```bash
|
|
217
222
|
entwurf install-omp-bridge # the birth extension, into <omp agent dir>/extensions/
|
|
218
223
|
entwurf install-omp-mcp # the omp-native entwurf-bridge server
|
|
224
|
+
entwurf install-omp-config # the operator setting: tools.xdev: false
|
|
219
225
|
entwurf install-omp-receive # the receiver extension: mailbox watch + doorbell
|
|
220
226
|
|
|
221
227
|
entwurf doctor-omp-bridge
|
|
222
|
-
entwurf doctor-omp-mcp
|
|
228
|
+
entwurf doctor-omp-mcp # also owns the tools.xdev runtime axis
|
|
223
229
|
entwurf doctor-omp-receive
|
|
224
230
|
```
|
|
225
231
|
|
|
232
|
+
The setting writer owns exactly the lines it adds and records them, so `uninstall-omp-config`
|
|
233
|
+
takes back its own bytes and nothing else. It refuses a symlinked config, a config it cannot
|
|
234
|
+
parse, and — deliberately — an EXPLICIT `tools: xdev: true`: that is your decision, not drift,
|
|
235
|
+
so setup names it as a component FAIL for you to resolve instead of overwriting it.
|
|
236
|
+
|
|
226
237
|
Order matters only in one direction: the receiver JOINS the citizen birth mints, and
|
|
227
238
|
announces a tool the MCP hand provides. Install it without them and it will log
|
|
228
239
|
`arm-deferred`, give up after ~20s, and `doctor-omp-receive` will name the missing sibling
|
|
@@ -234,8 +245,11 @@ retires it. A task subagent arms nothing. While nothing is armed, dispatch to th
|
|
|
234
245
|
id is the honest `mailbox-undeliverable` refusal — an unarmed receiver is a legible state,
|
|
235
246
|
not a broken one.
|
|
236
247
|
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
`[측정]` This section used to end by saying `setup` did not compose these and the verbs had to
|
|
249
|
+
be run by hand. That is what v0.16.0 actually shipped, and on an operator host it printed a
|
|
250
|
+
green `setup` summary with OMP entirely absent — no extension, no MCP entry, no visible garden
|
|
251
|
+
id. The composition landed afterwards; `docs/adding-a-harness.md` step 10 is the rule that
|
|
252
|
+
keeps the next harness from repeating it.
|
|
239
253
|
|
|
240
254
|
Both installers resolve the omp agent directory the way omp itself does, and REFUSE rather
|
|
241
255
|
than guess when an inherited `PI_CODING_AGENT_DIR`, `PI_CONFIG_DIR` or `PI_PROFILE` makes it
|
|
@@ -17,10 +17,9 @@
|
|
|
17
17
|
//
|
|
18
18
|
// Fence: imported by the root program with `.js` suffixes, same as the sibling
|
|
19
19
|
// lib/acp modules — no new strip-types fence.
|
|
20
|
-
import { readFileSync } from "node:fs";
|
|
21
20
|
import { createRequire } from "node:module";
|
|
22
21
|
import { homedir } from "node:os";
|
|
23
|
-
import {
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
24
23
|
import { enrichMcpServersWithEnvelope } from "./config.js";
|
|
25
24
|
import { loadEngraving } from "./engraving.js";
|
|
26
25
|
import { CORTEX_MODEL_PREFIX, curatedClaudeModels, curatedCortexModels, SUPPORTED_ANTHROPIC_MODEL_IDS, SUPPORTED_CORTEX_MODEL_IDS, } from "./models.js";
|
|
@@ -39,19 +38,29 @@ function shellQuote(value) {
|
|
|
39
38
|
// claude adapter — the first implementation (the rail's reference backend)
|
|
40
39
|
// ---------------------------------------------------------------------------
|
|
41
40
|
const SUPPORTED_CLAUDE_IDS = new Set(SUPPORTED_ANTHROPIC_MODEL_IDS);
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the claude launch — an ENTWURF-OWNED launcher, or the env override for debug.
|
|
43
|
+
* This is the single source for the claude launch spec; backend.ts holds no private copy.
|
|
44
|
+
*
|
|
45
|
+
* The default no longer names the vendor bin directly. `claude-acp-launch.js`
|
|
46
|
+
* imports it in-process; that file's header carries the reason (#72: a janitor
|
|
47
|
+
* for another harness selects `claude-agent-acp` by argv substring and SIGTERMs
|
|
48
|
+
* it by age, and the vendor's own handler erases the signal into exit 0).
|
|
49
|
+
*
|
|
50
|
+
* `CLAUDE_AGENT_ACP_COMMAND` is an EXPLICIT operator override and is deliberately
|
|
51
|
+
* NOT routed through the launcher: an operator who names their own command owns
|
|
52
|
+
* the result, including the loss of the name split and the signal observation.
|
|
53
|
+
*/
|
|
44
54
|
function resolveClaudeLaunch() {
|
|
45
55
|
const override = process.env.CLAUDE_AGENT_ACP_COMMAND?.trim();
|
|
46
56
|
if (override)
|
|
47
57
|
return { command: "bash", args: ["-lc", override] };
|
|
58
|
+
// Resolved here (not inside the launcher's own directory lookup) so a missing
|
|
59
|
+
// vendor package still fails at launch resolution, where it always failed.
|
|
48
60
|
const require = createRequire(import.meta.url);
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
if (!binPath)
|
|
53
|
-
throw new Error("@agentclientprotocol/claude-agent-acp resolved but exposes no bin entry");
|
|
54
|
-
return { command: process.execPath, args: [join(dirname(pkgJsonPath), binPath)] };
|
|
61
|
+
require.resolve("@agentclientprotocol/claude-agent-acp/package.json");
|
|
62
|
+
const launcher = fileURLToPath(new URL("./claude-acp-launch.js", import.meta.url));
|
|
63
|
+
return { command: process.execPath, args: [launcher] };
|
|
55
64
|
}
|
|
56
65
|
export const claudeAdapter = {
|
|
57
66
|
backend: "claude",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Garden-citizen dispatch substrate and meta-bridge for Claude Code, Codex, Antigravity, and pi harnesses.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -89,16 +89,16 @@
|
|
|
89
89
|
"zod": "^3.25.0 || ^4.0.0"
|
|
90
90
|
},
|
|
91
91
|
"peerDependencies": {
|
|
92
|
-
"@earendil-works/pi-ai": ">=0.84.
|
|
93
|
-
"@earendil-works/pi-coding-agent": ">=0.84.
|
|
94
|
-
"@earendil-works/pi-tui": ">=0.84.
|
|
92
|
+
"@earendil-works/pi-ai": ">=0.84.4 <0.85",
|
|
93
|
+
"@earendil-works/pi-coding-agent": ">=0.84.4 <0.85",
|
|
94
|
+
"@earendil-works/pi-tui": ">=0.84.4 <0.85",
|
|
95
95
|
"typebox": "*"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@biomejs/biome": "^2.4.13",
|
|
99
|
-
"@earendil-works/pi-ai": "0.84.
|
|
100
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
101
|
-
"@earendil-works/pi-tui": "0.84.
|
|
99
|
+
"@earendil-works/pi-ai": "0.84.4",
|
|
100
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
101
|
+
"@earendil-works/pi-tui": "0.84.4",
|
|
102
102
|
"@types/node": "^24.3.0",
|
|
103
103
|
"husky": "^9.1.7",
|
|
104
104
|
"rregex": "1.13.1",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"check:toolchain": "pnpm lint && pnpm typecheck",
|
|
166
166
|
"check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface",
|
|
167
167
|
"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-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-resume-args && ./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-entwurf-facts && ./run.sh check-control-socket-path && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./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",
|
|
168
|
-
"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-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-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-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-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-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex",
|
|
168
|
+
"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-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-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-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-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-launch-namespace && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex",
|
|
169
169
|
"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"
|
|
170
170
|
},
|
|
171
171
|
"pi": {
|
|
@@ -18,10 +18,9 @@
|
|
|
18
18
|
// Fence: imported by the root program with `.js` suffixes, same as the sibling
|
|
19
19
|
// lib/acp modules — no new strip-types fence.
|
|
20
20
|
|
|
21
|
-
import { readFileSync } from "node:fs";
|
|
22
21
|
import { createRequire } from "node:module";
|
|
23
22
|
import { homedir } from "node:os";
|
|
24
|
-
import {
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
25
24
|
|
|
26
25
|
import type { AcpConnectionLike } from "./acp-client.js";
|
|
27
26
|
import { enrichMcpServersWithEnvelope, type ResolvedAcpConfig } from "./config.js";
|
|
@@ -202,17 +201,28 @@ export interface AcpBackendAdapter {
|
|
|
202
201
|
|
|
203
202
|
const SUPPORTED_CLAUDE_IDS: ReadonlySet<string> = new Set(SUPPORTED_ANTHROPIC_MODEL_IDS);
|
|
204
203
|
|
|
205
|
-
/**
|
|
206
|
-
*
|
|
204
|
+
/**
|
|
205
|
+
* Resolve the claude launch — an ENTWURF-OWNED launcher, or the env override for debug.
|
|
206
|
+
* This is the single source for the claude launch spec; backend.ts holds no private copy.
|
|
207
|
+
*
|
|
208
|
+
* The default no longer names the vendor bin directly. `claude-acp-launch.js`
|
|
209
|
+
* imports it in-process; that file's header carries the reason (#72: a janitor
|
|
210
|
+
* for another harness selects `claude-agent-acp` by argv substring and SIGTERMs
|
|
211
|
+
* it by age, and the vendor's own handler erases the signal into exit 0).
|
|
212
|
+
*
|
|
213
|
+
* `CLAUDE_AGENT_ACP_COMMAND` is an EXPLICIT operator override and is deliberately
|
|
214
|
+
* NOT routed through the launcher: an operator who names their own command owns
|
|
215
|
+
* the result, including the loss of the name split and the signal observation.
|
|
216
|
+
*/
|
|
207
217
|
function resolveClaudeLaunch(): AcpLaunchSpec {
|
|
208
218
|
const override = process.env.CLAUDE_AGENT_ACP_COMMAND?.trim();
|
|
209
219
|
if (override) return { command: "bash", args: ["-lc", override] };
|
|
220
|
+
// Resolved here (not inside the launcher's own directory lookup) so a missing
|
|
221
|
+
// vendor package still fails at launch resolution, where it always failed.
|
|
210
222
|
const require = createRequire(import.meta.url);
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
if (!binPath) throw new Error("@agentclientprotocol/claude-agent-acp resolved but exposes no bin entry");
|
|
215
|
-
return { command: process.execPath, args: [join(dirname(pkgJsonPath), binPath)] };
|
|
223
|
+
require.resolve("@agentclientprotocol/claude-agent-acp/package.json");
|
|
224
|
+
const launcher = fileURLToPath(new URL("./claude-acp-launch.js", import.meta.url));
|
|
225
|
+
return { command: process.execPath, args: [launcher] };
|
|
216
226
|
}
|
|
217
227
|
|
|
218
228
|
export const claudeAdapter: AcpBackendAdapter = {
|