@junghanacs/entwurf 0.20.1 → 0.21.0
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 +85 -190
- package/BASELINE.md +4 -3
- package/CHANGELOG.md +195 -14
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +202 -60
- package/README.md +49 -22
- package/VERIFY.md +37 -7
- package/docs/acp-backend-rail.md +0 -1
- package/docs/external-mcp-host.md +54 -33
- package/docs/setup-clean-host.md +110 -17
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +101 -109
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +326 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +7 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +50 -15
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
- package/mcp/entwurf-bridge/src/index.ts +115 -111
- package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
- package/package.json +13 -4
- package/pi-extensions/entwurf-control.ts +71 -19
- package/pi-extensions/lib/codex-fresh-preflight.ts +363 -0
- package/pi-extensions/lib/compaction-send-guard.ts +80 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
- package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
- package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
- package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
- package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
- package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
- package/pi-extensions/lib/meta-sender-identity.ts +305 -0
- package/pi-extensions/lib/mux-fresh-call.ts +64 -19
- package/pi-extensions/lib/native-push/adapter.ts +21 -24
- package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
- package/pi-extensions/lib/native-push/register.ts +7 -9
- package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
- package/run.sh +218 -28
- package/scripts/check-agy-sender-identity.ts +1 -1
- package/scripts/check-codex-birth-hook.ts +264 -0
- package/scripts/check-codex-bridge-identity.ts +179 -0
- package/scripts/check-codex-native-push.ts +386 -0
- package/scripts/check-codex-sender-identity.ts +495 -0
- package/scripts/check-compaction-send-guard.ts +130 -0
- package/scripts/check-copilot-receive-arm.ts +4 -1
- package/scripts/check-entwurf-fact-provider.ts +38 -0
- package/scripts/check-entwurf-peers-surface.ts +13 -1
- package/scripts/check-entwurf-self-address.ts +15 -16
- package/scripts/check-entwurf-v2-contract.ts +4 -3
- package/scripts/check-entwurf-v2-decider.ts +7 -5
- package/scripts/check-entwurf-v2-native-push.ts +35 -7
- package/scripts/check-entwurf-v2-production.ts +203 -11
- package/scripts/check-entwurf-v2-runner.ts +1 -1
- package/scripts/check-entwurf-v2-surface.ts +1 -1
- package/scripts/check-gate-qualification.ts +6 -3
- package/scripts/check-harness-admission-parity.ts +0 -1
- package/scripts/check-mux-launch-tmux.ts +47 -2
- package/scripts/check-native-push-adapter.ts +20 -16
- package/scripts/check-native-push-register.ts +5 -1
- package/scripts/check-release-gate-outcomes.ts +47 -1
- package/scripts/check-setup-qualification.sh +3 -1
- package/scripts/codex-birth-doctor.sh +276 -0
- package/scripts/codex-birth-install.sh +414 -0
- package/scripts/codex-birth-uninstall.sh +170 -0
- package/scripts/codex-mcp-config.py +435 -0
- package/scripts/codex-statusline-config.py +434 -0
- package/scripts/codex_toml_io.py +532 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +113 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +399 -0
- package/scripts/lib/launch-receipt-windows.ts +46 -0
- package/scripts/lib/tmux-coordinate-row.ts +58 -0
- package/scripts/meta-bridge-fresh-cut.ts +6 -1
- package/scripts/mutants/codex-native.json +838 -0
- package/scripts/mutants/compaction-send-guard.json +103 -0
- package/scripts/mutants/entwurf-peers.json +19 -0
- package/scripts/mutants/mux-fresh-call.json +19 -8
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/mutants/v2-surface.json +53 -1
- package/scripts/raw-async-delivery/README.md +2 -1
- package/scripts/raw-codex-measure/README.md +114 -46
- package/scripts/smoke-agy-native-push-live.ts +3 -1
- package/scripts/smoke-codex-birth.sh +347 -0
- package/scripts/smoke-codex-config-state.sh +511 -0
- package/scripts/smoke-codex-fresh-live.ts +1186 -0
- package/scripts/smoke-codex-native-push-live.ts +75 -0
- package/scripts/smoke-setup-verdict.sh +123 -10
- package/scripts/tsconfig.json +1 -0
package/docs/setup-clean-host.md
CHANGED
|
@@ -8,8 +8,8 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
8
8
|
(`macos-install-surface`). Weaker than a physical-host doctor green. §1 is
|
|
9
9
|
this surface.
|
|
10
10
|
- **NOT CERTIFIED — pending physical host** — **on macOS**, every garden-native
|
|
11
|
-
harness rail in §2–§6 (pi, Claude, Copilot, OMP, agy), marker join,
|
|
12
|
-
mux. Wiring may be written; that is not a rail receipt. The remaining
|
|
11
|
+
harness rail in §2–§6 (pi, Claude, Copilot, OMP, agy, Codex), marker/request join,
|
|
12
|
+
ACP turn, mux. Wiring may be written; that is not a rail receipt. The remaining
|
|
13
13
|
unmeasured Darwin doctor reason is per-process environment DISCOVERY
|
|
14
14
|
(start-key is portable).
|
|
15
15
|
- **UNSUPPORTED** — native Windows only. macOS is never this.
|
|
@@ -20,13 +20,14 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| Node | **`>=24.0.0`** | package and bridge runtime |
|
|
22
22
|
| npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
|
|
23
|
-
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot wiring writers |
|
|
23
|
+
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot/Codex wiring writers |
|
|
24
24
|
| entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
|
|
25
25
|
| pi | optional-by-presence, `>=0.85.1 <0.86` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
|
|
26
26
|
| Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
|
|
27
27
|
| 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 |
|
|
28
28
|
| 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) |
|
|
29
29
|
| Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
|
|
30
|
+
| OpenAI Codex CLI | optional-by-presence, operator-installed and authenticated; detected composes all three operator-owned units (birth/MCP/status-line) and stays non-green until the operator trusts the birth declaration once in a visible Codex; visible fresh also requires an operator-owned existing tmux session named `codex`, with its app-server and supported TUIs seated there | native-push and visible fresh, supported in 0.21.0 |
|
|
30
31
|
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
31
32
|
|
|
32
33
|
Claude Code >=2.1.217 is required for the managed exec-hook lifecycle. The package
|
|
@@ -52,9 +53,9 @@ harness. The wiring is written; the row is FAIL:
|
|
|
52
53
|
> platform. Tracking: #78.
|
|
53
54
|
|
|
54
55
|
That FAIL is honest: setup runs no doctor, and no physical-host rail
|
|
55
|
-
receipt exists — it is not an install failure. An Entwurf-only host
|
|
56
|
-
harness on PATH
|
|
57
|
-
`result: green`), which is what CERTIFIED (CI)
|
|
56
|
+
receipt exists — it is not an install failure. An Entwurf-only host with no
|
|
57
|
+
harness on PATH stays green (six SKIP, `bins: PASS`, `core: PASS`,
|
|
58
|
+
`result: green`), which is what CERTIFIED (CI) observes.
|
|
58
59
|
|
|
59
60
|
A physical Mac can send host facts without installing entwurf or logging
|
|
60
61
|
into a harness: `scripts/raw-macos-measure/probe.sh` (`/bin/sh`, ~3.5s,
|
|
@@ -91,10 +92,10 @@ npx entwurf check-bridge
|
|
|
91
92
|
`check-bridge` is auth-free. It proves the installed prebuilt MCP server boots and
|
|
92
93
|
lists the seven garden tools; it does not prove a backend model turn or native hook.
|
|
93
94
|
|
|
94
|
-
Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, agy,
|
|
95
|
-
authentication remain operator-owned optional prerequisites for the
|
|
96
|
-
all may be absent on an Entwurf-only host. A source checkout's
|
|
97
|
-
for building and testing this repo, not a transitive product installation promise.
|
|
95
|
+
Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, Codex CLI, agy,
|
|
96
|
+
Cortex, and their authentication remain operator-owned optional prerequisites for the
|
|
97
|
+
integrations that use them; all may be absent on an Entwurf-only host. A source checkout's
|
|
98
|
+
pinned Pi development packages are for building and testing this repo, not a transitive product installation promise.
|
|
98
99
|
|
|
99
100
|
Maintainers using a source checkout do not install a second global entwurf package. Full source
|
|
100
101
|
setup currently requires Node 24, pnpm, and Python 3 on PATH; every harness — including pi — is
|
|
@@ -109,12 +110,14 @@ cd ~/repos/gh/entwurf
|
|
|
109
110
|
|
|
110
111
|
This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
|
|
111
112
|
link is foreign, outside PATH, or shadowed by another command. It detects and wires
|
|
112
|
-
pi/Claude/agy/Copilot/OMP by presence and prints a computed per-component
|
|
113
|
-
detected harness that cannot be completed makes setup exit nonzero.
|
|
114
|
-
composes all four native units
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
pi/Claude/agy/Copilot/OMP/Codex by presence and prints a computed per-component
|
|
114
|
+
PASS/SKIP/FAIL summary — a detected harness that cannot be completed makes setup exit nonzero.
|
|
115
|
+
A detected `copilot` composes all four native units, and a detected `omp` composes its own four.
|
|
116
|
+
A detected Codex install is a supported 0.21.0 path: setup composes its user-scope birth,
|
|
117
|
+
MCP, and status-line atoms but never escalates or starts the app-server. It reports independent
|
|
118
|
+
component outcomes, preserves exact env forwarding and idempotence, and returns a named
|
|
119
|
+
`codex-birth FAIL` until the vendor trust receipt exists.
|
|
120
|
+
§4, §4b, and §4c keep the explicit per-unit install/doctor/inverse surfaces.
|
|
118
121
|
|
|
119
122
|
### 1.1 User-scope ownership (one shared registration, one recorded owner)
|
|
120
123
|
|
|
@@ -396,6 +399,92 @@ The MCP entry deliberately uses the same server key as any Claude Code import so
|
|
|
396
399
|
SHADOWS it; see [`external-mcp-host.md`](./external-mcp-host.md) for why that key is pinned and
|
|
397
400
|
why `disabledServers` is never the way to hide an import.
|
|
398
401
|
|
|
402
|
+
## 4c. Optional OpenAI Codex CLI native citizen
|
|
403
|
+
|
|
404
|
+
Supported in 0.21.0 with Linux-focused evidence; on macOS it is NOT CERTIFIED —
|
|
405
|
+
pending physical host. It is native, not ACP: the purpose is to preserve the operator's
|
|
406
|
+
Codex tools, delegation, and work context rather than provide another GPT access path.
|
|
407
|
+
Entwurf owns three atoms, not the harness and not a daemon:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
# All three are operator-scope; `entwurf setup` runs them when it detects Codex. No root.
|
|
411
|
+
entwurf install-codex-birth
|
|
412
|
+
entwurf install-codex-mcp
|
|
413
|
+
entwurf install-codex-statusline
|
|
414
|
+
|
|
415
|
+
entwurf doctor-codex-birth
|
|
416
|
+
entwurf doctor-codex-mcp
|
|
417
|
+
entwurf doctor-codex-statusline
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
The birth atom publishes a `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its
|
|
421
|
+
launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`; it mints a V3 record on the
|
|
422
|
+
thread's first turn and sets the thread title to the garden id.
|
|
423
|
+
|
|
424
|
+
**One step is the operator's, and no command can do it for them.** The vendor will not run a
|
|
425
|
+
user-layer hook until it has been trusted once: open a visible plain Codex, answer
|
|
426
|
+
`Trust all and continue`, then send one first turn. `doctor-codex-birth` reads the receipt the
|
|
427
|
+
vendor writes for that declaration and is RED without it — entwurf never writes, pre-seeds or
|
|
428
|
+
recomputes that value, because it is the operator's security decision, not ours. After the one
|
|
429
|
+
answer, later sessions raise no prompt and are born automatically. The MCP writer owns
|
|
430
|
+
`[mcp_servers.entwurf-bridge]` in `$CODEX_HOME/config.toml`. Its exact `env_vars` allowlist is
|
|
431
|
+
`CODEX_HOME`, `ENTWURF_DIR`, `PI_CODING_AGENT_DIR`, `ENTWURF_META_SESSIONS_DIR`,
|
|
432
|
+
`ENTWURF_META_MAILBOX_DIR`, `ENTWURF_META_SENDERS_DIR`, `ENTWURF_META_RECEIVERS_DIR`,
|
|
433
|
+
`TMUX`, and `TMUX_PANE`.
|
|
434
|
+
The status-line writer owns only `thread-title`. Foreign or symlinked config is refused.
|
|
435
|
+
|
|
436
|
+
Native delivery requires the operator-owned default app-server and one existing tmux session
|
|
437
|
+
named exactly `codex`. Start the app-server from a pane there and seat supported Codex TUIs there:
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Run these commands inside the operator-owned tmux session named exactly `codex`.
|
|
441
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
442
|
+
mkdir -p "$CODEX_HOME/app-server-control"
|
|
443
|
+
codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
444
|
+
codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Omitted Codex fresh placement resolves exact `codex` on the caller's tmux server. The app-server's
|
|
448
|
+
`TMUX`/`TMUX_PANE` is forwarded to its MCP child, so a Codex citizen's omitted outbound Pi placement
|
|
449
|
+
stays in that home. It is **not** an arbitrary attached-TUI pane join. Exact Codex 0.153.4 source
|
|
450
|
+
exposes no request→attached-TUI-seat carrier, so clients attached from other sessions have no
|
|
451
|
+
adjacency claim; that wider topology is unsupported and unclaimed. Entwurf does not guess panes,
|
|
452
|
+
create the tmux home, expose a generic app-server manager/API, or start/restart the server. Custom
|
|
453
|
+
`CODEX_HOME` and Entwurf roots cross the same explicit `env_vars` boundary. Strict request `_meta`
|
|
454
|
+
identifies the thread caller, not its seat. Delivery uses one `codex queue` invocation with no retry.
|
|
455
|
+
|
|
456
|
+
The 2026-09-11 loaded-thread run remains pre-amendment native-push/identity evidence. The final
|
|
457
|
+
first-admission acceptance passed on 2026-09-12: real initial Pi outside `codex`, then omitted-placement
|
|
458
|
+
Codex and outbound Pi inside it, with exact callbacks and addressed delivery both ways. `DELIVERY.md`
|
|
459
|
+
owns the 57-assertion receipt, four coordinates, and digest.
|
|
460
|
+
|
|
461
|
+
`entwurf_fresh_call` accepts backend `codex` — supported in 0.21.0 — and requires an explicit model. Its
|
|
462
|
+
preflight must prove the safe birth closure, exact MCP/env atom, `thread-title`, and app-server
|
|
463
|
+
socket before tmux mutation. The sibling's garden id comes from the callback sender envelope,
|
|
464
|
+
never the launch receipt. There is no Codex resume surface, watcher, supervisor, or app-server
|
|
465
|
+
lifecycle ownership. The accepted LIVE used a real record-backed visible Pi for the first leg;
|
|
466
|
+
release-gate stripped ambient `PI_SESSION_ID`/`PI_AGENT_ID`, and the fixture/self-fetch citizen only
|
|
467
|
+
collected receipts. The smoke reported initial Pi, app-server, fresh Codex, and outbound Pi
|
|
468
|
+
coordinates separately: the initial Pi differed and the latter three matched the exact `codex` home.
|
|
469
|
+
Qualification and the frozen full floor are complete for this lane: `check-gate-qualification`
|
|
470
|
+
killed 475/475 across 43 lanes with origin purity green, `check:full` exited 0, and the LIVE release
|
|
471
|
+
gate reported MUST 24/0/0 with `cut: OK`. The bounds above are unchanged — no request→arbitrary
|
|
472
|
+
attached-TUI seat join, no resume surface, and macOS remains NOT CERTIFIED pending a physical host.
|
|
473
|
+
|
|
474
|
+
Oracle runs the first-admission LIVE with explicit ownership/model selectors:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
LIVE=1 \
|
|
478
|
+
ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid> \
|
|
479
|
+
ENTWURF_CODEX_FRESH_MODEL=<codex-model> \
|
|
480
|
+
ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
481
|
+
entwurf smoke-codex-fresh-live
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Run this from a tmux session other than `codex`. The entrypoint name does not waive the contract
|
|
485
|
+
above. A fixture/self-fetch citizen may collect receipts but cannot replace the real visible Pi leg;
|
|
486
|
+
a Pi that starts inside the Codex home proves only the weaker shared-seat mechanism.
|
|
487
|
+
|
|
399
488
|
## 5. Optional Antigravity native citizen
|
|
400
489
|
|
|
401
490
|
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
@@ -464,7 +553,7 @@ rail receipt. Doctors refuse on Darwin until a physical-host receipt
|
|
|
464
553
|
exists.
|
|
465
554
|
|
|
466
555
|
If install or doctor reports an unreadable/old active citizen generation, do not edit
|
|
467
|
-
records by hand. Close pi, Claude, Copilot,
|
|
556
|
+
records by hand. Close pi, Claude, Copilot, OMP, agy, and Codex sessions first, run
|
|
468
557
|
`entwurf meta-bridge-fresh-cut`, and read its exit status before any install. Then choose the
|
|
469
558
|
installation mode you actually own:
|
|
470
559
|
|
|
@@ -496,6 +585,7 @@ PASS/SKIP/FAIL summary. The complete quiescence, archive, and exit-code contract
|
|
|
496
585
|
- `scripts/raw-macos-measure/probe.sh`: physical-Darwin host facts (~3.5s,
|
|
497
586
|
no install, no login). Measurement, not a certification path.
|
|
498
587
|
- `doctor-meta-bridge`: one installed real Claude host, only with a new live session.
|
|
588
|
+
- `doctor-codex-{birth,mcp,statusline}` plus `LIVE=1 CODEX_LIVE_THREAD_ID=<id> entwurf smoke-codex-native-push-live`: one installed Codex host; the aggregate does not own a loaded thread.
|
|
499
589
|
- `LIVE=1 entwurf release-gate /path/to/scratch --cut`: aggregate runtime acceptance (`--cut` makes any MUST SKIP red; without it the run is a diagnostic pass).
|
|
500
590
|
|
|
501
591
|
Keep these verdicts separate. Current protocol is [VERIFY.md](../VERIFY.md); recorded
|
|
@@ -519,6 +609,9 @@ entwurf uninstall-copilot-bridge
|
|
|
519
609
|
entwurf uninstall-agy-hooks
|
|
520
610
|
entwurf uninstall-agy-statusline
|
|
521
611
|
entwurf uninstall-agy-bridge
|
|
612
|
+
entwurf uninstall-codex-statusline
|
|
613
|
+
entwurf uninstall-codex-mcp
|
|
614
|
+
entwurf uninstall-codex-birth
|
|
522
615
|
entwurf remove ~/entwurf-smoke
|
|
523
616
|
# only when no other project uses the shared user-scope pi registration:
|
|
524
617
|
entwurf remove-user-scope
|
|
@@ -64,16 +64,17 @@ import * as process from "node:process";
|
|
|
64
64
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
65
65
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
66
66
|
import { z } from "zod";
|
|
67
|
+
import { codexFreshPreflight } from "../../../pi-extensions/lib/codex-fresh-preflight.js";
|
|
67
68
|
import { controlSocketPathIn, defaultControlSocketDir } from "../../../pi-extensions/lib/control-socket-path.js";
|
|
68
69
|
import { resolveMailboxReceiverFacts } from "../../../pi-extensions/lib/entwurf-deliverability.js";
|
|
69
70
|
import { listEntwurfFacts } from "../../../pi-extensions/lib/entwurf-fact-provider.js";
|
|
70
|
-
import { renderEntwurfPeers } from "../../../pi-extensions/lib/entwurf-peers-render.js";
|
|
71
|
+
import { ENTWURF_PEERS_RENDER_LIMIT, renderEntwurfPeers } from "../../../pi-extensions/lib/entwurf-peers-render.js";
|
|
71
72
|
import { computeSelfAddressability } from "../../../pi-extensions/lib/entwurf-self-address.js";
|
|
72
73
|
import { nativePushSupported } from "../../../pi-extensions/lib/entwurf-v2-contract.js";
|
|
73
74
|
import { resolveMailboxWakeModeCapability } from "../../../pi-extensions/lib/entwurf-v2-decider.js";
|
|
74
75
|
import { runAndRenderEntwurfV2FromSurface } from "../../../pi-extensions/lib/entwurf-v2-surface.js";
|
|
75
76
|
import { makeVisibleResumeDeps, renderVisibleResume, visibleResume, } from "../../../pi-extensions/lib/entwurf-v2-visible-resume.js";
|
|
76
|
-
import { probeNativeSenderAlive, resolveTrustedMetaSenderIdentity, } from "../../../pi-extensions/lib/meta-sender-identity.js";
|
|
77
|
+
import { probeNativeSenderAlive, reconcileSenderIdentityClaims, resolveCodexRequestSenderIdentity, resolveTrustedMetaSenderIdentity, } from "../../../pi-extensions/lib/meta-sender-identity.js";
|
|
77
78
|
import { applyOmpBridgeChildRootPolicy, defaultMetaMailboxDir, defaultMetaSessionsDir, makeStoreRecordReader, readActiveStoreEntries, readMetaInbox, readMetaReceiverMarker, readMetaSenderMarker, requireBackend, } from "../../../pi-extensions/lib/meta-session.js";
|
|
78
79
|
import { freshCall, renderFreshCall } from "../../../pi-extensions/lib/mux-fresh-call.js";
|
|
79
80
|
import { RESUME_CALL_REJECT_HINT, resumeCall } from "../../../pi-extensions/lib/mux-resume-call.js";
|
|
@@ -128,11 +129,11 @@ function textErr(msg) {
|
|
|
128
129
|
const server = new McpServer({ name: "entwurf-bridge", version: "0.1.0" });
|
|
129
130
|
// Transparency envelope.
|
|
130
131
|
//
|
|
131
|
-
// Record-backed pi and
|
|
132
|
-
// so the receiver renders WHO (agentId, sessionId), FROM WHERE (cwd), and
|
|
133
|
-
// (timestamp UTC, displayed in KST). `entwurf_self` is identity-required: pi's env
|
|
134
|
-
//
|
|
135
|
-
//
|
|
132
|
+
// Record-backed pi, pid-marker native senders, and request-scoped Codex senders carry a
|
|
133
|
+
// structured envelope so the receiver renders WHO (agentId, sessionId), FROM WHERE (cwd), and
|
|
134
|
+
// WHEN (timestamp UTC, displayed in KST). `entwurf_self` is identity-required: pi's env is a
|
|
135
|
+
// child carrier for the garden id established by record birth; a native sender marker or Codex
|
|
136
|
+
// request selector is accepted only through its backing record. Plain anonymous external hosts
|
|
136
137
|
// fail. #50 C4: v2 delivery is identity-REQUIRED by default — "if we don't know
|
|
137
138
|
// who sent it, we don't send it" holds on every install surface, not only where
|
|
138
139
|
// an installer remembered to set a flag. The ONE documented escape hatch is
|
|
@@ -144,27 +145,25 @@ const server = new McpServer({ name: "entwurf-bridge", version: "0.1.0" });
|
|
|
144
145
|
class EntwurfEnvelopeWiringError extends Error {
|
|
145
146
|
constructor(missing) {
|
|
146
147
|
super(`entwurf sender envelope wiring incomplete — missing env: ${missing.join(", ")}, ` +
|
|
147
|
-
"and no
|
|
148
|
-
"PI_SESSION_ID + PI_AGENT_ID
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
148
|
+
"and no record-backed native sender claim was found. This MCP child should inherit " +
|
|
149
|
+
"PI_SESSION_ID + PI_AGENT_ID from an entwurf-control pi session, carry a live sender marker " +
|
|
150
|
+
"written by its native hook, or be the managed Codex MCP child whose current tool request " +
|
|
151
|
+
"names a thread backed by a Codex citizen record. entwurf_self is callable only while one of " +
|
|
152
|
+
"those authoritative identity paths is present.");
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
|
-
// #50 C4: anonymous sends are refused BY DEFAULT — a send with no pi-session
|
|
155
|
-
//
|
|
156
|
-
//
|
|
155
|
+
// #50 C4: anonymous sends are refused BY DEFAULT — a send with no pi-session identity,
|
|
156
|
+
// trusted pid marker, or request-scoped Codex record does not go out as anonymous external-mcp
|
|
157
|
+
// unless the operator explicitly wired the escape hatch.
|
|
157
158
|
// "If we don't know who sent it, we don't send it."
|
|
158
159
|
class EntwurfSenderIdentityError extends Error {
|
|
159
160
|
constructor() {
|
|
160
161
|
super("entwurf-bridge refused: no authoritative sender identity. Anonymous external sends are " +
|
|
161
|
-
"refused by default, and no pi-session env
|
|
162
|
-
"marker
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER=1 (explicit operator wiring; the send is then marked " +
|
|
167
|
-
"external/non-replyable).");
|
|
162
|
+
"refused by default, and this request has no complete pi-session env, live record-backed " +
|
|
163
|
+
"sender marker, or managed Codex thread selector backed by a Codex citizen record. Open the " +
|
|
164
|
+
"session through its installed birth/MCP integration, then retry. A deliberately-anonymous " +
|
|
165
|
+
"external MCP host may set ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER=1 (explicit operator wiring; " +
|
|
166
|
+
"the send is then marked external/non-replyable).");
|
|
168
167
|
}
|
|
169
168
|
}
|
|
170
169
|
function buildStrictPiSenderEnvelope() {
|
|
@@ -203,41 +202,7 @@ function buildStrictPiSenderEnvelope() {
|
|
|
203
202
|
replyable: self.replyable,
|
|
204
203
|
};
|
|
205
204
|
}
|
|
206
|
-
async function
|
|
207
|
-
// No pi-session identity. Try the meta-sender marker: a native backend that minted a
|
|
208
|
-
// garden-id from its own hook (Claude SessionStart / agy PreInvocation). The marker is
|
|
209
|
-
// keyed by the shared parent pid — this MCP child's process.ppid IS the native host the
|
|
210
|
-
// hook ran under (NOT cwd inference). A trusted marker promotes this process from
|
|
211
|
-
// anonymous external-mcp to a meta-session sender addressed by its garden-id.
|
|
212
|
-
const trusted = resolveTrustedMetaSenderIdentity({
|
|
213
|
-
markerPath: process.env.ENTWURF_META_SENDER_MARKER?.trim() || undefined,
|
|
214
|
-
});
|
|
215
|
-
if (!trusted)
|
|
216
|
-
return null;
|
|
217
|
-
const { marker, identity } = trusted;
|
|
218
|
-
// Identity is trusted — but `replyable` is a SEPARATE fact, and WHICH fact depends on the
|
|
219
|
-
// rail a reply would ride (보정①). THREE values, not a native-push-or-self-fetch binary:
|
|
220
|
-
// native-push ← nativePushSupported(backend). NOT wakeMode: `direct-inject` also covers
|
|
221
|
-
// codex/pi, which have no native-push adapter.
|
|
222
|
-
// self-fetch ← resolveMailboxWakeModeCapability (the decider's mailbox seam — one owner
|
|
223
|
-
// with dispatch). A new hardcoded backend list would drift the moment the registry
|
|
224
|
-
// admits another self-fetch citizen.
|
|
225
|
-
// none ← neither. omp today: no mailbox drain, no native-push adapter. Rendering
|
|
226
|
-
// this as self-fetch printed a mailboxPath nothing drains.
|
|
227
|
-
// self-fetch (claude-code/copilot): can this citizen's own inbox wake? → the SHARED
|
|
228
|
-
// receiver composition `resolveMailboxReceiverFacts`, the same one the v2 dispatch seam
|
|
229
|
-
// uses, so a citizen's self-reported replyability can never disagree with what dispatch
|
|
230
|
-
// decides about it. It reads the presence marker (a dead/reused owner already folds to
|
|
231
|
-
// null) AND, where the watch owner is the sender-marker process, the #101 join that says
|
|
232
|
-
// the owner is still serving THIS garden rather than one it switched away from.
|
|
233
|
-
// native-push (antigravity): there is no inbox and no watch. A reply is injected into a
|
|
234
|
-
// live app-server conversation, so only an adapter probe can answer. Composing the
|
|
235
|
-
// receiver atom here would demand `watchArmed` from a backend that never arms one, and
|
|
236
|
-
// every agy citizen would report replyable:false forever.
|
|
237
|
-
// Either way an inactive/unreachable citizen STILL returns its identity (who-sent must
|
|
238
|
-
// survive; degrading to null would erase the sender) — only with replyable:false.
|
|
239
|
-
// The rail, named ONCE and reused for both the predicate and the caller's rendering —
|
|
240
|
-
// so entwurf_self can never re-derive it differently from what decided `replyable`.
|
|
205
|
+
async function buildMetaSenderEnvelope(identity, cwd, trustedMarker) {
|
|
241
206
|
const metaDeliveryDomain = nativePushSupported(identity.backend)
|
|
242
207
|
? "native-push"
|
|
243
208
|
: resolveMailboxWakeModeCapability(identity)
|
|
@@ -252,6 +217,9 @@ async function buildTrustedMetaSenderEnvelope(cwd = process.cwd()) {
|
|
|
252
217
|
}
|
|
253
218
|
: metaDeliveryDomain === "self-fetch"
|
|
254
219
|
? (() => {
|
|
220
|
+
if (!trustedMarker) {
|
|
221
|
+
throw new Error(`entwurf-bridge: ${identity.backend} resolved as self-fetch without the sender marker its receiver join requires`);
|
|
222
|
+
}
|
|
255
223
|
const receiver = resolveMailboxReceiverFacts(identity, {
|
|
256
224
|
readReceiverMarker: (gardenId) => readMetaReceiverMarker({ gardenId }),
|
|
257
225
|
readSenderMarker: (backend, ownerPid) => readMetaSenderMarker({ backend: requireBackend(backend), ownerPid }),
|
|
@@ -274,7 +242,7 @@ async function buildTrustedMetaSenderEnvelope(cwd = process.cwd()) {
|
|
|
274
242
|
envelope: {
|
|
275
243
|
sessionId: identity.gardenId,
|
|
276
244
|
agentId: `meta-session/${identity.backend}`,
|
|
277
|
-
cwd
|
|
245
|
+
cwd,
|
|
278
246
|
timestamp: new Date().toISOString(),
|
|
279
247
|
origin: "meta-session",
|
|
280
248
|
replyable: self.replyable,
|
|
@@ -282,17 +250,43 @@ async function buildTrustedMetaSenderEnvelope(cwd = process.cwd()) {
|
|
|
282
250
|
metaDeliveryDomain,
|
|
283
251
|
};
|
|
284
252
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
253
|
+
async function resolveAuthoritativeSender(context, cwd = process.cwd()) {
|
|
254
|
+
const sessionId = process.env.PI_SESSION_ID?.trim();
|
|
255
|
+
const agentId = process.env.PI_AGENT_ID?.trim();
|
|
256
|
+
const pi = sessionId && agentId && cwd ? buildStrictPiSenderEnvelope() : null;
|
|
257
|
+
const marker = resolveTrustedMetaSenderIdentity({
|
|
258
|
+
markerPath: process.env.ENTWURF_META_SENDER_MARKER?.trim() || undefined,
|
|
259
|
+
});
|
|
260
|
+
const codex = resolveCodexRequestSenderIdentity({
|
|
261
|
+
provenance: process.env.ENTWURF_BRIDGE_NATIVE_HOST,
|
|
262
|
+
clientInfo: server.server.getClientVersion(),
|
|
263
|
+
requestMeta: context.requestMeta,
|
|
264
|
+
});
|
|
265
|
+
const selected = reconcileSenderIdentityClaims([
|
|
266
|
+
...(pi ? [{ rail: "pi-session", id: pi.sessionId }] : []),
|
|
267
|
+
...(marker ? [{ rail: "meta-sender-marker", id: marker.identity.gardenId }] : []),
|
|
268
|
+
...(codex ? [{ rail: "codex-request", id: codex.identity.gardenId }] : []),
|
|
269
|
+
]);
|
|
270
|
+
if (!selected)
|
|
271
|
+
return null;
|
|
272
|
+
if (codex && codex.identity.gardenId === selected.id) {
|
|
273
|
+
return buildMetaSenderEnvelope(codex.identity, codex.identity.cwd);
|
|
274
|
+
}
|
|
275
|
+
if (marker && marker.identity.gardenId === selected.id) {
|
|
276
|
+
return buildMetaSenderEnvelope(marker.identity, marker.marker.cwd || cwd, marker);
|
|
277
|
+
}
|
|
278
|
+
if (pi && pi.sessionId === selected.id)
|
|
279
|
+
return { envelope: pi };
|
|
280
|
+
throw new Error(`entwurf-bridge: reconciled sender ${selected.id} has no matching identity source`);
|
|
281
|
+
}
|
|
282
|
+
// Async only when the selected sender rides native-push: replyability is the adapter's live fact.
|
|
283
|
+
async function buildAuthoritativeSelfEnvelope(context = {}) {
|
|
284
|
+
const resolved = await resolveAuthoritativeSender(context);
|
|
285
|
+
if (resolved)
|
|
286
|
+
return resolved;
|
|
288
287
|
const sessionId = process.env.PI_SESSION_ID?.trim();
|
|
289
288
|
const agentId = process.env.PI_AGENT_ID?.trim();
|
|
290
289
|
const cwd = process.cwd();
|
|
291
|
-
if (sessionId && agentId && cwd)
|
|
292
|
-
return { envelope: buildStrictPiSenderEnvelope() };
|
|
293
|
-
const meta = await buildTrustedMetaSenderEnvelope(cwd);
|
|
294
|
-
if (meta)
|
|
295
|
-
return meta;
|
|
296
290
|
const missing = [];
|
|
297
291
|
if (!sessionId)
|
|
298
292
|
missing.push("PI_SESSION_ID");
|
|
@@ -302,18 +296,11 @@ async function buildAuthoritativeSelfEnvelope() {
|
|
|
302
296
|
missing.push("cwd");
|
|
303
297
|
throw new EntwurfEnvelopeWiringError(missing);
|
|
304
298
|
}
|
|
305
|
-
async function buildSendSenderEnvelope() {
|
|
306
|
-
const
|
|
307
|
-
|
|
299
|
+
async function buildSendSenderEnvelope(context = {}) {
|
|
300
|
+
const resolved = await resolveAuthoritativeSender(context);
|
|
301
|
+
if (resolved)
|
|
302
|
+
return resolved.envelope;
|
|
308
303
|
const cwd = process.cwd();
|
|
309
|
-
if (sessionId && agentId && cwd)
|
|
310
|
-
return buildStrictPiSenderEnvelope();
|
|
311
|
-
const meta = await buildTrustedMetaSenderEnvelope(cwd);
|
|
312
|
-
// Delivery takes the WIRE envelope only — the rail axis is rendering-local.
|
|
313
|
-
if (meta)
|
|
314
|
-
return meta.envelope;
|
|
315
|
-
// No marker. #50 C4: anonymous external is refused UNLESS the operator wired the
|
|
316
|
-
// explicit escape hatch — identity-required is the default, not an install flag.
|
|
317
304
|
if (process.env.ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER !== "1") {
|
|
318
305
|
throw new EntwurfSenderIdentityError();
|
|
319
306
|
}
|
|
@@ -365,12 +352,11 @@ server.tool("entwurf_v2", "CANONICAL DELIVERY SURFACE for garden ids: message, r
|
|
|
365
352
|
"outcome (delivered / rejected / delivered-but-lock-dirty). EXISTING targets only; discover with " +
|
|
366
353
|
"entwurf_peers. A peer entwurf_peers shows as liveness=alive → fire-and-forget. A " +
|
|
367
354
|
"citizen with NO socket liveness (liveness=unsupported) is ALSO fire-and-forget — unsupported means only " +
|
|
368
|
-
'"no control-socket probe"
|
|
369
|
-
"
|
|
370
|
-
"
|
|
371
|
-
"
|
|
372
|
-
"
|
|
373
|
-
"native-push-target-dead; indeterminate → native-push-probe-indeterminate (unestablished ≠ gone). " +
|
|
355
|
+
'"no control-socket probe." The decider resolves the actual rail at dispatch time: a self-fetch ' +
|
|
356
|
+
"mailbox only while deliverable, native-push direct injection only while its adapter probe is alive, " +
|
|
357
|
+
"or a THIRD RESULT: rejection when neither holds. An inactive self-fetch citizen is mailbox-undeliverable; " +
|
|
358
|
+
"native-push has NO mailbox: dead → native-push-target-dead; indeterminate → " +
|
|
359
|
+
"native-push-probe-indeterminate. " +
|
|
374
360
|
"DORMANT IS UNREACHABLE: a socket-domain citizen that is not running gets dormant-fire-forget-unsupported " +
|
|
375
361
|
"— same receiver rule as the mailbox, no active drainer means no delivery. " +
|
|
376
362
|
"The intent that used to answer there, owned-outcome, resumed it by launching a hidden background child " +
|
|
@@ -402,11 +388,11 @@ server.tool("entwurf_v2", "CANONICAL DELIVERY SURFACE for garden ids: message, r
|
|
|
402
388
|
"follow_up (queue after it). The mailbox and native-push plans carry no mode, so it has no " +
|
|
403
389
|
"effect on those rails."),
|
|
404
390
|
wants_reply: z.boolean().optional().describe("Human-conversation reply hint (default false)"),
|
|
405
|
-
}, async ({ target, intent, message, mode, wants_reply }) => {
|
|
391
|
+
}, async ({ target, intent, message, mode, wants_reply }, extra) => {
|
|
406
392
|
try {
|
|
407
393
|
// Resolved ONCE so the dispatch-moment timestamp is fixed and the control RPC sender
|
|
408
394
|
// + the mailbox body sender share one envelope. No replyability gate (see above).
|
|
409
|
-
const sender = await buildSendSenderEnvelope();
|
|
395
|
+
const sender = await buildSendSenderEnvelope({ requestMeta: extra._meta });
|
|
410
396
|
const rendered = await runAndRenderEntwurfV2FromSurface({ target, intent, message, mode, wants_reply },
|
|
411
397
|
// No trust-preflight inputs are passed, and none exist to pass: the preflight on this
|
|
412
398
|
// path guarded the resume verdict and left with `owned-outcome`. `senderProvider` is
|
|
@@ -424,15 +410,14 @@ server.tool("entwurf_self", "Return this caller's authoritative identity envelop
|
|
|
424
410
|
"degrading it to nothing would erase who-sent. Use to confirm WHO you " +
|
|
425
411
|
"are (agentId, sessionId), FROM WHERE (cwd), and WHEN this snapshot was taken. " +
|
|
426
412
|
"Works for pi sessions (PI_SESSION_ID / PI_AGENT_ID) and for garden-native meta-sessions, whose " +
|
|
427
|
-
"garden id comes from
|
|
428
|
-
"
|
|
429
|
-
"
|
|
430
|
-
"
|
|
431
|
-
"
|
|
432
|
-
"
|
|
433
|
-
"authoritative reply address.", {}, async () => {
|
|
413
|
+
"garden id comes from the matching record plus either a trusted native process marker or Codex's " +
|
|
414
|
+
"request-scoped thread selector. For a meta-session it also reports WHICH rail a reply rides, " +
|
|
415
|
+
"because that differs by backend: a self-fetch citizen has a drainable mailbox, while a " +
|
|
416
|
+
"native-push citizen has NO mailbox at all — a reply is injected straight into its live native " +
|
|
417
|
+
"conversation/thread. Do not expect a mailbox just because origin is meta-session. Throws for " +
|
|
418
|
+
"plain anonymous external MCP hosts because they have no authoritative reply address.", {}, async (_args, request) => {
|
|
434
419
|
try {
|
|
435
|
-
const self = await buildAuthoritativeSelfEnvelope();
|
|
420
|
+
const self = await buildAuthoritativeSelfEnvelope({ requestMeta: request._meta });
|
|
436
421
|
const sender = self.envelope;
|
|
437
422
|
const kst = formatKstTimestamp(sender.timestamp);
|
|
438
423
|
const extra = {};
|
|
@@ -522,6 +507,9 @@ server.tool("entwurf_peers", "List the entwurf fact surface: garden citizens (fr
|
|
|
522
507
|
readRecord: makeStoreRecordReader(sessionsDir),
|
|
523
508
|
// Socket axis: the same dir dispatch uses (grammar SSOT), scan-internal only.
|
|
524
509
|
socket: { dir: ENTWURF_DIR },
|
|
510
|
+
// #112: observe only rows this human surface can render. Full store facts,
|
|
511
|
+
// diagnostics, liveness, and machine payload remain intact.
|
|
512
|
+
observationLimit: ENTWURF_PEERS_RENDER_LIMIT,
|
|
525
513
|
});
|
|
526
514
|
const { text } = renderEntwurfPeers(result);
|
|
527
515
|
return textOk(text);
|
|
@@ -610,29 +598,29 @@ server.tool("entwurf_register_native", "Register an ALREADY-RUNNING native conve
|
|
|
610
598
|
// its own garden id, a fresh cell answered with its uuidv7 `PI_SESSION_ID` value read out of the
|
|
611
599
|
// environment by an MCP server it had spawned itself — confidently, and wrong. A sibling launched
|
|
612
600
|
// against that answer would call home to a garden id nobody holds.
|
|
613
|
-
server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operator's tmux and hand it a first task.
|
|
614
|
-
"backends only: pi, claude-code, copilot, omp. The sibling's FIRST action is a callback to you carrying a nonce, and the " +
|
|
601
|
+
server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operator's tmux and hand it a first task. Five fixed " +
|
|
602
|
+
"backends only: pi, claude-code, copilot, omp, codex. The sibling's FIRST action is a callback to you carrying a nonce, and the " +
|
|
615
603
|
"sender envelope of that callback is its garden id — that is how you learn the address of something that " +
|
|
616
604
|
"did not exist a moment ago. This returns a LAUNCH receipt (tmux window/pane plus that nonce) and nothing " +
|
|
617
605
|
"else: it does NOT mean the runtime started, the first turn ran, or the task was delivered. Nothing polls " +
|
|
618
606
|
"for the callback; if it never arrives the window is visible and can be read directly. For EXISTING " +
|
|
619
607
|
"citizens use entwurf_v2 — this tool only creates, and entwurf_peers only reports. Model is REQUIRED and " +
|
|
620
608
|
"is passed to the chosen runtime CLI (`provider/model` for pi; model id/alias for Claude Code; a model name " +
|
|
621
|
-
"or `auto` for copilot; a fuzzy model pattern for omp).
|
|
622
|
-
"
|
|
623
|
-
"
|
|
624
|
-
"
|
|
625
|
-
"prompt, so the sibling could not call you back at all). An optional " +
|
|
609
|
+
"or `auto` for copilot; a fuzzy model pattern for omp or codex). Copilot, omp, and codex are refused BEFORE " +
|
|
610
|
+
"any window opens when their required birth, MCP, receive/delivery, or visible-identity units are absent. " +
|
|
611
|
+
"Codex additionally requires the operator-owned default app-server socket; entwurf never starts or supervises it. " +
|
|
612
|
+
"An optional " +
|
|
626
613
|
"cwd starts the sibling in ONE literal absolute existing directory (cross-repo fresh) — never pick resume " +
|
|
627
614
|
"for a dormant record's cwd; resume is continuity-only. Omitted/empty cwd means the caller's own directory. " +
|
|
628
|
-
"An optional placement.tmuxSession
|
|
629
|
-
"
|
|
615
|
+
"An optional placement.tmuxSession is an expert override naming ONE EXISTING session on this agent's own tmux server. " +
|
|
616
|
+
"When omitted, Codex targets the exact existing `codex` home session; other backends target the caller's session. " +
|
|
617
|
+
"A missing named/home session is tmux-session-missing and NOTHING is created. " +
|
|
630
618
|
"There are no arbitrary command/env knobs. Do not put secrets in the task — model and task argv are visible to " +
|
|
631
619
|
"same-user processes on this host. Requires that this agent itself runs " +
|
|
632
620
|
"inside tmux: without a pane anchor there is no session to open a sibling beside.", {
|
|
633
621
|
backend: z
|
|
634
|
-
.enum(["pi", "claude-code", "copilot", "omp"])
|
|
635
|
-
.describe("Which fixed runtime to open. Only these
|
|
622
|
+
.enum(["pi", "claude-code", "copilot", "omp", "codex"])
|
|
623
|
+
.describe("Which fixed runtime to open. Only these five; there is no arbitrary command."),
|
|
636
624
|
model: z
|
|
637
625
|
.string()
|
|
638
626
|
.min(1)
|
|
@@ -646,7 +634,7 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
646
634
|
// as an unused suppression while the escape goes back to being fixable.
|
|
647
635
|
// biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above
|
|
648
636
|
.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\[\]-]*$/)
|
|
649
|
-
.describe("Required runtime model: canonical provider/model for pi, a Claude Code model id/alias, or a Copilot model name
|
|
637
|
+
.describe("Required runtime model: canonical provider/model for pi, a Claude Code model id/alias, or a Copilot/OMP/Codex model name."),
|
|
650
638
|
task: z
|
|
651
639
|
.string()
|
|
652
640
|
.min(1)
|
|
@@ -663,11 +651,11 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
663
651
|
.describe("EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid."),
|
|
664
652
|
})
|
|
665
653
|
.optional()
|
|
666
|
-
.describe("Optional
|
|
667
|
-
}, async ({ backend, model, task, cwd, placement }) => {
|
|
654
|
+
.describe("Optional expert seat override: open the sibling in ONE EXISTING tmux session of this agent's own server. When omitted, Codex selects the exact existing `codex` home session; other backends use the caller's session. Nothing is ever created. Independent of cwd; neither is inferred from the other. The receipt reports the selected name, its source, and resolved target session id."),
|
|
655
|
+
}, async ({ backend, model, task, cwd, placement }, extra) => {
|
|
668
656
|
let callerGardenId = null;
|
|
669
657
|
try {
|
|
670
|
-
const self = await buildAuthoritativeSelfEnvelope();
|
|
658
|
+
const self = await buildAuthoritativeSelfEnvelope({ requestMeta: extra._meta });
|
|
671
659
|
callerGardenId = self.envelope.sessionId;
|
|
672
660
|
}
|
|
673
661
|
catch (err) {
|
|
@@ -683,7 +671,11 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
683
671
|
callerGardenId = null;
|
|
684
672
|
}
|
|
685
673
|
try {
|
|
686
|
-
const
|
|
674
|
+
const missing = backend === "codex" ? await codexFreshPreflight(process.env) : null;
|
|
675
|
+
const result = missing
|
|
676
|
+
? { ok: false, reason: missing }
|
|
677
|
+
: freshCall({ backend, model, task, cwd, placement, callerGardenId });
|
|
678
|
+
const rendered = renderFreshCall(result);
|
|
687
679
|
return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
|
|
688
680
|
}
|
|
689
681
|
catch (err) {
|