@junghanacs/entwurf 0.25.1 → 0.30.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 +1 -1
- package/BASELINE.md +1 -1
- package/CHANGELOG.md +73 -0
- package/DELIVERY.md +1 -1
- package/README.md +178 -674
- package/VERIFY.md +16 -15
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +6 -2
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +346 -10
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +27 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +12 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-mailbox.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +3 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +13 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +3 -3
- package/mcp/entwurf-bridge/src/index.ts +30 -3
- package/package.json +11 -11
- package/pi-extensions/entwurf-control.ts +50 -637
- package/pi-extensions/lib/acp/backend-adapter.ts +13 -1
- package/pi-extensions/lib/entwurf-fact-provider.ts +2 -2
- package/pi-extensions/lib/entwurf-mcp-server.ts +74 -0
- package/pi-extensions/lib/entwurf-peers-render.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-decider.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-mailbox.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +3 -3
- package/pi-extensions/lib/entwurf-v2-surface.ts +3 -5
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +3 -3
- package/pi-extensions/lib/fresh-call-composition.ts +13 -6
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +3 -3
- package/run.sh +163 -50
- package/scripts/check-entwurf-peers-surface.ts +8 -15
- package/scripts/check-entwurf-self-address.ts +7 -11
- package/scripts/check-entwurf-v2-surface.ts +20 -112
- package/scripts/check-fresh-call-dispatch.ts +12 -13
- package/scripts/check-gate-qualification.ts +248 -4
- package/scripts/check-install-surface.ts +3 -2
- package/scripts/check-mux-resume-call.ts +24 -35
- package/scripts/check-pi-bridge-sender.ts +130 -0
- package/scripts/check-pi-mcp-bridge.ts +251 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +17 -5
- package/scripts/lib/codex-fresh-source-receipts.ts +21 -2
- package/scripts/lib/mutation-qualify.ts +269 -44
- package/scripts/lib/pi-mcp-bridge-probe.mjs +170 -0
- package/scripts/meta-bridge-doctor.sh +16 -5
- package/scripts/mutants/acp-foreground-only.json +17 -0
- package/scripts/mutants/codex-native.json +11 -11
- package/scripts/mutants/control-send-receipt.json +6 -4
- package/scripts/mutants/copilot-birth.json +2 -1
- package/scripts/mutants/fresh-call-dispatch.json +3 -14
- package/scripts/mutants/gate-qualification.json +59 -0
- package/scripts/mutants/mux-fresh-call.json +43 -45
- package/scripts/mutants/mux-resume-call.json +11 -35
- package/scripts/mutants/pack-install.json +15 -4
- package/scripts/mutants/pi-bridge-sender.json +54 -0
- package/scripts/mutants/pi-floor.json +6 -2
- package/scripts/mutants/pi-mcp-bridge.json +36 -0
- package/scripts/mutants/pi-mcp-register.json +51 -0
- package/scripts/mutants/v2-surface.json +6 -6
- package/scripts/resident-rpc-drive.ts +5 -1
- package/scripts/smoke-acp-bundled-mcp-live.ts +3 -1
- package/scripts/smoke-acp-cortex-live.ts +3 -1
- package/scripts/smoke-acp-session-reuse-live.ts +29 -12
- package/scripts/smoke-acp-socket-citizen-live.ts +3 -1
- package/scripts/smoke-acp-v2-send-live.ts +3 -1
- package/scripts/smoke-codex-fresh-live.ts +20 -1
- package/scripts/smoke-entwurf-chain-live.ts +6 -2
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -1
- package/scripts/smoke-herdr-raw-install-live.sh +1 -1
- package/scripts/smoke-mux-lifecycle-live.ts +1 -1
- package/scripts/smoke-resident-garden-guard.sh +3 -1
package/README.md
CHANGED
|
@@ -1,748 +1,252 @@
|
|
|
1
1
|
# entwurf
|
|
2
2
|
|
|
3
|
-
`entwurf` is a garden-citizen dispatch substrate: a thin bridge that lets
|
|
3
|
+
`entwurf` is a garden-citizen dispatch substrate: a thin bridge that lets independent
|
|
4
|
+
agent harnesses address one another by **garden id**, without owning each other's
|
|
5
|
+
transcript, authentication, tools, or runtime.
|
|
4
6
|
|
|
5
7
|

|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/@junghanacs/entwurf) · maintained by [junghanacs.com](https://junghanacs.com/)
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one adapter, not the project subject. Claude Code, GitHub Copilot CLI, and OMP (`omp`) are shipped self-fetch citizens; Antigravity (`agy`) is shipped native-push. Codex CLI is a **native-push citizen, supported in 0.21.0 on Linux** over the operator-owned app-server: vendor-trusted user-scope birth, strict request-scoped identity, loaded-thread probe, one-shot queue delivery, and visible fresh beside the CALLER's own pane. Unrestricted attached-TUI seat inference is not supported or claimed; #95 D1 retired the fixed `codex` tmux home on 2026-09-16, so the operator seats the app-server wherever they like and a Codex caller opens its sibling beside its own TUI pane. Codex remains native so it keeps its vendor tools, delegation, and work context; this is not another way to buy or expose GPT through ACP.
|
|
11
|
+
A sibling is a visible session with its own runtime boundary, not a disposable
|
|
12
|
+
worker or a subagent of a new central harness. Entwurf supplies identity, delivery,
|
|
13
|
+
receipts and named lifecycle verbs; the operator and native harnesses keep steering.
|
|
14
|
+
Git branches, worktrees, file isolation and internal delegation remain their concern.
|
|
15
|
+
[Short answers: FAQ](./FAQ.md).
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
Claude Code / Copilot /
|
|
19
|
-
→ garden id
|
|
18
|
+
pi / Claude Code / Copilot / OMP / Antigravity / Codex
|
|
19
|
+
→ record-backed garden id
|
|
20
20
|
→ entwurf_v2
|
|
21
|
-
→ control-socket |
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
[`entwurf_v2`](#entwurf_v2--canonical-dispatch-verb) is the canonical dispatch surface over *existing* garden citizens — live control-socket send, meta-mailbox enqueue, and native-push into a live Antigravity conversation or loaded Codex thread. It starts no process on any rail. `entwurf_fresh_call` is separate lifecycle. With placement omitted the seat follows the CALLER: a Codex citizen opens beside its own TUI pane, found by the `thread-id` in that pane's terminal title, and 0 or 2+ matching panes reject with no fallback; every other caller opens in its own session. A missing app-server rejects before launch, and Entwurf never creates or supervises it. An explicit `placement.tmuxSession` remains an expert override. Exact 0.153.4 source confirms that arbitrary attached-TUI request→seat inference is unavailable, so Entwurf makes no such claim. Codex has no resume surface.
|
|
25
|
-
|
|
26
|
-
**Garden id is deliberate vocabulary.** It is not a decorative synonym for session id, worker, delegate, or subagent. The unfamiliar word is a guard: each harness keeps its own identity and transcript, while `entwurf` supplies a narrow addressable surface between siblings.
|
|
27
|
-
|
|
28
|
-
**A narrow harness tool surface is discipline, not a missing feature.** When entwurf drives a backend the way pi taught — the ACP Claude session, a pi-native sibling — it runs without a sub-agent tool or a todo tool, on a narrow tool surface in auto-approve (`yolo`) mode (the ACP backend yolo-runs inside its isolated overlay). That restraint is the point: it keeps the one forged screwdriver from drifting into a second orchestrator, and keeps the operator's own driver — not a hidden agent swarm — the thing actually steering. See [AGENTS.md](./AGENTS.md) North Star.
|
|
29
|
-
|
|
30
|
-
**The ACP plugin is one ingress, not the boundary.** It re-enters as a pi provider/model on a host `--entwurf-control` session that is *already* a v2 socket-citizen; it does not mint its own socket / peers / citizen layer (see [AGENTS.md](./AGENTS.md) §ACP Plugin Boundary). No OAuth proxy, no subscription bypass, no CLI transcript scraping, no Claude Code emulation.
|
|
31
|
-
|
|
32
|
-
```text
|
|
33
|
-
pi --entwurf-control
|
|
34
|
-
→ entwurf ACP plugin
|
|
35
|
-
→ claude-agent-acp
|
|
36
|
-
→ Claude backend under the operator's local auth
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
**Native bridges reach beyond ACP transport.** A global `SessionStart` hook registers native Claude Code sessions as **garden-native meta-sessions** with a garden id, a mailbox, and a trusted sender marker. That makes an already-running Claude Code terminal addressable through `entwurf_v2` (the mailbox path), self-identifying through `entwurf_self`, and replyable by garden id — without turning pi into a second harness or importing Claude's transcript.
|
|
40
|
-
|
|
41
|
-
```text
|
|
42
|
-
native Claude Code
|
|
43
|
-
→ SessionStart hook
|
|
44
|
-
→ mailbox-backed meta-session <garden-id>
|
|
45
|
-
→ entwurf-bridge MCP
|
|
46
|
-
→ entwurf_self | entwurf_v2 | entwurf_inbox_read
|
|
21
|
+
→ control-socket | self-fetch mailbox | native-push
|
|
47
22
|
```
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
and
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Antigravity uses a separate shipped rail. Its `PreInvocation` hook births or re-attaches the conversation by native `conversationId`, writes a record-backed sender marker, and leaves delivery to the live native LS gRPC route. There is no mailbox or receiver marker on this rail: `entwurf_v2` probes the conversation and direct-injects with native-push.
|
|
24
|
+
The package holds the v2 core, native-harness bridges, a pi adapter and an ACP
|
|
25
|
+
plugin. ACP is one ingress, not the boundary; pi is one adapter, not the center.
|
|
26
|
+
Claude and Cortex Code are ACP backends. Codex and Antigravity stay native so they
|
|
27
|
+
retain their own tools and work context. Capabilities differ by rail: a shipped
|
|
28
|
+
citizen is not a promise of every lifecycle operation.
|
|
56
29
|
|
|
57
|
-
|
|
58
|
-
native Antigravity / agy
|
|
59
|
-
→ PreInvocation imprint → meta-session <garden-id>
|
|
60
|
-
→ entwurf-bridge MCP sender identity
|
|
61
|
-
↔ entwurf_v2 native-push
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Claude's `install-meta-bridge`, Copilot's four `install-copilot-*` surfaces, agy's
|
|
65
|
-
`install-agy-{bridge,statusline,hooks}`, OMP's four `install-omp-{bridge,mcp,config,receive}`,
|
|
66
|
-
and Codex's birth, MCP and statusline units are distinct because their
|
|
67
|
-
lifecycle and delivery transports are genuinely different. See [DELIVERY.md](./DELIVERY.md).
|
|
68
|
-
|
|
69
|
-
> **Direction.** Inverse of [`pi-acp`](https://github.com/svkozak/pi-acp). `pi-acp` lets external ACP clients talk *to* pi; `entwurf` lets garden citizens talk across harness boundaries — with pi as one adapter, not the center.
|
|
70
|
-
|
|
71
|
-
## Concept primer
|
|
30
|
+
## Install
|
|
72
31
|
|
|
73
|
-
|
|
32
|
+
**Choose by where you work. Neither route installs a harness, subscription or login.**
|
|
74
33
|
|
|
75
|
-
|
|
76
|
-
- **Garden / garden id** — the garden is the shared address space where independent harness sessions become citizens without losing their own runtime or transcript. A garden id is the stable address of one such citizen (for pi, a garden-native session id like `YYYYMMDDTHHMMSS-<6hex>`; for native harnesses, a meta-session id minted from an authoritative lifecycle hook — Claude `SessionStart`, Codex `SessionStart`, Copilot's first-prompt birth hook, agy `PreInvocation`, and for OMP an in-process extension bound to both session edges that mints only the visible `mode === "tui"` host). It is not a worker name and not proof that pi owns the session. The same-looking id may name a live control socket, a dormant pi record, a mailbox-backed native session, or a native-push conversation/thread, so routing always reads the record and live rail facts first.
|
|
77
|
-
- **Engraving** — optional short operator text delivered through each backend's native identity carrier. Not a giant hidden prompt, not a tool catalog.
|
|
78
|
-
- **MCP** — in this repo, MCP is just the transport by which ACP-backed sessions receive pi capabilities that native pi exposes directly as extensions. It is not a general MCP platform. Explicit `entwurfProvider.mcpServers` only; no ambient `~/.mcp.json` scanning, no automatic retrieval. The same `entwurf-bridge` entry can also be wired into another host's MCP catalog (Claude Code, Copilot, Codex, Antigravity, OMP, …) when the operator chooses. `entwurf_self` returns an authoritative pi-session or trusted meta-session identity envelope; `entwurf_v2` requires an authoritative sender by default (#50 C4) — a plain external MCP host with no identity lane is refused unless the operator explicitly wires the documented anonymous hatch, and even then it is never replyable.
|
|
79
|
-
- **Session persistence** — re-attaches pi to the same remote ACP session. Does not hydrate backend transcripts into pi history.
|
|
34
|
+
### Direct — ordinary shell or tmux
|
|
80
35
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- **Direct** — `npm install -g @junghanacs/entwurf`, then `entwurf setup <project>`. This is the
|
|
86
|
-
general route: it composes every harness it FINDS on the host — pi, Claude Code, Codex, Copilot,
|
|
87
|
-
Antigravity, the ACP rail — and reports each one PASS / SKIP / FAIL. Use it in an ordinary shell,
|
|
88
|
-
in tmux, anywhere.
|
|
89
|
-
- **Herdr workbench** — `herdr plugin install` from inside a Herdr session. It activates Entwurf for
|
|
90
|
-
the pi and Claude Code sessions Herdr has integrated, and only those. Contract, evidence grades
|
|
91
|
-
and its refusals: [plugins/herdr/README.md](plugins/herdr/README.md).
|
|
92
|
-
|
|
93
|
-
Neither route installs a harness, a subscription, or a login (Hard Rule 17). Everything below this
|
|
94
|
-
paragraph describes the direct route.
|
|
95
|
-
|
|
96
|
-
**Platform evidence, in one breath.** The Entwurf-only install surface on
|
|
97
|
-
macOS is CERTIFIED (CI) (`macos-install-surface`). On macOS every
|
|
98
|
-
garden-native harness rail, marker/request join, ACP turn, and mux is NOT CERTIFIED —
|
|
99
|
-
pending physical host. On Linux the released harness rails are the certified axis;
|
|
100
|
-
Codex joined them in 0.21.0. Its explicit-home first admission was measured on 2026-09-12 — a real Pi
|
|
101
|
-
outside `codex` opened Codex and outbound Pi inside it, with exact callbacks and delivery both ways —
|
|
102
|
-
and the release acceptance that followed closed `check:full`, the LIVE release gate at MUST 24/0/0,
|
|
103
|
-
and a 475/475 qualification.
|
|
104
|
-
Unrestricted attached-TUI placement
|
|
105
|
-
is unsupported rather than a release blocker.
|
|
106
|
-
Native Windows is UNSUPPORTED.
|
|
107
|
-
CERTIFIED (CI) is weaker than a physical-host doctor green.
|
|
108
|
-
The npm package has no `os` restriction; that is installability, not a
|
|
109
|
-
support claim.
|
|
110
|
-
|
|
111
|
-
**macOS landed in 0.20.0.** That release added macOS support for Entwurf's
|
|
112
|
-
install surface: the npm package installs, `entwurf --help` and `entwurf
|
|
113
|
-
check-bridge` boot, and `entwurf setup` composes the integrations it finds.
|
|
114
|
-
The four installers that previously rejected Darwin by platform name no longer
|
|
115
|
-
do. That surface was validated only in macOS CI because the developers did not
|
|
116
|
-
have a physical Mac. So macOS native-harness rails—marker join, an ACP turn,
|
|
117
|
-
and mux included—are **NOT CERTIFIED — pending physical host**; setup writes
|
|
118
|
-
their wiring but reports them non-green. Linux's certified rail status is
|
|
119
|
-
unchanged.
|
|
120
|
-
|
|
121
|
-
On Darwin, `entwurf setup` that detects a harness is intentionally
|
|
122
|
-
non-green: the wiring WAS written and nothing failed to install, but the
|
|
123
|
-
harness rail is NOT CERTIFIED — pending physical host. That FAIL is not
|
|
124
|
-
an install defect. An Entwurf-only host (no harness on PATH) stays green.
|
|
125
|
-
|
|
126
|
-
A physical Mac can send host facts without installing or logging in:
|
|
127
|
-
`scripts/raw-macos-measure/probe.sh` (`/bin/sh`, ~3.5s). Measurement,
|
|
128
|
-
not certification.
|
|
129
|
-
|
|
130
|
-
`entwurf` is a neutral npm package first. Get the package, then run **`entwurf setup
|
|
131
|
-
<project>`** — one command, the same front door from an npm global install, an npm
|
|
132
|
-
project-local install, or a source checkout. It composes every harness it finds on the
|
|
133
|
-
host and reports each one PASS / SKIP / FAIL. You are not meant to assemble the parts by
|
|
134
|
-
hand; the per-harness installers further down are the repair surface for when one unit
|
|
135
|
-
needs to be redone alone. Pi is still the adapter that hosts the ACP plugin and live
|
|
136
|
-
control-socket surface, but the base install is **not** `pi install npm:...` anymore.
|
|
137
|
-
|
|
138
|
-
The package exposes six bins:
|
|
139
|
-
|
|
140
|
-
- `entwurf` → `run.sh` (installer, checks, native-bridge doctors/installers)
|
|
141
|
-
- `entwurf-bridge` → the MCP stdio launcher (`mcp/entwurf-bridge/start.sh`)
|
|
142
|
-
- `entwurf-statusline` → the Claude Code statusline renderer (`scripts/meta-bridge-statusline.sh`)
|
|
143
|
-
- `entwurf-agy-statusline` → the Antigravity garden-id statusline renderer (`scripts/agy-statusline.sh`)
|
|
144
|
-
- `entwurf-agy-imprint` → the Antigravity `PreInvocation` birth/sender hook (`scripts/agy-imprint.sh`)
|
|
145
|
-
- `entwurf-copilot-statusline` → the Copilot CLI garden-id footer renderer (`scripts/copilot-statusline.sh`)
|
|
146
|
-
|
|
147
|
-
The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.
|
|
148
|
-
|
|
149
|
-
Installing Entwurf installs **Entwurf only**: its package bytes, six bins, bridge, and
|
|
150
|
-
integration artifacts. It does not install `pi`, Claude Code, Copilot CLI, Codex, agy, omp, Cortex,
|
|
151
|
-
or any other harness runtime. Those are operator choices and may all be absent. The bridge also
|
|
152
|
-
does not provide credentials, tokens, subscription access, or an auth bypass; whatever an
|
|
153
|
-
operator-installed harness already trusts is what Entwurf can use. `setup` is composition, not
|
|
154
|
-
recruitment: it may wire a harness that is present, but never downloads one to make a matrix cell
|
|
155
|
-
look complete.
|
|
156
|
-
|
|
157
|
-
### From npm — user/global install
|
|
36
|
+
Prerequisites: **Node >=24.0.0**, Python 3 for `setup`, and any harnesses you choose
|
|
37
|
+
to use, already installed and authenticated. Pi is optional-by-presence, supported
|
|
38
|
+
range `>=1.0.0 <1.1`; Claude Code supported floor `>=2.1.217` is required for its
|
|
39
|
+
managed exec-hook lifecycle. pnpm is needed only for source-checkout setup.
|
|
158
40
|
|
|
159
41
|
```bash
|
|
160
42
|
npm install -g @junghanacs/entwurf
|
|
161
|
-
|
|
162
43
|
entwurf setup /path/to/your-project
|
|
163
44
|
entwurf check-bridge
|
|
164
45
|
```
|
|
165
46
|
|
|
166
|
-
`setup`
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`setup` writes every byte Entwurf owns, and three things are deliberately left to you. They
|
|
173
|
-
are the only manual steps between a fresh install and calling a Codex sibling, and all three are
|
|
174
|
-
answers only you can give:
|
|
175
|
-
|
|
176
|
-
1. **Trust the birth hook once, in a visible plain Codex.** Answer `Trust all and continue`
|
|
177
|
-
and send one first turn. Nobody can do this for you: the receipt is the vendor's record of
|
|
178
|
-
*your* security decision, and writing it ourselves would turn a prompt into a silent
|
|
179
|
-
install. Until it exists, `setup` and `doctor-codex-birth` stay honestly non-green and say
|
|
180
|
-
exactly this.
|
|
181
|
-
2. **Start the app-server, in a detached tmux session that is NOT where you work.**
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
entwurf codex-app-server
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
One command for the vendor string nobody wants to type. It `exec`s
|
|
188
|
-
`codex app-server --listen unix://<default socket>` right there — so Ctrl-C is yours, and
|
|
189
|
-
Entwurf neither supervises nor restarts it. The session matters because the MCP bridge is
|
|
190
|
-
this server's child and inherits its `TMUX`: that tmux server is the one caller-seat
|
|
191
|
-
lookups read. The launcher prints which seat it got rather than guessing one for you.
|
|
192
|
-
3. **Answer the folder-consent prompt once for each directory you open siblings in.** Codex
|
|
193
|
-
records a direct decision per EXACT directory — not the parent, not the git root — and a
|
|
194
|
-
directory with no answer anywhere opens a consent screen instead of running a first turn.
|
|
195
|
-
`entwurf_fresh_call` prints `codex-launch-cwd-undecided` and opens the window anyway — the
|
|
196
|
-
screen is self-repairing, and answering it once is the whole fix. (Two neighbours differ: a
|
|
197
|
-
project `.codex` layer can consent with no entry at all, and a directory inside an explicitly
|
|
198
|
-
untrusted project is noted as `codex-launch-cwd-untrusted-ancestor` because the vendor answers
|
|
199
|
-
that one with an error naming the repository root instead of a prompt.) You can also do it up
|
|
200
|
-
front:
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
codex -C <the directory you will launch siblings in> # answer the prompt, then quit
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Same reason as step 1: it is your security decision, and writing it ourselves would turn a
|
|
207
|
-
prompt into a silent install.
|
|
208
|
-
|
|
209
|
-
Then `entwurf_fresh_call` with `backend: "codex"` works. Attaching your own visible Codex TUI
|
|
210
|
-
(`codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"`) is optional
|
|
211
|
-
and independent — a sibling Entwurf opens brings its own.
|
|
212
|
-
|
|
213
|
-
> **Observation, not a verb (#95).** That `--remote` string is exactly as unfriendly as the
|
|
214
|
-
> `--listen` one was, and it has no managed spelling. It is deliberately left alone: it is how
|
|
215
|
-
> an operator attaches their OWN terminal, not how a sibling is opened, and `entwurf_fresh_call`
|
|
216
|
-
> already spells it internally for the siblings it launches. Whether the operator-facing half
|
|
217
|
-
> deserves its own verb is open.
|
|
218
|
-
|
|
219
|
-
### From npm — project-local install
|
|
220
|
-
|
|
221
|
-
```bash
|
|
222
|
-
cd /path/to/your-project
|
|
223
|
-
npm install --save-dev @junghanacs/entwurf
|
|
224
|
-
|
|
225
|
-
npx entwurf setup .
|
|
226
|
-
npx entwurf check-bridge
|
|
227
|
-
```
|
|
47
|
+
`setup` composes each harness it finds and reports PASS / SKIP / FAIL. An absent
|
|
48
|
+
harness is an explicit SKIP; a detected incomplete/below-floor integration is a
|
|
49
|
+
named FAIL with nonzero exit, not a false green. `check-bridge` proves the installed
|
|
50
|
+
MCP bytes boot and list tools without backend auth; it does **not** certify a real
|
|
51
|
+
model turn or native receive rail.
|
|
228
52
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
53
|
+
[Clean-host setup](./docs/setup-clean-host.md) owns project-local/source installs,
|
|
54
|
+
config paths, optional backends, doctors, upgrade/repair and uninstall.
|
|
55
|
+
Codex also needs your own hook-trust decision, app-server and exact-directory
|
|
56
|
+
consent: [the operator steps](./docs/setup-clean-host.md#three-operator-decisions-before-fresh).
|
|
57
|
+
Plain MCP registration alone does not make a garden citizen.
|
|
234
58
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
59
|
+
**Platform bounds:** Linux is the certified harness-rail axis. macOS Entwurf-only
|
|
60
|
+
installation is **CERTIFIED (CI)**, weaker than a physical-host doctor; its native
|
|
61
|
+
harness rails, ACP and mux remain **NOT CERTIFIED — pending physical host**.
|
|
62
|
+
Detected harness wiring on Darwin can therefore be written while setup stays
|
|
63
|
+
non-green. Native Windows is **UNSUPPORTED**. The package has no npm `os`
|
|
64
|
+
restriction; that is not certification of every platform's harness rails.
|
|
240
65
|
|
|
241
|
-
|
|
242
|
-
`node_modules/.bin/entwurf-bridge` — see
|
|
243
|
-
[External MCP registration](#external-mcp-registration).
|
|
244
|
-
|
|
245
|
-
### From source — development clone
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
|
|
249
|
-
cd ~/repos/gh/entwurf
|
|
250
|
-
|
|
251
|
-
./run.sh setup /path/to/your-project
|
|
252
|
-
./run.sh check-bridge
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are
|
|
256
|
-
optional-by-presence: `setup` runs the frozen dependency install, then composes what the operator
|
|
257
|
-
already installed — a compatible `pi` (`>=0.87.1 <0.88`), Claude Code, agy, and the Copilot CLI
|
|
258
|
-
each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP,
|
|
259
|
-
and a detected harness that cannot be completed (including a below-floor `pi`) is a named FAIL
|
|
260
|
-
that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
|
|
261
|
-
store. It also exposes stable commands under `~/.local/bin`, including `entwurf` → this
|
|
262
|
-
checkout's `run.sh`, so managed Copilot fresh does not depend on an unrelated global npm/pnpm
|
|
263
|
-
installation. A detected `copilot` composes all four of its native units (birth → MCP →
|
|
264
|
-
receiver → visible footer) in one go. Package consumers
|
|
265
|
-
run the same `entwurf setup <project>` through their npm-provided bin: installed mode is decided
|
|
266
|
-
by name first, skips the source-only pnpm bootstrap entirely, and reports the stable commands as
|
|
267
|
-
already provided by npm bin linking.
|
|
268
|
-
|
|
269
|
-
The pi user-scope registration is ONE shared entry with a recorded owner (#86 C2): installing
|
|
270
|
-
from a second checkout or npm root does not silently steal it — normal `install`/`setup` refuse
|
|
271
|
-
with zero settings bytes written, `entwurf takeover-user-scope` is the operator-explicit move,
|
|
272
|
-
`entwurf doctor-pi-package` names the ownership verdict, and `entwurf remove-user-scope` is
|
|
273
|
-
same-owner-only. The full contract — atomicity across the package and provider halves, the
|
|
274
|
-
split verdict over an operator's own override, legacy adoption, and the `managedSettingsPath`
|
|
275
|
-
binding — is [docs/setup-clean-host.md §1.1](./docs/setup-clean-host.md#11-user-scope-ownership-one-shared-registration-one-recorded-owner).
|
|
276
|
-
|
|
277
|
-
A development clone runs the bridge source through Node's strip-types path;
|
|
278
|
-
an npm-installed package runs the prebuilt JS under `mcp/entwurf-bridge/dist/`
|
|
279
|
-
because Node refuses to strip `.ts` files under `node_modules`. The dev launcher's
|
|
280
|
-
source path means `./run.sh check-bridge` needs no build. After `git pull`, however,
|
|
281
|
-
run `pnpm install` when the lockfile changed and run `pnpm run build-bridge` before
|
|
282
|
-
artifact-consuming checks such as `./run.sh check-bridge-delivery`: `dist/` is
|
|
283
|
-
gitignored and may be absent or stale immediately after a checkout or pull.
|
|
284
|
-
|
|
285
|
-
### Pi adapter / ACP plugin lane
|
|
286
|
-
|
|
287
|
-
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
288
|
-
physical host (control sockets, mux, ACP plugin hosting).
|
|
289
|
-
|
|
290
|
-
To use the `entwurf` provider inside pi, install a compatible pi binary
|
|
291
|
-
separately (`@earendil-works/pi-coding-agent >=0.87.1 <0.88`). Then point pi at
|
|
292
|
-
the npm-installed package or development clone:
|
|
293
|
-
|
|
294
|
-
```bash
|
|
295
|
-
# global npm install path
|
|
296
|
-
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
|
|
297
|
-
|
|
298
|
-
# project-local install path
|
|
299
|
-
pi -e ./node_modules/@junghanacs/entwurf --list-models entwurf
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
For daily operator sessions, launch pi with `--entwurf-control` — no id
|
|
303
|
-
injection; the meta-record mints the garden address (see [Garden launcher](#garden-launcher)). Older pi
|
|
304
|
-
versions may silently miss the provider/extension surface, so treat the pi floor
|
|
305
|
-
as release-critical for the ACP/plugin lane. A host that only uses
|
|
306
|
-
`entwurf-bridge` from Claude Code / Copilot / Codex / Antigravity / OMP does not need pi at all for
|
|
307
|
-
delivery: no `entwurf_v2` rail launches a pi process. OMP is a pi fork, but it is its own binary and
|
|
308
|
-
resolves its own agent directory, so that lineage does not reintroduce a `pi` requirement either. That external-only shape works with the same
|
|
309
|
-
`setup` command: pi is optional-by-presence there, so a pi-less host simply gets an explicit pi
|
|
310
|
-
SKIP while the detected harnesses are composed.
|
|
311
|
-
|
|
312
|
-
### Native harness repair and doctors
|
|
313
|
-
|
|
314
|
-
A plain MCP registration exposes the bridge tools; a **garden-native** session also
|
|
315
|
-
needs entwurf's lifecycle and identity unit. `setup` composes every user-scope unit
|
|
316
|
-
for each detected harness, including Codex's birth hook — its paths are the operator's
|
|
317
|
-
own, so nothing here asks for root. What setup cannot do is answer the vendor's one-time
|
|
318
|
-
hook-trust prompt: only the operator can, in their own visible Codex, so a first setup on
|
|
319
|
-
a Codex host writes every byte and stays honestly non-green until they have. This is the
|
|
320
|
-
repair surface: each unit has its own installer, doctor, and inverse.
|
|
321
|
-
|
|
322
|
-
- **Claude Code** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — `install-meta-bridge`, `doctor-meta-bridge`.
|
|
323
|
-
- **Antigravity / agy** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
|
|
324
|
-
- **GitHub Copilot CLI** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — 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-*`.
|
|
325
|
-
- **OMP (`omp`)** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — 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.
|
|
326
|
-
- **OpenAI Codex CLI** (supported in 0.21.0 on Linux; macOS NOT CERTIFIED — pending physical host) — `install-codex-birth` publishes the `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`, all operator-owned; it owns **that declaration, not the file** — another integration's `SessionStart` group coexists (the vendor keys trust per `<path>:<event>:<group>:<handler>`), so install appends, uninstall splices only ours out, foreign bytes survive both, and `doctor-codex-birth` reports neighbours in a FOREIGN section it certifies nothing in; the vendor trust receipt is read at the index our declaration was measured at, is the operator's single answer, and `doctor-codex-birth` reports it as its own axis (present, or red with the exact instruction — never computed, never written); `install-codex-mcp` owns `[mcp_servers.entwurf-bridge]`, including the `env_vars` boundary for `CODEX_HOME`, Entwurf garden/control roots, and the app-server's `TMUX`/`TMUX_PANE`; `install-codex-statusline` owns `thread-title` in `tui.status_line`; `install-codex-terminal-title` owns `thread-id` in `tui.terminal_title`. Each has a matching doctor and inverse. **One Codex precondition is the vendor's own and no installer supplies it: the directory a sibling starts in must already be answered in this Codex.** A direct consent decision is recorded per exact directory on this rail — not the parent, not the git root — and a directory the vendor has no answer for at all opens a consent screen instead of running a first turn, so `entwurf_fresh_call` prints `codex-launch-cwd-undecided` and opens the window anyway; that screen is self-repairing, and answering it once teaches the vendor the directory for good. **Nothing here refuses a launch** — the note is a diagnostic, and the one caller that treats it as a precondition is `smoke-codex-fresh-live`, where nobody is at the keyboard. Two neighbouring cases read differently: a directory answered `untrusted` is not noted at all (the vendor skips its screen on this rail, so that turn starts), and a directory INSIDE an explicitly untrusted project is noted as `codex-launch-cwd-untrusted-ancestor` because the vendor answers that one with an error naming the repository root rather than a prompt. The check is a narrow local read of the operator's own config, never the vendor's verdict: the vendor also consents through project layers it reads from its app-server, and everything the check cannot see stays silent. The operator owns the app-server and seats it wherever they like; Entwurf never creates or supervises it. Omitted fresh placement follows the CALLER, never the backend being opened: an explicit `placement` always wins, then a Codex CALLER opens beside its own TUI pane — located by the `thread-id` that pane's terminal title carries, with 0 or 2+ matching panes refused and no fallback — then the caller's own session. (#95 first shipped a fixed `codex` home for omitted-placement Codex TARGETS; #95 D1 retired it on 2026-09-16 once the anchor made the room unnecessary.) A pane title is a placement input only, never an address, liveness or delivery fact. Exact 0.153.4 source's missing per-client carrier still bounds placing a sibling beside a TUI whose thread nobody named as unsupported.
|
|
327
|
-
|
|
328
|
-
Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
|
|
329
|
-
walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
|
|
330
|
-
|
|
331
|
-
#### Launching Copilot as a garden citizen — `entwurf copilot`
|
|
332
|
-
|
|
333
|
-
Copilot only scans for extensions when its CLI is started with
|
|
334
|
-
`COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS`, and when that flag is absent it skips the
|
|
335
|
-
scan **silently** — no error, no log line, no receiver. entwurf does not own your shell
|
|
336
|
-
and writes nothing to your rc files, so it owns one invocation instead:
|
|
337
|
-
|
|
338
|
-
```bash
|
|
339
|
-
entwurf copilot # managed launch, in this terminal
|
|
340
|
-
entwurf copilot -p "…" --model gpt-5.4
|
|
341
|
-
copilot # the plain vendor CLI, untouched
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
`entwurf copilot` execs the vendor CLI in your current terminal — same cwd, same pid,
|
|
345
|
-
same exit status, no tmux window and no new citizen (a Copilot session is still born on
|
|
346
|
-
its first prompt). Before it launches it verifies that the receiver unit it is about to
|
|
347
|
-
promise is actually installed, and refuses with `entwurf install-copilot-receive` if it
|
|
348
|
-
is not, rather than starting a session that can never be delivered to.
|
|
349
|
-
|
|
350
|
-
**Running it is your consent to its profile.** For that one invocation it adds:
|
|
351
|
-
|
|
352
|
-
| Injected | When |
|
|
353
|
-
|---|---|
|
|
354
|
-
| `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` | always — your other feature-flag tokens are preserved, in order, deduplicated |
|
|
355
|
-
| `--model auto` | only when you passed no `--model` |
|
|
356
|
-
| `--yolo` | only when you passed no explicit permission or surface policy flag |
|
|
357
|
-
|
|
358
|
-
The escape hatch is simply to state your own policy: any of `--yolo`, `--allow-all`,
|
|
359
|
-
`--allow-all-tools`, `--allow-all-paths`, `--allow-all-urls`, `--allow-tool`,
|
|
360
|
-
`--deny-tool`, `--allow-url`, `--deny-url`, `--available-tools` or `--excluded-tools`
|
|
361
|
-
suppresses the injected `--yolo`. The narrowing flags are in that list on purpose — adding
|
|
362
|
-
`--yolo` beside your `--allow-url=…` would silently widen exactly what you were
|
|
363
|
-
restricting. `--allow-all-mcp-server-instructions` (prompt content, not authorization) and
|
|
364
|
-
`--autopilot` (a mode) are deliberately not policy flags. Everything you pass is forwarded
|
|
365
|
-
byte-identically, injected defaults land before any `--` terminator, and nothing after the
|
|
366
|
-
terminator is read as policy. Nothing is written to disk; run plain `copilot` and none of
|
|
367
|
-
this applies.
|
|
368
|
-
|
|
369
|
-
Why `--yolo` is the default: the managed lane exists so a sibling can wake an idle session,
|
|
370
|
-
and draining the mailbox with `entwurf_inbox_read` costs two interactive approvals under
|
|
371
|
-
the default permission prompts — which an idle, unattended session is not there to answer.
|
|
372
|
-
|
|
373
|
-
Claude Code uses the supported floor `>=2.1.217`; older versions silently discard the
|
|
374
|
-
exec-hook `args`, so install and doctor fail loud rather than falling back. After any
|
|
375
|
-
upgrade, rerun `entwurf setup <project>` — it re-composes every detected harness, all four
|
|
376
|
-
Copilot units included — and restart its existing processes; reach for a single
|
|
377
|
-
`install-*` only when one unit needs repair on its own. A claimed Claude host is certified
|
|
378
|
-
only when a **new** session using the
|
|
379
|
-
installed artifact makes `doctor-meta-bridge` exit 0 with the live owner join.
|
|
380
|
-
|
|
381
|
-
The Claude meta-bridge rail is CERTIFIED on Linux. On macOS it is
|
|
382
|
-
NOT CERTIFIED — pending physical host. The Darwin install fence is
|
|
383
|
-
open; uninstall was already open. Detailed diagnosis and clean-host
|
|
384
|
-
steps live in
|
|
385
|
-
[docs/setup-clean-host.md](./docs/setup-clean-host.md).
|
|
386
|
-
|
|
387
|
-
The active citizen store is V3-only. A store that fails certification is never
|
|
388
|
-
silently migrated: quiesce the native sessions, run `entwurf meta-bridge-fresh-cut`,
|
|
389
|
-
then reinstall. The cut archives routing records only—never native transcripts or
|
|
390
|
-
external memory—and no runtime reads the archive. Exit meanings and the complete
|
|
391
|
-
operator contract are in [docs/fresh-cut-policy.md](./docs/fresh-cut-policy.md).
|
|
392
|
-
|
|
393
|
-
`entwurf check-bridge` proves the MCP surface boots without backend auth. A real ACP
|
|
394
|
-
turn requires `LIVE=1 entwurf smoke-acp-provider-live`; the full release protocol and
|
|
395
|
-
host evidence boundaries are [VERIFY.md](./VERIFY.md) and [BASELINE.md](./BASELINE.md).
|
|
396
|
-
|
|
397
|
-
> **Extension set — do not filter.** The ACP provider, `entwurf-control`, and
|
|
398
|
-
> `model-lock` extensions ship as one set. Disable the package as a whole rather than
|
|
399
|
-
> filtering individual entries into a partially wired state.
|
|
400
|
-
|
|
401
|
-
### Backend prerequisites
|
|
402
|
-
|
|
403
|
-
**Claude is the reference ACP backend.** The Claude ACP server package (`@agentclientprotocol/claude-agent-acp`, pinned with `@agentclientprotocol/sdk`) ships as a pinned `dependency` of `entwurf`; backend authentication still belongs to the operator's local `claude` CLI / runtime. Once the bridge is installed, the resolver picks the ACP server in this order:
|
|
404
|
-
|
|
405
|
-
1. **`CLAUDE_AGENT_ACP_COMMAND` env override** — explicit override for an alternative binary or a wrapper command.
|
|
406
|
-
2. **`require.resolve(...)` against the bundled package dependency** (`@agentclientprotocol/claude-agent-acp`). This is the default path; no extra global install needed.
|
|
407
|
-
3. **`PATH:claude-agent-acp` fallback** — used when the package resolution fails (e.g. a hand-edited `node_modules`).
|
|
408
|
-
|
|
409
|
-
The curated model registry exposes unprefixed Claude ids — `claude-opus-5`, `claude-sonnet-5`,
|
|
410
|
-
`claude-fable-5-1` — plus the `cortex-` rows below.
|
|
411
|
-
Codex is not an ACP backend. Its native-citizen lane, supported in 0.21.0, is independent so the
|
|
412
|
-
session retains Codex's native tools, delegation, and work context: trust-gated hook birth, strict
|
|
413
|
-
request `_meta` identity, app-server probe, one-shot native-push, and visible fresh.
|
|
414
|
-
|
|
415
|
-
**Snowflake Cortex Code is the second ACP backend** (contract and audit:
|
|
416
|
-
[docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)). Curated ids are
|
|
417
|
-
`cortex-auto`, `cortex-claude-opus-5`, `cortex-claude-sonnet-5`, and
|
|
418
|
-
`cortex-openai-gpt-5.4`.
|
|
419
|
-
|
|
420
|
-
The operator supplies an authenticated `cortex` CLI and selects a connection with
|
|
421
|
-
`entwurfProvider.cortexConnection` or `ENTWURF_ACP_CORTEX_CONNECTION`. `CORTEX_HOME`
|
|
422
|
-
must be absent: the adapter refuses it because it would bypass the session-scoped
|
|
423
|
-
HOME containment. Explicit MCP servers are projected into the overlay-private
|
|
424
|
-
`cortex/mcp.json`; only the bridge receives the real operator HOME needed for the
|
|
425
|
-
garden store.
|
|
426
|
-
|
|
427
|
-
`check-acp-cortex` runs in `pnpm run check:full`. Real acceptance is deliberately on demand:
|
|
66
|
+
### Herdr workbench
|
|
428
67
|
|
|
429
68
|
```bash
|
|
430
|
-
|
|
69
|
+
herdr plugin install junghan0611/entwurf/plugins/herdr --yes
|
|
431
70
|
```
|
|
432
71
|
|
|
433
|
-
|
|
72
|
+
This is a separate consumer path: it activates only Herdr-integrated **pi and
|
|
73
|
+
Claude Code**, acquires the locked Entwurf runtime, and adds an observation pane.
|
|
74
|
+
It puts no Entwurf bins on PATH. The
|
|
75
|
+
[Herdr integration guide](https://github.com/junghan0611/entwurf/blob/main/plugins/herdr/README.md)
|
|
76
|
+
owns its prerequisites, first install → first use, evidence and explicit removal.
|
|
77
|
+
The plugin does not own delivery or the Herdr launch rail; a direct-installed
|
|
78
|
+
citizen inside Herdr selects that rail from its own process context too.
|
|
434
79
|
|
|
435
|
-
###
|
|
436
|
-
|
|
437
|
-
Works from terminals and from Emacs frontends that launch [pi-coding-agent](https://github.com/dnouri/pi-coding-agent).
|
|
438
|
-
|
|
439
|
-
<details>
|
|
440
|
-
<summary>Watch entwurf in Doom Emacs (1104×627 GIF, click to expand)</summary>
|
|
441
|
-
|
|
442
|
-

|
|
443
|
-
|
|
444
|
-
</details>
|
|
445
|
-
|
|
446
|
-
For a dedicated agent socket, pass the socket name:
|
|
447
|
-
|
|
448
|
-
```elisp
|
|
449
|
-
(setq pi-coding-agent-extra-args
|
|
450
|
-
'("--entwurf-control" "--emacs-agent-socket" "pi"))
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
The bridge exports the socket name to ACP children as `PI_EMACS_AGENT_SOCKET`, so skills call Emacs without hardcoding:
|
|
454
|
-
|
|
455
|
-
```bash
|
|
456
|
-
emacsclient -s "${PI_EMACS_AGENT_SOCKET:-server}" --eval '(...)'
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
## Settings
|
|
460
|
-
|
|
461
|
-
Reference shape lives in [`pi/settings.reference.json`](./pi/settings.reference.json). Minimum:
|
|
462
|
-
|
|
463
|
-
```json
|
|
464
|
-
{
|
|
465
|
-
"compaction": { "enabled": false },
|
|
466
|
-
"entwurfProvider": {
|
|
467
|
-
"appendSystemPrompt": false,
|
|
468
|
-
"settingSources": [],
|
|
469
|
-
"strictMcpConfig": true,
|
|
470
|
-
"showToolNotifications": true,
|
|
471
|
-
"tools": ["Read", "Bash", "Edit", "Write"],
|
|
472
|
-
"skillPlugins": [],
|
|
473
|
-
"permissionAllow": ["Read(*)", "Bash(*)", "Edit(*)", "Write(*)", "mcp__*"],
|
|
474
|
-
"mcpServers": {
|
|
475
|
-
"entwurf-bridge": {
|
|
476
|
-
"command": "/path/to/entwurf/mcp/entwurf-bridge/start.sh",
|
|
477
|
-
"args": []
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
`mcpServers` is the only ACP MCP injection path. In practice this repo is about the bundled `entwurf-bridge`, which carries pi capabilities into ACP-backed sessions — not about being a general MCP catalog. Invalid entries throw `McpServerConfigError` — broken tool state surfaces as broken tool state. `./run.sh install` writes the bundled `entwurf-bridge` entry and prunes the legacy bundled `session-bridge` entry from older installs.
|
|
485
|
-
|
|
486
|
-
`appendSystemPrompt: false` is intentional. Pi / AGENTS context rides the first-user augment; putting it into the Claude `_meta.systemPrompt` carrier can route OAuth sessions to metered "extra usage" billing.
|
|
487
|
-
|
|
488
|
-
**Which keys reach which backend.** `entwurfProvider` is one block for both backends, but its keys are not universal. `tools` / `permissionAllow` / `disallowedTools` / `settingSources` / `skillPlugins` / `appendSystemPrompt` are Claude's declaration surface: they do not shape a cortex session, which runs its own native tools and reaches MCP through the overlay-private `mcp.json` projection instead. They are not inert, though — the bridge still reads `tools` for its backend-invariant exclude-tools preflight and folds all of them into the config signature, so editing one still forces a fresh cortex session. `cortexConnection` is cortex-only. `mcpServers` is the one declaration surface that reaches both, each through its own transport. (`compaction` is a *top-level* pi key, not an `entwurfProvider` one — it is pi's own transcript policy and is not a backend knob at all; see §Compaction.) Unknown and retired keys are ignored rather than rejected, so a key aimed at the wrong backend fails silently: if a cortex session does not show the surface you configured, check that the key is one cortex actually consumes before suspecting the bridge.
|
|
489
|
-
|
|
490
|
-
### External MCP registration
|
|
491
|
-
|
|
492
|
-
`entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code,
|
|
493
|
-
Copilot CLI, Codex CLI, Antigravity, OMP). Two shapes exist and they are not interchangeable:
|
|
494
|
-
|
|
495
|
-
- **plain external MCP host** — no garden meta-record or sender marker. It can read the
|
|
496
|
-
surfaces, but `entwurf_v2` is **refused by default**: there is no authoritative sender.
|
|
497
|
-
- **garden-native session** — a trusted lifecycle hook minted a garden id, so it is
|
|
498
|
-
addressable and replyable by that id.
|
|
499
|
-
|
|
500
|
-
```bash
|
|
501
|
-
claude mcp add --scope user entwurf-bridge entwurf-bridge
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
Per-harness registration (Claude Code `~/.mcp.json`, Codex `~/.codex/config.toml`, the
|
|
505
|
-
managed `install-agy-*` surfaces, and OMP's managed `install-omp-mcp` into `<omp agent dir>/mcp.json`
|
|
506
|
-
— whose pinned server key is what shadows a borrowed Claude import, see
|
|
507
|
-
[docs/external-mcp-host.md](./docs/external-mcp-host.md) §OMP), the PATH/env boundary for
|
|
508
|
-
GUI-launched MCP servers, the
|
|
509
|
-
anonymous-sender hatch, and the full external/meta-session semantics are in
|
|
510
|
-
[docs/external-mcp-host.md](./docs/external-mcp-host.md).
|
|
511
|
-
For the maintained multi-harness setup and skill/command packaging details, see `agent-config`. See also the MCP entry in [Concept primer](#concept-primer), the sender envelope contract in [AGENTS.md](./AGENTS.md), and [Custom skills](#custom-skills) for the in-pi ACP skill surface.
|
|
512
|
-
|
|
513
|
-
## Per-backend operating surface
|
|
514
|
-
|
|
515
|
-
The Claude ACP backend keeps its native model / API / tools; entwurf shapes only what enters from pi. Claude honors an explicit `CLAUDE_CONFIG_DIR` export when set by the operator.
|
|
516
|
-
|
|
517
|
-
**Claude** uses `_meta.systemPrompt` for the engraving carrier (kept short and pure — billing-safe; rich operator context rides the first user message instead, see [Context carriers](#context-carriers)) and `CLAUDE_CONFIG_DIR` for a whitelist overlay so auth/runtime entries stay available while operator memory, hooks, agents, history, local settings, and project memory remain hidden. The overlay writes an explicit empty `hooks: {}` because Claude SDK organic compaction needs the configured-empty shape; no operator hook definitions are inherited. It also pins `permissions.defaultMode: "bypassPermissions"` so an unattended ACP turn cannot suspend on an interactive permission prompt; explicit `tools` / `disallowedTools` still constrain the callable surface and backend authentication remains the operator's. The four-tool baseline is `Read`, `Bash`, `Edit`, and `Write`; `permissionAllow` carries their allow declarations, and `Skill` is added automatically when `skillPlugins` is non-empty. Operator context cap override: `ENTWURF_ACP_CLAUDE_CONTEXT=<int>`.
|
|
518
|
-
|
|
519
|
-
Codex is not an ACP backend here. Its native-push lane, supported in 0.21.0, uses `SessionStart`
|
|
520
|
-
for birth, strict request `_meta` for who-sent, and a loaded app-server thread as the live route.
|
|
521
|
-
A Codex CALLER with placement omitted opens its sibling beside its own TUI pane, matched by the
|
|
522
|
-
`thread-id` its terminal title carries; a pane whose thread nobody named is never inferred.
|
|
523
|
-
|
|
524
|
-
Antigravity is also not an ACP backend. It is a native-push citizen: `PreInvocation` supplies birth/sender identity, `entwurf_v2` probes and direct-injects replies into the live conversation, and no mailbox/receiver marker is involved.
|
|
525
|
-
|
|
526
|
-
entwurf owns **no** memory layer at all — the ACP plugin's boundary explicitly excludes a memory DB (see `AGENTS.md` §ACP Plugin Boundary), and no backend is a memory authority for another. What this overlay does is narrower: Claude's native memory layer is pinned off so operator memory, project state, and history never leak into an ACP session. Whatever semantic-memory / Denote tooling an operator runs is their own skill surface on whichever harness hosts it — deliberately kept out of the MCP bridge, and not a pi privilege.
|
|
80
|
+
### Garden launcher
|
|
527
81
|
|
|
528
|
-
|
|
82
|
+
After direct setup, start a pi citizen in your project:
|
|
529
83
|
|
|
530
84
|
```bash
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
./run.sh smoke-agy-statusline-state # agy ambient garden-id install surface
|
|
536
|
-
./run.sh smoke-agy-hooks-state # agy PreInvocation birth hook
|
|
537
|
-
./run.sh check-agy-sender-identity # record-backed pid/start-key sender identity
|
|
538
|
-
./run.sh check-codex-native-push # deterministic Codex UDS probe + no-replay queue contract
|
|
539
|
-
./run.sh smoke-codex-config-state # Codex MCP/status-line install, doctor, and inverse
|
|
540
|
-
./run.sh check-codex-birth-hook # sandboxed payload + declaration: one V3 per top-level thread, the envelope's own axes, no markers, and the declaration leaf (normalized digest, selection by launcher command, byte-preserving splice)
|
|
541
|
-
|
|
542
|
-
# source-maintainer only — qualification snapshots the git work surface, and both
|
|
543
|
-
# commands are source-contract gates rather than installed operator checks:
|
|
544
|
-
./run.sh check-agy-permission-matrix # AGY permission contract space as a literal table (declared cells + stated exclusions)
|
|
545
|
-
./run.sh check-gate-manifests # the qualification HEAD alone: self-test + manifest-set validation + declared lane inventory, zero mutants run (in check:hermetic)
|
|
546
|
-
./run.sh check-gate-qualification # kill-proof: committed defect mutants must turn their gates red for the claimed reason
|
|
547
|
-
|
|
548
|
-
# agy LIVE acceptance — requires an already-running conversation:
|
|
549
|
-
LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live
|
|
550
|
-
|
|
551
|
-
# Codex loaded-thread probe (pre-amendment evidence; not first-release acceptance):
|
|
552
|
-
LIVE=1 CODEX_LIVE_THREAD_ID=<threadId> ./run.sh smoke-codex-native-push-live
|
|
553
|
-
|
|
554
|
-
# Codex caller-seat acceptance. The operator-owned app-server must sit in a session OTHER than
|
|
555
|
-
# the one the Pi/Codex pair runs in — that separation is what tells the caller-pane anchor apart
|
|
556
|
-
# from the app-server's inherited environment. This smoke ALSO needs its one stable launch
|
|
557
|
-
# directory answered once (manual step 3 above); VERIFY.md's Codex section owns that procedure,
|
|
558
|
-
# including how to derive the exact path instead of retyping it. Record all four coordinates:
|
|
559
|
-
LIVE=1 ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid> \
|
|
560
|
-
ENTWURF_CODEX_FRESH_MODEL=<codex-model> \
|
|
561
|
-
ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
562
|
-
./run.sh smoke-codex-fresh-live
|
|
563
|
-
# This accepts only a real visible Pi → visible Codex → visible Pi run: initial Pi in a session
|
|
564
|
-
# other than the app-server's, then Codex and its outbound Pi beside it. A fixture first leg may
|
|
565
|
-
# collect receipts but does not satisfy the topology. No arbitrary attached-TUI parity is claimed.
|
|
566
|
-
|
|
567
|
-
# ACP plugin LIVE acceptance — need the operator's local Claude auth/credit:
|
|
568
|
-
LIVE=1 ./run.sh smoke-acp-socket-citizen-live # turn-free socket citizenship (S1)
|
|
569
|
-
LIVE=1 ./run.sh smoke-acp-raw-turn-live # pinned ACP pipe + raw 1 turn (S2a)
|
|
570
|
-
LIVE=1 ./run.sh smoke-acp-overlay-live # config overlay + hooks:{} + tool meta (S2b)
|
|
571
|
-
LIVE=1 ./run.sh smoke-acp-provider-live # real pi provider path + progress/L3 (S2c/S2f)
|
|
572
|
-
LIVE=1 ./run.sh smoke-acp-session-reuse-live # process-scoped reuse + codeword recall (S2d)
|
|
573
|
-
LIVE=1 ./run.sh smoke-acp-carrier-augment-live # augment delivery + empty-carrier billing clean (S2e-1)
|
|
574
|
-
|
|
575
|
-
LIVE=1 ./run.sh release-gate /tmp/scratch --cut # the single cut gate (MUST + BEHAVIOR; --cut refuses any MUST SKIP)
|
|
576
|
-
LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live # Cortex is on-demand: the aggregate does not re-certify it
|
|
85
|
+
cd /path/to/your-project
|
|
86
|
+
entwurf pi
|
|
87
|
+
# equivalent when the package is registered (also the Herdr plugin's spelling):
|
|
88
|
+
pi --entwurf-control
|
|
577
89
|
```
|
|
578
90
|
|
|
579
|
-
`
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
(
|
|
583
|
-
unconditionally on `workflow_dispatch -f qualify=true` and a weekly schedule, and release-gate
|
|
584
|
-
carries it as a MUST step; a tag push runs no CI at all). A branch run whose body was filtered out
|
|
585
|
-
is not release evidence: the exact-SHA oracle requires the run it reads to carry that step
|
|
586
|
-
concluding success. A gate a
|
|
587
|
-
release touches must kill its known defect for the claimed `[QK:<claim>]` reason —
|
|
588
|
-
the descriptions above name what each smoke covers, and no check count is quality
|
|
589
|
-
evidence on its own. Gate qualification needs the git work surface, while the matrix
|
|
590
|
-
is the source permission-contract gate; both run from a clone, never as a post-install
|
|
591
|
-
operator step.
|
|
91
|
+
`entwurf pi` execs your own pi with the control flag. A plain pi may load the
|
|
92
|
+
package without becoming a citizen. The record mints the garden id; do not inject
|
|
93
|
+
a session id. Native harness launch and manual Herdr argv instructions live in
|
|
94
|
+
[setup §2–§5](./docs/setup-clean-host.md#2-optional-pi-adapter--acp-plugin).
|
|
592
95
|
|
|
593
|
-
|
|
96
|
+
In that **harness session**, ask the agent to:
|
|
594
97
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
<your-plugin-root>/
|
|
599
|
-
├── .claude-plugin/
|
|
600
|
-
│ └── plugin.json
|
|
601
|
-
└── skills/
|
|
602
|
-
└── <skill-name>/
|
|
603
|
-
└── SKILL.md
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
A self-contained example lives at [`pi/skill-plugin-example/`](./pi/skill-plugin-example/). Put plugin roots anywhere on disk except under `~/.pi/agent/` (pi's internal cache).
|
|
98
|
+
1. Call `entwurf_self` to confirm its authoritative identity.
|
|
99
|
+
2. Call `entwurf_peers` to discover existing citizens and their liveness facts.
|
|
100
|
+
3. Pick a live garden id and call `entwurf_v2` with a message, for example:
|
|
607
101
|
|
|
608
102
|
```json
|
|
609
103
|
{
|
|
610
|
-
"
|
|
611
|
-
|
|
612
|
-
|
|
104
|
+
"target": "<garden-id from entwurf_peers>",
|
|
105
|
+
"intent": "fire-and-forget",
|
|
106
|
+
"message": "Hello from a sibling.",
|
|
107
|
+
"wants_reply": true
|
|
613
108
|
}
|
|
614
109
|
```
|
|
615
110
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
111
|
+
These are tools, not shell subcommands. Pi calls
|
|
112
|
+
`mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The server exposes **eight**
|
|
113
|
+
verbs; a pi citizen has **six active + two hidden** (`entwurf_inbox_read` and
|
|
114
|
+
`entwurf_register_native`), since it has no mailbox and its own extension births it.
|
|
115
|
+
The server key stays `entwurf-bridge`. An operator/trusted-project MCP entry with
|
|
116
|
+
that normalized namespace can replace the registration by Pi's own precedence;
|
|
117
|
+
`/mcp` shows it, and Entwurf does not scan or fight that choice.
|
|
623
118
|
|
|
624
119
|
## Entwurf orchestration
|
|
625
120
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resume, cross-process MCP dispatch across a different cwd, and a live peer greeting. It is **archived evidence**: it shows a resume verb this repo no longer has (see `demo/README.md`).
|
|
631
|
-
|
|
632
|
-
<details>
|
|
633
|
-
<summary>Watch (2131×1142 GIF, click to expand)</summary>
|
|
634
|
-
|
|
635
|
-

|
|
636
|
-
|
|
637
|
-
</details>
|
|
638
|
-
|
|
639
|
-
Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted `meta-session` envelope, but **`replyable` is a fact its own rail decides, not a consequence of being trusted** — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. `entwurf_self` also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling. **v2 never gates on it:** a `wants_reply` from an external/non-replyable caller is passed through and surfaced honestly beside that sender's `replyable: false`, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 `entwurf_send` did reject it; that behaviour went with the verb.)
|
|
640
|
-
|
|
641
|
-
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes eight tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / native-push (Antigravity, Codex), and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — WHERE it opens is a fact about the calling process, not a parameter: inside herdr (`HERDR_ENV=1`) it opens a NEW TAB in the caller's own workspace, without taking focus, and admits only `pi` and `claude-code`, refusing `placement` by name; everywhere else it launches one of five fixed backends (`pi`, `claude-code`, `copilot`, `omp`, `codex`) as a visible window on the operator's own tmux server. There is no fallback between the two — an incomplete herdr context is refused by its herdr reason rather than quietly opening a tmux window nobody inside herdr can see. Everything that follows in this paragraph describes the tmux rail; the herdr rail's coordinates, refusals and reclaim are owned by [docs/herdr-launch-rail.md](./docs/herdr-launch-rail.md). On tmux it requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd) and one optional `placement.tmuxSession` naming an EXISTING session on that server to open the sibling in instead of the caller's own (the operator's project seat — nothing is ever created: an absent session is `tmux-session-missing` and a name outside the addressed grammar `[A-Za-z0-9][A-Za-z0-9_-]*` is `tmux-session-name-invalid`, with no window and no session left behind. The two inputs never infer each other, and the positive form of that matters more than the prohibition: **with `cwd` omitted, a seated sibling starts in THIS agent's own working directory**, measured — it does not inherit the target session's path or its active pane, so naming the `org` seat does not put the sibling in the `org` project. Pass `cwd` when you want a different directory. entwurf never resolves that path: it hands the literal string to tmux, so with a SYMLINKED directory the receipt echoes what you asked for while the record, the transcript path, the callback envelope and `pane_current_path` all show the path the OS resolved it to — two spellings of one place, and the resolved one is what `entwurf_resume_call` later reopens), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units, and an `omp` launch carries its task in the `--entwurf-bootstrap` payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. Omitted placement follows the CALLER rather than the backend being opened: a Codex CALLER opens its sibling beside its own TUI pane, located by the `thread-id` that pane's terminal title carries (0 or 2+ matching panes reject with no fallback); every other caller opens in its own seat. (0.21.0 shipped a backend-selected default instead — an omitted-placement `codex` TARGET resolved a fixed operator-owned session named `codex` — and #95 D1 retired it on 2026-09-16.) Any of the five can still be sent to an operator-owned existing seat with an explicit `placement.tmuxSession`, and named `claude`/`pi` seats remain available that way, but they are not omitted-placement defaults. Read tmux here as the VISIBLE LAUNCH SEAT, never as an address: the garden id is the address, and it arrives in the callback envelope. Sibling symmetry means each backend keeps its own transcript, auth, native tools and visibility — not that the transports are made identical. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd/seat plus tmux coordinates — including the RESOLVED target session id, which is where the window actually is — and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
|
|
121
|
+
One narrow dispatch capability connects independent siblings, not an agent factory.
|
|
122
|
+
The record is the **sole address authority**. A pane, socket, native transcript id,
|
|
123
|
+
model or process marker is a carrier or observation, never a second address axis.
|
|
124
|
+
Liveness is read at dispatch time; an id's shape does not tell you its transport.
|
|
642
125
|
|
|
643
126
|
### `entwurf_v2` — canonical dispatch verb
|
|
644
127
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
| target state | intent | transport |
|
|
128
|
+
| What you want | Tool | What its receipt means |
|
|
648
129
|
|---|---|---|
|
|
649
|
-
|
|
|
650
|
-
|
|
|
651
|
-
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
130
|
+
| Message, reply or hand off to an existing garden id | `entwurf_v2 {target, intent:"fire-and-forget", message}` | Receiver acceptance boundary, mailbox enqueue or native injection; not completion |
|
|
131
|
+
| Open a new visible sibling | `entwurf_fresh_call {backend, model, task, cwd?}` | Launch receipt only; the sibling's nonce callback envelope supplies its new garden id |
|
|
132
|
+
| Reopen a dormant pi citizen under its own id | `entwurf_resume_call {target}` | Separate LAUNCH and socket OBSERVATION receipts; no turn is run |
|
|
133
|
+
|
|
134
|
+
**Delivery starts no process.** A live pi receives over its record-addressed Unix
|
|
135
|
+
control socket; an armed self-fetch citizen (Claude Code, Copilot, OMP) through its
|
|
136
|
+
mailbox; a probe-alive native-push citizen (Antigravity, Codex) through its native
|
|
137
|
+
conversation/thread. Dormant pi, undeliverable mailboxes, dead/indeterminate native
|
|
138
|
+
push and record-less sockets reject honestly. Only control-socket dispatch takes
|
|
139
|
+
the per-target lock; the other rails use their own deliverability/probe evidence.
|
|
140
|
+
|
|
141
|
+
A control-socket `sent` means a turn was triggered, not that the model read the
|
|
142
|
+
text. `queued-steer` and `queued-follow-up` are volatile process queues, with no
|
|
143
|
+
FIFO between them; abort drops them. `accepted-unknown-boundary` is acceptance
|
|
144
|
+
we cannot classify, never a reason to retry. A mailbox enqueue is a durable file
|
|
145
|
+
receipt. `wants_reply` is etiquette, not waiting, polling or ownership; replyability
|
|
146
|
+
is a live rail fact, not merely a consequence of trusted identity.
|
|
147
|
+
[DELIVERY](./DELIVERY.md#control-socket-acceptance-boundaries-120) owns these distinctions.
|
|
148
|
+
|
|
149
|
+
**Mux launches; it never delivers.** Inside Herdr, fresh opens a new unfocused tab
|
|
150
|
+
in the caller's workspace, pi/Claude Code only, with no tmux fallback. Outside,
|
|
151
|
+
it opens pi/Claude Code/Copilot/OMP/Codex visibly on the caller's own tmux server.
|
|
152
|
+
The model is required; optional `cwd` is a literal absolute directory, omitted or
|
|
153
|
+
empty means the caller's cwd. Optional `placement.tmuxSession` names an existing
|
|
154
|
+
tmux session (not permitted inside Herdr), never a new session or a cwd lookup.
|
|
155
|
+
Omitted tmux placement follows the caller: Codex beside its own TUI pane matched
|
|
156
|
+
by thread-id, everyone else in their own session. Missing/ambiguous context refuses.
|
|
157
|
+
A launch receipt is neither the callback address nor proof of task processing.
|
|
158
|
+
Resume is pi-only, tmux-only, same id, record-supplied transcript/model/provider/cwd;
|
|
159
|
+
a live or non-pi target refuses, and an unobserved window stays visible without retry.
|
|
160
|
+
Exact boundaries: [tmux launch](https://github.com/junghan0611/entwurf/blob/main/docs/mux-launch-rail.md)
|
|
161
|
+
and [Herdr launch](https://github.com/junghan0611/entwurf/blob/main/docs/herdr-launch-rail.md).
|
|
669
162
|
|
|
670
|
-
|
|
671
|
-
pi --entwurf-control
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
**Opening pi inside herdr.** herdr starts a supported agent with its canonical executable and no
|
|
675
|
-
extra argv — measured on herdr 0.9.0, `herdr agent start <name> --kind pi --pane <id>` runs plain
|
|
676
|
-
`pi`. Citizenship is argv-gated (`pi-extensions/entwurf-control.ts`), so a pi opened that way is a
|
|
677
|
-
running pi and *not* a garden citizen: no record, no control socket, no garden id. Pass the flag
|
|
678
|
-
through herdr's own argv passthrough:
|
|
679
|
-
|
|
680
|
-
```bash
|
|
681
|
-
herdr agent start <name> --kind pi --pane <pane-id> -- --entwurf-control
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
A sibling that entwurf itself opens already carries the flag; this is only for a pi you start from
|
|
685
|
-
herdr by hand. The claude axis needs nothing extra — its record is minted by the meta-bridge plugin's
|
|
686
|
-
own `SessionStart` hook, which entwurf installs and herdr does not touch. Where such a citizen is
|
|
687
|
-
visible is reported as evidence in `entwurf_peers` (`placement=herdr <pane>`), never as an address:
|
|
688
|
-
[`docs/mux-launch-rail.md`](./docs/mux-launch-rail.md) §7-a.
|
|
689
|
-
|
|
690
|
-
The wiring those two axes need does not have to be installed by hand on a Herdr host: the Entwurf
|
|
691
|
-
Herdr plugin's install-time build reads Herdr's own `integration status`, and for every harness in
|
|
692
|
-
`{pi, claude-code}` that Herdr reports as integrated it acquires an Entwurf runtime at a stable
|
|
693
|
-
Entwurf-owned address and registers the user-scope wiring against it — no Entwurf clone, no manual
|
|
694
|
-
`npm install`. `herdr plugin install junghan0611/entwurf/plugins/herdr` is the whole command, every
|
|
695
|
-
reinstall is the refresh trigger, and `entwurf herdr-plugin-deactivate` is the explicit inverse
|
|
696
|
-
(Herdr's own uninstall removes only its checkout and calls no cleanup hook).
|
|
697
|
-
[`plugins/herdr/README.md`](./plugins/herdr/README.md) owns that lane, including which acquisition
|
|
698
|
-
source the committed lock names.
|
|
699
|
-
|
|
700
|
-
**Resuming an existing garden session.** Use `entwurf_resume_call {target}`. It reopens a DORMANT pi citizen under the SAME garden id in a visible window in the caller's own tmux session, resolving the transcript, model, provider and cwd from the record — so it takes only the target id, and it runs no turn: the window comes back with the conversation and waits, and talking to it is still `entwurf_v2`. Two receipts arrive and mean different things: a LAUNCH receipt (tmux made a window and was asked to start pi) and an OBSERVATION receipt (the control socket answered under the same id, or `resume-unobserved`). Unobserved is a real outcome, not an error to retry — the window is visible, so read it. A citizen that is already live is refused; so is a non-pi target (`target-not-pi`), because only pi stands a control socket up. The predecessor, `entwurf_v2 intent=owned-outcome`, resumed by launching a hidden window-less background child and was withdrawn under the visible-first rule; delivery still starts no process. Identity preconditions live in `resume-launch-identity.ts`, gated by `check-resume-launch-identity`.
|
|
163
|
+
## Concept primer
|
|
701
164
|
|
|
702
|
-
|
|
165
|
+
- **Entwurf** (기투, projection-of-self): opening and addressing independent visible
|
|
166
|
+
siblings, not turning them into workers of a second orchestrator.
|
|
167
|
+
- **Garden / garden id:** the shared address space and stable id of one V3
|
|
168
|
+
record-backed citizen. Native harnesses keep their own identity and transcript.
|
|
169
|
+
- **ACP:** a plugin that exposes backend turns as pi provider/models under the
|
|
170
|
+
operator's own auth. The host pi is already a citizen; ACP mints no second one.
|
|
171
|
+
- **MCP:** tool-call ingress to the same compiled `entwurf-bridge`, not a general
|
|
172
|
+
MCP platform or the pi receive rail. ACP uses explicit `mcpServers` declarations;
|
|
173
|
+
no ambient config scan or automatic retrieval. Plain anonymous hosts have no
|
|
174
|
+
authoritative reply address and sends are refused by default.
|
|
175
|
+
- **Engraving:** optional short operator text in the backend's identity carrier,
|
|
176
|
+
not rich project context or a hidden tool catalog.
|
|
177
|
+
|
|
178
|
+
## Pi 1.0 and a short history
|
|
179
|
+
|
|
180
|
+
**We welcome Pi's built-in MCP:** [You Said No MCP!](https://earendil.com/posts/you-said-no-mcp/)
|
|
181
|
+
(Earendil, 2026-09-29). The 0.30.0 cut targets Pi 1.0 admission and ACP basics.
|
|
182
|
+
Instead of keeping native pi copies of the verbs, the adapter registers the same
|
|
183
|
+
compiled bridge after record birth, under the citizen's identity, and Pi supervises
|
|
184
|
+
that MCP child. **MCP is ingress; pi receive remains the record-addressed Unix
|
|
185
|
+
control socket.** Garden identity and dispatch stay Entwurf's. What `pi-durable`
|
|
186
|
+
or Pi codemode should mean here remains undecided: this cut neither adopts nor
|
|
187
|
+
certifies them. Herdr 0.9.3 is a later lane, not a companion upgrade.
|
|
188
|
+
|
|
189
|
+
Entwurf is the 0.12+ successor to
|
|
190
|
+
[`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp):
|
|
191
|
+
the work was renamed around the dispatch substrate rather than the pi adapter.
|
|
192
|
+
The pre-0.12 [two-pane demo](./demo/README.md) is **archived evidence**, including
|
|
193
|
+
retired resume behaviour, not current instructions. [CHANGELOG](./CHANGELOG.md)
|
|
194
|
+
keeps the release history; dated Codex topology and qualification receipts remain
|
|
195
|
+
in [DELIVERY](./DELIVERY.md) and [BASELINE](./BASELINE.md), not rewritten as today's acceptance.
|
|
196
|
+
|
|
197
|
+
## Detailed operation
|
|
198
|
+
|
|
199
|
+
### Settings
|
|
200
|
+
|
|
201
|
+
[Setup — settings and backend-specific keys](./docs/setup-clean-host.md#settings-reference-and-backend-specific-keys)
|
|
202
|
+
owns configuration, including billing-safe carrier choices and pi-versus-backend keys.
|
|
703
203
|
|
|
704
|
-
|
|
204
|
+
### External MCP registration
|
|
705
205
|
|
|
706
|
-
|
|
206
|
+
[Setup — external hosts](./docs/setup-clean-host.md#stable-bins-and-external-mcp-hosts)
|
|
207
|
+
and [manual wiring](./docs/external-mcp-host.md) own paths, env and identity prerequisites.
|
|
707
208
|
|
|
708
|
-
|
|
209
|
+
### Custom skills
|
|
709
210
|
|
|
710
|
-
|
|
211
|
+
[Setup — custom skills](./docs/setup-clean-host.md#custom-skills) owns the Claude ACP
|
|
212
|
+
plugin layout and validation; other harnesses keep their native skill mechanisms.
|
|
711
213
|
|
|
712
|
-
|
|
214
|
+
### Context carriers
|
|
713
215
|
|
|
714
|
-
|
|
216
|
+
[Setup — carriers](./docs/setup-clean-host.md#context-carriers) separates short
|
|
217
|
+
engraving from the rich first-user augment and the actual callable schema.
|
|
715
218
|
|
|
716
|
-
|
|
219
|
+
### Compaction policy
|
|
717
220
|
|
|
718
|
-
|
|
221
|
+
[Setup — reuse and compaction](./docs/setup-clean-host.md#session-reuse-and-compaction):
|
|
222
|
+
Entwurf does not implement backend compaction or hydrate another harness's transcript.
|
|
719
223
|
|
|
720
224
|
## What this repo owns, and does not
|
|
721
225
|
|
|
722
|
-
Owns:
|
|
723
|
-
|
|
724
|
-
Does not:
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
This repo also doubles as the maintainer's working laboratory for agent-harness boundaries — new workflow patterns (e.g. Mitsein over MCP) land here first as low-level instruments, before crystallizing into invariants or graduating into more polished surfaces elsewhere.
|
|
226
|
+
Owns: garden identity, live-fact dispatch and receipts, native bridge integrations,
|
|
227
|
+
visible lifecycle launch seams, the pi adapter and isolated ACP plugin lifecycle.
|
|
228
|
+
Does not: a second harness, memory DB, prompt reconstruction, transcript hydration,
|
|
229
|
+
credential mediation, subscription bypass, pane-typing delivery or hidden orchestration.
|
|
230
|
+
[AGENTS](./AGENTS.md) owns these invariants and exact capability boundaries.
|
|
729
231
|
|
|
730
232
|
## Verification surfaces
|
|
731
233
|
|
|
732
|
-
-
|
|
733
|
-
-
|
|
734
|
-
-
|
|
234
|
+
- [VERIFY](./VERIFY.md): evidence strength, deterministic/package gates and LIVE release protocol.
|
|
235
|
+
- [BASELINE](./BASELINE.md): operator-driven observations and dated host receipts.
|
|
236
|
+
- [DELIVERY](./DELIVERY.md): per-harness async receive capabilities, boundaries and limits.
|
|
735
237
|
|
|
736
|
-
|
|
238
|
+
Installation, a doctor, a fixture and an exact release candidate prove different things;
|
|
239
|
+
none is interchangeable with the others or an all-host/long-haul guarantee.
|
|
737
240
|
|
|
738
241
|
## References
|
|
739
242
|
|
|
740
|
-
-
|
|
741
|
-
- Current
|
|
742
|
-
-
|
|
743
|
-
- [
|
|
744
|
-
- [
|
|
745
|
-
- [agent-
|
|
243
|
+
- [FAQ](./FAQ.md) · [Clean-host setup](./docs/setup-clean-host.md) · [Release record](./CHANGELOG.md)
|
|
244
|
+
- [Current ordering](https://github.com/junghan0611/entwurf/blob/main/NEXT.md)
|
|
245
|
+
- [Adding a harness](https://github.com/junghan0611/entwurf/blob/main/docs/adding-a-harness.md)
|
|
246
|
+
- [ACP backend contract](./docs/acp-backend-rail.md)
|
|
247
|
+
- [xenodium/agent-shell](https://github.com/xenodium/agent-shell) — Emacs ACP client, `resume > load > new` origin
|
|
248
|
+
- [claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp) — canonical Claude ACP server
|
|
249
|
+
- [agent-config](https://github.com/junghan0611/agent-config) — real consumer
|
|
746
250
|
|
|
747
251
|
## License
|
|
748
252
|
|