@junghanacs/entwurf 0.12.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 +240 -0
- package/BASELINE.md +227 -0
- package/CHANGELOG.md +1210 -0
- package/CONTRIBUTING.md +63 -0
- package/DELIVERY.md +209 -0
- package/LICENSE +21 -0
- package/README.md +504 -0
- package/VERIFY.md +260 -0
- package/demo/README.md +188 -0
- package/demo/demo-baseline.sh +156 -0
- package/demo/demo.sh +183 -0
- package/docs/assets/entwurf-demo.gif +0 -0
- package/docs/assets/entwurf-doomemacs.gif +0 -0
- package/docs/assets/entwurf-entwurf.gif +0 -0
- package/docs/assets/entwurf-hero.jpg +0 -0
- package/docs/setup-clean-host.md +305 -0
- package/mcp/entwurf-bridge/src/index.ts +513 -0
- package/mcp/entwurf-bridge/start.sh +25 -0
- package/mcp/entwurf-bridge/test.sh +54 -0
- package/mcp/tsconfig.json +29 -0
- package/package.json +130 -0
- package/pi/entwurf-capabilities.json +9 -0
- package/pi/entwurf-targets.json +20 -0
- package/pi/meta-bridge/.claude-plugin/marketplace.json +12 -0
- package/pi/meta-bridge/entwurf-meta-receive/.claude-plugin/plugin.json +5 -0
- package/pi/meta-bridge/entwurf-meta-receive/hooks/hooks.json +50 -0
- package/pi/meta-bridge/entwurf-meta-receive/scripts/doorbell.sh +54 -0
- package/pi/settings.reference.json +43 -0
- package/pi/skill-plugin-example/.claude-plugin/plugin.json +4 -0
- package/pi/skill-plugin-example/skills/hello/SKILL.md +18 -0
- package/pi-extensions/acp-provider.ts +63 -0
- package/pi-extensions/entwurf-control.ts +1692 -0
- package/pi-extensions/lib/acp/acp-client.ts +90 -0
- package/pi-extensions/lib/acp/augment.ts +238 -0
- package/pi-extensions/lib/acp/backend-adapter.ts +319 -0
- package/pi-extensions/lib/acp/backend.ts +873 -0
- package/pi-extensions/lib/acp/config.ts +552 -0
- package/pi-extensions/lib/acp/context.ts +177 -0
- package/pi-extensions/lib/acp/engraving.ts +123 -0
- package/pi-extensions/lib/acp/event-mapper.ts +339 -0
- package/pi-extensions/lib/acp/models.ts +102 -0
- package/pi-extensions/lib/acp/overlay.ts +220 -0
- package/pi-extensions/lib/acp/prompts/engraving.md +1 -0
- package/pi-extensions/lib/acp/session-store.ts +418 -0
- package/pi-extensions/lib/acp/tool-surface.ts +184 -0
- package/pi-extensions/lib/entwurf-control-rpc.ts +208 -0
- package/pi-extensions/lib/entwurf-core.ts +2033 -0
- package/pi-extensions/lib/entwurf-deliverability.ts +123 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +158 -0
- package/pi-extensions/lib/entwurf-facts.ts +251 -0
- package/pi-extensions/lib/entwurf-mailbox-guard.ts +100 -0
- package/pi-extensions/lib/entwurf-peers-render.ts +154 -0
- package/pi-extensions/lib/entwurf-preflight.ts +247 -0
- package/pi-extensions/lib/entwurf-resume-args.ts +88 -0
- package/pi-extensions/lib/entwurf-self-address.ts +117 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +427 -0
- package/pi-extensions/lib/entwurf-v2-decider.ts +441 -0
- package/pi-extensions/lib/entwurf-v2-lock.ts +412 -0
- package/pi-extensions/lib/entwurf-v2-mailbox.ts +87 -0
- package/pi-extensions/lib/entwurf-v2-production.ts +326 -0
- package/pi-extensions/lib/entwurf-v2-release.ts +149 -0
- package/pi-extensions/lib/entwurf-v2-resume-marker.ts +34 -0
- package/pi-extensions/lib/entwurf-v2-runner.ts +196 -0
- package/pi-extensions/lib/entwurf-v2-send-fallback.ts +166 -0
- package/pi-extensions/lib/entwurf-v2-send.ts +271 -0
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +337 -0
- package/pi-extensions/lib/entwurf-v2-spawn.ts +323 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +205 -0
- package/pi-extensions/lib/meta-mailbox-body.ts +80 -0
- package/pi-extensions/lib/meta-session.ts +1951 -0
- package/pi-extensions/lib/project-trust-handler.ts +154 -0
- package/pi-extensions/lib/session-id.js +57 -0
- package/pi-extensions/lib/socket-discovery.ts +346 -0
- package/pi-extensions/lib/socket-probe.ts +92 -0
- package/pi-extensions/meta-bridge-hook.ts +250 -0
- package/pi-extensions/model-lock.ts +235 -0
- package/prompts/engraving.md +27 -0
- package/protocol.js +31 -0
- package/run.sh +2832 -0
- package/scripts/check-acp-backend-preflight.ts +130 -0
- package/scripts/check-acp-carrier-augment.ts +297 -0
- package/scripts/check-acp-config.ts +322 -0
- package/scripts/check-acp-event-mapper.ts +253 -0
- package/scripts/check-acp-overlay.ts +154 -0
- package/scripts/check-acp-prompt-builder.ts +191 -0
- package/scripts/check-acp-provider-surface.ts +159 -0
- package/scripts/check-acp-sdk-surface.ts +211 -0
- package/scripts/check-acp-session-reuse.ts +756 -0
- package/scripts/check-acp-session-store.ts +387 -0
- package/scripts/check-acp-tool-surface.ts +159 -0
- package/scripts/check-entwurf-bridge-boot.ts +199 -0
- package/scripts/check-entwurf-capabilities.ts +123 -0
- package/scripts/check-entwurf-control-rpc.ts +187 -0
- package/scripts/check-entwurf-deliverability.ts +158 -0
- package/scripts/check-entwurf-fact-provider.ts +316 -0
- package/scripts/check-entwurf-facts.ts +358 -0
- package/scripts/check-entwurf-mailbox-guard.ts +264 -0
- package/scripts/check-entwurf-peers-surface.ts +235 -0
- package/scripts/check-entwurf-resume-args.ts +149 -0
- package/scripts/check-entwurf-self-address.ts +209 -0
- package/scripts/check-entwurf-session-identity.ts +703 -0
- package/scripts/check-entwurf-v2-contract.ts +536 -0
- package/scripts/check-entwurf-v2-decider.ts +795 -0
- package/scripts/check-entwurf-v2-lock.ts +368 -0
- package/scripts/check-entwurf-v2-mailbox.ts +228 -0
- package/scripts/check-entwurf-v2-matrix.ts +437 -0
- package/scripts/check-entwurf-v2-production.ts +406 -0
- package/scripts/check-entwurf-v2-release.ts +245 -0
- package/scripts/check-entwurf-v2-runner.ts +452 -0
- package/scripts/check-entwurf-v2-send-fallback.ts +371 -0
- package/scripts/check-entwurf-v2-send.ts +454 -0
- package/scripts/check-entwurf-v2-spawn-production.ts +452 -0
- package/scripts/check-entwurf-v2-spawn.ts +398 -0
- package/scripts/check-entwurf-v2-surface.ts +352 -0
- package/scripts/check-keyset-overlap.py +120 -0
- package/scripts/check-mailbox-receipt-state.ts +170 -0
- package/scripts/check-meta-capability-source.ts +112 -0
- package/scripts/check-meta-dual-consumers.ts +154 -0
- package/scripts/check-meta-dual-read.ts +158 -0
- package/scripts/check-meta-listing.ts +138 -0
- package/scripts/check-meta-mailbox-state-write.ts +135 -0
- package/scripts/check-meta-migration.ts +212 -0
- package/scripts/check-meta-receiver-marker.ts +185 -0
- package/scripts/check-meta-record-v2.ts +191 -0
- package/scripts/check-meta-session.ts +673 -0
- package/scripts/check-model-lock.ts +408 -0
- package/scripts/check-package-source-routing.ts +253 -0
- package/scripts/check-pi-preflight.ts +304 -0
- package/scripts/check-project-trust-handler.ts +265 -0
- package/scripts/check-shell-quote.ts +121 -0
- package/scripts/check-socket-discovery.ts +428 -0
- package/scripts/check-socket-probe.ts +106 -0
- package/scripts/fixtures/probe-mcp-server.ts +33 -0
- package/scripts/gnew-rpc-drive.ts +211 -0
- package/scripts/lib/acp-child-cleanup.ts +116 -0
- package/scripts/meta-bridge-doctor.sh +315 -0
- package/scripts/meta-bridge-hook-log.sh +26 -0
- package/scripts/meta-bridge-install.sh +135 -0
- package/scripts/meta-bridge-prune.ts +199 -0
- package/scripts/meta-bridge-state.py +549 -0
- package/scripts/meta-bridge-statusline.sh +192 -0
- package/scripts/meta-bridge-store-doctor.ts +64 -0
- package/scripts/meta-bridge-uninstall.sh +39 -0
- package/scripts/new-session-id.ts +25 -0
- package/scripts/postinstall-chmod.cjs +58 -0
- package/scripts/raw-async-delivery/README.md +258 -0
- package/scripts/raw-async-delivery/cc-enqueue-addressed.sh +35 -0
- package/scripts/raw-async-delivery/cc-mailbox-rewake.sh +38 -0
- package/scripts/raw-async-delivery/cc-watch-filechanged.sh +19 -0
- package/scripts/raw-async-delivery/cc-watch-sessionstart.sh +15 -0
- package/scripts/raw-async-delivery/codex-local-appserver.sh +39 -0
- package/scripts/raw-async-delivery/plugin-entwurf-receive/.claude-plugin/plugin.json +5 -0
- package/scripts/raw-async-delivery/plugin-entwurf-receive/hooks/hooks.json +28 -0
- package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-filechanged.sh +28 -0
- package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-sessionstart.sh +27 -0
- package/scripts/raw-async-delivery/raw-agy-send.sh +29 -0
- package/scripts/raw-async-delivery/raw-claude-enqueue.sh +30 -0
- package/scripts/raw-async-delivery/raw-codex-ws-turn-start.py +164 -0
- package/scripts/raw-async-delivery/repro-addressed-routing.sh +96 -0
- package/scripts/raw-async-delivery/repro-plugin-idle-wake.sh +104 -0
- package/scripts/resolve-acp-bridge.ts +25 -0
- package/scripts/smoke-acp-bundled-mcp-live.ts +292 -0
- package/scripts/smoke-acp-carrier-augment-live.ts +192 -0
- package/scripts/smoke-acp-mcp-live.ts +129 -0
- package/scripts/smoke-acp-memory-containment-live.ts +389 -0
- package/scripts/smoke-acp-overlay-live.ts +314 -0
- package/scripts/smoke-acp-provider-live.ts +162 -0
- package/scripts/smoke-acp-raw-turn-live.ts +261 -0
- package/scripts/smoke-acp-session-reuse-live.ts +172 -0
- package/scripts/smoke-acp-skill-live.ts +144 -0
- package/scripts/smoke-acp-socket-citizen-live.ts +168 -0
- package/scripts/smoke-claude-native-resume-live.sh +198 -0
- package/scripts/smoke-entwurf-v2-matrix-live.ts +398 -0
- package/scripts/smoke-entwurf-v2-spawn-live.ts +175 -0
- package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +428 -0
- package/scripts/smoke-meta-async-drift.sh +171 -0
- package/scripts/smoke-meta-honesty.sh +147 -0
- package/scripts/smoke-meta-install-state.sh +403 -0
- package/scripts/smoke-meta-keyset-guard.sh +111 -0
- package/scripts/smoke-meta-prune.sh +174 -0
- package/scripts/smoke-resident-garden-guard.sh +433 -0
- package/scripts/smoke-session-id-name.ts +187 -0
- package/scripts/tsconfig.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# entwurf
|
|
2
|
+
|
|
3
|
+
`entwurf` is a garden-citizen dispatch substrate: a thin bridge that lets already-existing agent harnesses address one another by **garden id** without pretending to own each other's transcript, auth, or runtime.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@junghanacs/entwurf) · maintained by [junghanacs.com](https://junghanacs.com/)
|
|
8
|
+
|
|
9
|
+
npm package: <https://www.npmjs.com/package/@junghanacs/entwurf>
|
|
10
|
+
|
|
11
|
+
> **Public, active development.** Real working code, still young. Verify it in your own workflow before relying on it all day. Evidence calibration: [VERIFY.md](./VERIFY.md); native async-delivery capability levels: [DELIVERY.md](./DELIVERY.md).
|
|
12
|
+
|
|
13
|
+
> **Current state for 0.12.0.** This repo is **entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + an ACP plugin**. Pi is one supported harness adapter — important because it supplies control sockets and hosts the ACP plugin today — but it is not the project subject. Claude Code is shipped through the meta-bridge; pi is shipped through the control-socket adapter; Codex and Antigravity (`agy`) reach the garden with verified delivery probes, documented per launch mode in [DELIVERY.md](./DELIVERY.md), with shipped adapter/install lanes landing after the 0.12.0 doc cut. The ACP plugin is Claude-first; Cortex/vendor-governed ACP backends are future lanes.
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary>Watch archived pre-0.12 demo (2131×1142 GIF, click to expand)</summary>
|
|
17
|
+
|
|
18
|
+
> This GIF is historical pre-0.12 evidence and still shows the retired v1 demo flow. The current 0.12 tool surface is `entwurf_v2`; a v2-native demo retake is a follow-up.
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
Claude Code / Codex / agy / pi
|
|
26
|
+
→ garden id
|
|
27
|
+
→ entwurf_v2
|
|
28
|
+
→ control-socket | spawn-bg resume | meta-mailbox
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
[`entwurf_v2`](#entwurf_v2--canonical-dispatch-verb) is the canonical dispatch surface over *existing* garden citizens — live control-socket send (including record-less socket-only pi sessions), spawn-bg resume, and meta-mailbox enqueue. The v1 entwurf verbs are gone. Fresh sibling minting and non-Claude ACP backends are deferred lanes.
|
|
32
|
+
|
|
33
|
+
**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.
|
|
34
|
+
|
|
35
|
+
**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.
|
|
36
|
+
|
|
37
|
+
**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.
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
pi --entwurf-control
|
|
41
|
+
→ entwurf ACP plugin
|
|
42
|
+
→ claude-agent-acp
|
|
43
|
+
→ Claude backend under the operator's local auth
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**The meta-bridge reaches 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.
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
native Claude Code
|
|
50
|
+
→ SessionStart hook
|
|
51
|
+
→ meta-session <garden-id>
|
|
52
|
+
→ entwurf-bridge MCP
|
|
53
|
+
→ entwurf_self | entwurf_v2 | entwurf_inbox_read
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
This meta-bridge installer/doctor is **Claude Code only** in the shipped 0.12.0 surface. Codex and Antigravity delivery probes are recorded in [DELIVERY.md](./DELIVERY.md) as verified adapter evidence, not yet shipped install surfaces.
|
|
57
|
+
|
|
58
|
+
> **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.
|
|
59
|
+
|
|
60
|
+
> **Project boundary.** `entwurf` is not a fork, plugin, dependency, or integration layer of `oh-my-pi`, and it is not developed in coordination with `oh-my-pi`. Issues in other Pi / ACP projects may be useful as general implementation references, but they are not `entwurf` integration issues unless this repository explicitly links them as such.
|
|
61
|
+
|
|
62
|
+
> **Anthropic subscription billing.** From 2026-06-15, Anthropic third-party agent paths (ACP, Agent SDK, `claude -p`, entwurf's Claude backend) consume a separate Agent SDK credit pool, distinct from Claude chat and the `claude` CLI used as an interactive terminal. `entwurf` respects that distinction — no bypass, no emulation — and preserves capability dignity across supported backends (see [AGENTS.md](./AGENTS.md) invariants #7, #9, #10). The recommended default runtime leans toward paths outside Anthropic's Agent SDK metering, with Claude invoked when its quality is worth the credit cost. The operator decides the mix.
|
|
63
|
+
|
|
64
|
+
> **Gemini CLI migration.** Google announced that Gemini CLI stops serving requests for Google AI Pro / Ultra and unpaid individual tiers on **2026-06-18**; those users should migrate to [Antigravity CLI](https://antigravity.google/product/antigravity-cli). See Google's migration note: [Transitioning Gemini CLI to Antigravity CLI](https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/). The repository still carries existing Gemini adapter code for compatibility, but this README no longer presents Gemini CLI as a recommended setup path during the migration window.
|
|
65
|
+
|
|
66
|
+
## Concept primer
|
|
67
|
+
|
|
68
|
+
A few words that look unusual for a coding tool.
|
|
69
|
+
|
|
70
|
+
- **Entwurf** (기투, projection-of-self) — sibling sessions with their own runtime boundary. Not "delegate," not "worker," not "sub-agent." Spawn, resume, and live peer messaging are first-class.
|
|
71
|
+
- **Engraving** — optional short operator text delivered through each backend's native identity carrier. Not a giant hidden prompt, not a tool catalog.
|
|
72
|
+
- **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, Codex, Antigravity, …) when the operator chooses. `entwurf_self` returns an authoritative pi-session or trusted meta-session identity envelope; `entwurf_v2` can deliver from plain external MCP hosts, but only pi-session and trusted meta-session senders are replyable.
|
|
73
|
+
- **Session persistence** — re-attaches pi to the same remote ACP session. Does not hydrate backend transcripts into pi history.
|
|
74
|
+
|
|
75
|
+
## Install
|
|
76
|
+
|
|
77
|
+
`entwurf` is a thin garden-citizen bridge with a **Claude-first ACP plugin**. Pi is the host adapter for that ACP plugin today; Codex runs as a direct pi-native provider by default, with a tested `ENTWURF_ACP_FOR_CODEX=1` opt-in that routes it through the ACP bridge too. The bridge does not provide Claude credentials, tokens, or subscription access, and does not bypass any backend auth. Whatever the operator's local `claude` / `codex` already trusts is what entwurf uses.
|
|
78
|
+
|
|
79
|
+
`pi` installs the bridge from an `npm:` source (the published `@junghanacs/entwurf` package) in **global** (default, writes to `~/.pi/agent/settings.json`) or **project** (`-l` flag, writes to `.pi/settings.json`) scope. A `git:` source and a local clone remain available for tracking `main` or hacking on the bridge.
|
|
80
|
+
|
|
81
|
+
After installing the package, run `run.sh install .` in your target project. The script writes the `entwurfProvider` block into `.pi/settings.json` with the correct absolute path for `entwurf-bridge/start.sh` — no hand-editing required. The exact location of `run.sh` depends on which install path was used (each section below shows it). For manual configuration, [`pi/settings.reference.json`](./pi/settings.reference.json) is the reference shape — see [Settings](#settings) below.
|
|
82
|
+
|
|
83
|
+
### From npm via pi — global
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pi install npm:@junghanacs/entwurf
|
|
87
|
+
cd /path/to/your-project
|
|
88
|
+
~/.pi/agent/npm/node_modules/@junghanacs/entwurf/run.sh install .
|
|
89
|
+
~/.pi/agent/npm/node_modules/@junghanacs/entwurf/run.sh check-bridge
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### From npm via pi — project (`-l` flag)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd /path/to/your-project
|
|
96
|
+
pi install -l npm:@junghanacs/entwurf
|
|
97
|
+
./.pi/npm/node_modules/@junghanacs/entwurf/run.sh install .
|
|
98
|
+
./.pi/npm/node_modules/@junghanacs/entwurf/run.sh check-bridge
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### From source via pi — global (alternative)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pi install git:github.com/junghan0611/entwurf
|
|
105
|
+
cd /path/to/your-project
|
|
106
|
+
~/.pi/agent/git/github.com/junghan0611/entwurf/run.sh install .
|
|
107
|
+
~/.pi/agent/git/github.com/junghan0611/entwurf/run.sh check-bridge
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### From source via pi — project (`-l` flag)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
cd /path/to/your-project
|
|
114
|
+
pi install -l git:github.com/junghan0611/entwurf
|
|
115
|
+
./.pi/git/github.com/junghan0611/entwurf/run.sh install .
|
|
116
|
+
./.pi/git/github.com/junghan0611/entwurf/run.sh check-bridge
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Local development clone
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
|
|
123
|
+
cd ~/repos/gh/entwurf
|
|
124
|
+
pnpm install
|
|
125
|
+
pi install ./
|
|
126
|
+
./run.sh install /path/to/your-project
|
|
127
|
+
./run.sh check-bridge
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> **First time on a clean Ubuntu / Debian / macOS host?** See the [clean-host walk-through](./docs/setup-clean-host.md) — `nvm` + `pnpm` + `pi` install, `pi install git:...`, `run.sh install .`, the missing-auth boundary surface, and an authenticated runtime smoke for Claude.
|
|
131
|
+
|
|
132
|
+
> **Post-install checks.** `run.sh check-bridge` proves the `entwurf-bridge` MCP surface loads (provider registration + protocol/negative-path), with no backend auth needed. To prove the **ACP backend actually answers** — the bridge spawns Claude through the provider path and a real turn comes back — run `LIVE=1 run.sh smoke-acp-provider-live` (it needs the operator's local Claude auth/credit). Package-source routing — so that a `provider=entwurf` Entwurf target from a `git:` / `npm:` install resolves and does not die with `Unknown provider "entwurf"` (#29) — is pinned deterministically by `run.sh check-package-source-routing`, which runs inside `pnpm check` and the release gate.
|
|
133
|
+
|
|
134
|
+
> **Extension set — do not filter.** `entwurf` ships three `pi.extensions` entries as a single set: the ACP provider extension (`pi-extensions/acp-provider.ts`) plus `pi-extensions/entwurf-control.ts` and `pi-extensions/model-lock.ts`. Filtering some out via pi's object-form package configuration can leave the model lock or entwurf-control surface in a broken state. Disable the entire package or none of it unless you know precisely which boundary you are turning off.
|
|
135
|
+
|
|
136
|
+
### Backend prerequisites
|
|
137
|
+
|
|
138
|
+
The ACP plugin is **Claude-first**. 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:
|
|
139
|
+
|
|
140
|
+
1. **`CLAUDE_AGENT_ACP_COMMAND` env override** — explicit override for an alternative binary or a wrapper command.
|
|
141
|
+
2. **`require.resolve(...)` against the bundled package dependency** (`@agentclientprotocol/claude-agent-acp`). This is the default path; no extra global install needed.
|
|
142
|
+
3. **`PATH:claude-agent-acp` fallback** — used when the package resolution fails (e.g. a hand-edited `node_modules`).
|
|
143
|
+
|
|
144
|
+
The curated model registry exposes Claude models only, so the ACP backend is Claude. Codex is *not* an ACP backend here — a native Codex session is already a first-class garden citizen via direct injection, so it needs no ACP plugin (see [AGENTS.md](./AGENTS.md)). Vendor / governed CLIs (e.g. Cortex) are a later ACP backend lane.
|
|
145
|
+
|
|
146
|
+
### Emacs frontends
|
|
147
|
+
|
|
148
|
+
Works from terminals and from Emacs frontends that launch [pi-coding-agent](https://github.com/dnouri/pi-coding-agent).
|
|
149
|
+
|
|
150
|
+
<details>
|
|
151
|
+
<summary>Watch entwurf in Doom Emacs (1104×627 GIF, click to expand)</summary>
|
|
152
|
+
|
|
153
|
+

|
|
154
|
+
|
|
155
|
+
</details>
|
|
156
|
+
|
|
157
|
+
For a dedicated agent socket, pass the socket name:
|
|
158
|
+
|
|
159
|
+
```elisp
|
|
160
|
+
(setq pi-coding-agent-extra-args
|
|
161
|
+
'("--entwurf-control" "--emacs-agent-socket" "pi"))
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The bridge exports the socket name to ACP children as `PI_EMACS_AGENT_SOCKET`, so skills call Emacs without hardcoding:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
emacsclient -s "${PI_EMACS_AGENT_SOCKET:-server}" --eval '(...)'
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Settings
|
|
171
|
+
|
|
172
|
+
Reference shape lives in [`pi/settings.reference.json`](./pi/settings.reference.json). Minimum:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"compaction": { "enabled": false },
|
|
177
|
+
"entwurfProvider": {
|
|
178
|
+
"appendSystemPrompt": false,
|
|
179
|
+
"settingSources": [],
|
|
180
|
+
"strictMcpConfig": true,
|
|
181
|
+
"showToolNotifications": true,
|
|
182
|
+
"tools": ["Read", "Bash", "Edit", "Write"],
|
|
183
|
+
"skillPlugins": [],
|
|
184
|
+
"permissionAllow": ["Read(*)", "Bash(*)", "Edit(*)", "Write(*)", "mcp__*"],
|
|
185
|
+
"mcpServers": {
|
|
186
|
+
"entwurf-bridge": {
|
|
187
|
+
"command": "/path/to/entwurf/mcp/entwurf-bridge/start.sh",
|
|
188
|
+
"args": []
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`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.
|
|
196
|
+
|
|
197
|
+
`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.
|
|
198
|
+
|
|
199
|
+
### Wiring `entwurf-bridge` into an external MCP host
|
|
200
|
+
|
|
201
|
+
`entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code, Codex CLI, Antigravity/`agy`, …). That host does **not** become a pi session and does **not** need to be ACP-backed. There are now two honest cases:
|
|
202
|
+
|
|
203
|
+
- **plain external MCP host**: no garden meta-record / sender marker. It can call tools, but its sender envelope is external/non-replyable.
|
|
204
|
+
- **garden-native meta-session**: the native `SessionStart` hook minted a garden id and wrote a trusted sender marker. It is not a pi control-socket session, but it **is replyable by garden id**.
|
|
205
|
+
|
|
206
|
+
**Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads the target's type (live pi vs. dormant pi vs. Claude Code meta-session, which a bare garden id does not reveal) and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, and drain your mailbox with `entwurf_inbox_read`. Fresh sibling creation from nothing is a deferred lane. (The old v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone — the bridge wires only the v2 dispatch surface.)
|
|
207
|
+
|
|
208
|
+
Observed: Claude Code, Codex CLI, and Antigravity CLI all reach garden citizens through this MCP bridge from a plain external host; sync result delivery is the baseline, and a garden-native meta-session is symmetric/replyable over the mailbox once sender identity is proven.
|
|
209
|
+
|
|
210
|
+
Prerequisites on the host running the external MCP client:
|
|
211
|
+
|
|
212
|
+
- `pi` on PATH (for the `owned-outcome` spawn-bg resume path).
|
|
213
|
+
- `~/.pi/agent/entwurf-targets.json` (target registry) when dispatching to a target that resolves to a spawn-bg resume.
|
|
214
|
+
- A live pi session launched with `--entwurf-control` populates `~/.pi/entwurf-control/<sessionId>.sock`; required for `entwurf_v2` control-socket dispatch and `entwurf_peers`.
|
|
215
|
+
|
|
216
|
+
> **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. If `pi` works in your terminal but an external-host `entwurf_v2` spawn-bg resume fails with `spawn pi ENOENT`, pass a full PATH in the MCP server `env`, set `ENTWURF_BRIDGE_ENV_FILE` to a small shell file that exports PATH, or point the host at a wrapper that can find `pi`. `start.sh` sources only the explicit `ENTWURF_BRIDGE_ENV_FILE`; it never reads personal dotfiles automatically.
|
|
217
|
+
|
|
218
|
+
Example env file:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# ~/.config/entwurf-bridge/env.sh
|
|
222
|
+
export PATH="$HOME/.local/share/pnpm:$HOME/.local/bin:$HOME/.nvm/versions/node/v24.15.0/bin:$PATH"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Then add it to the external MCP config:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"env": {
|
|
230
|
+
"ENTWURF_BRIDGE_ENV_FILE": "/home/operator/.config/entwurf-bridge/env.sh",
|
|
231
|
+
"ENTWURF_BRIDGE_EXTERNAL_AGENT_ID": "external-mcp/claude-code"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Emergency/manual workaround when the MCP server environment is wrong but an existing entwurf session must be resumed: run `pi --session /path/to/entwurf.jsonl ...` from an interactive shell whose PATH is known-good. Treat this as a debug escape hatch, not a replacement for fixing the MCP launch environment.
|
|
237
|
+
|
|
238
|
+
External/meta-session semantics:
|
|
239
|
+
|
|
240
|
+
- `entwurf_v2` from a plain external host delivers with `origin: "external-mcp"` / `replyable: false`; `wants_reply: true` is rejected (no reply address).
|
|
241
|
+
- `entwurf_v2` from a trusted meta-session delivers with `origin: "meta-session"` / `replyable: true`; `wants_reply: true` is allowed and the receiver can reply to the sender's garden id.
|
|
242
|
+
- `entwurf_v2` with `intent: "owned-outcome"` to a dormant pi target needs `pi` on PATH (it spawns a `pi --entwurf-control` resume child); async completion followUp requires a replyable pi control-socket caller.
|
|
243
|
+
- `entwurf_self` returns the same authoritative identity for pi sessions **and** trusted meta-sessions. A plain external host with no pi env and no trusted sender marker still fails because there is no reply address to report.
|
|
244
|
+
|
|
245
|
+
#### Claude Code
|
|
246
|
+
|
|
247
|
+
Claude Code supports both CLI registration and a separated global MCP config. The separated file is recommended for dotfile / `agent-config` workflows because `~/.claude.json` also carries OAuth-bearing state.
|
|
248
|
+
|
|
249
|
+
**Option A — CLI add:**
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
claude mcp add --scope user entwurf-bridge \
|
|
253
|
+
bash /absolute/path/to/entwurf/mcp/entwurf-bridge/start.sh
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
This writes the entry into `~/.claude.json`'s top-level `mcpServers`. Good for one-off setup; do not version-control the resulting `~/.claude.json`.
|
|
257
|
+
|
|
258
|
+
**Option B — separated `~/.mcp.json`:**
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"mcpServers": {
|
|
263
|
+
"entwurf-bridge": {
|
|
264
|
+
"type": "stdio",
|
|
265
|
+
"command": "bash",
|
|
266
|
+
"args": [
|
|
267
|
+
"/absolute/path/to/entwurf/mcp/entwurf-bridge/start.sh"
|
|
268
|
+
],
|
|
269
|
+
"env": {
|
|
270
|
+
"ENTWURF_BRIDGE_EXTERNAL_AGENT_ID": "external-mcp/claude-code"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Claude Code reads `~/.mcp.json` in addition to `~/.claude.json`'s top-level `mcpServers`. The `env` block identifies the calling host on the receiver render — omit it and `entwurf_v2` shows `external-mcp/unknown-host`. If Claude Code permissions are locked down, allow `mcp__*` or `mcp__entwurf-bridge__*` in `~/.claude/settings.json`.
|
|
278
|
+
|
|
279
|
+
#### Codex CLI
|
|
280
|
+
|
|
281
|
+
Add the server to `~/.codex/config.toml`:
|
|
282
|
+
|
|
283
|
+
```toml
|
|
284
|
+
[mcp_servers.entwurf-bridge]
|
|
285
|
+
command = "/absolute/path/to/entwurf/mcp/entwurf-bridge/start.sh"
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### Antigravity CLI (`agy`)
|
|
289
|
+
|
|
290
|
+
Documented global config path:
|
|
291
|
+
|
|
292
|
+
```text
|
|
293
|
+
~/.gemini/antigravity-cli/mcp_config.json
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Current runtime-compatible path also observed:
|
|
297
|
+
|
|
298
|
+
```text
|
|
299
|
+
~/.gemini/config/mcp_config.json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Use the same server entry in either file:
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"mcpServers": {
|
|
307
|
+
"entwurf-bridge": {
|
|
308
|
+
"command": "/absolute/path/to/entwurf/mcp/entwurf-bridge/start.sh"
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
#### External-host skills and commands
|
|
315
|
+
|
|
316
|
+
MCP registration gives the external harness the tools; the host still needs workflow guidance. Put the Mitsein-over-MCP (cross-harness collaboration) rules in that host's instruction file or, when supported, as a host-native skill. Do not assume pi slash commands are portable across external hosts — if a workflow must work across Claude Code, Codex CLI, Antigravity, and future hosts, make it a skill or MCP tool rather than a command shortcut.
|
|
317
|
+
|
|
318
|
+
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.
|
|
319
|
+
|
|
320
|
+
## Per-backend operating surface
|
|
321
|
+
|
|
322
|
+
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.
|
|
323
|
+
|
|
324
|
+
**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. The four-tool baseline (`Read`, `Bash`, `Edit`, `Write`) is enforced through `tools` + `permissionAllow`; `Skill` is added automatically when `skillPlugins` is non-empty. Operator context cap override: `ENTWURF_ACP_CLAUDE_CONTEXT=<int>`.
|
|
325
|
+
|
|
326
|
+
(Codex is *not* an ACP backend here — it reaches the garden natively. Vendor / governed CLIs are a later ACP backend lane.)
|
|
327
|
+
|
|
328
|
+
Pi is the canonical memory authority (semantic-memory + Denote llmlog); Claude's native memory layer is pinned off.
|
|
329
|
+
|
|
330
|
+
## Smoke commands
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
pnpm check # full deterministic floor (all check-* gates, incl. check-acp-*)
|
|
334
|
+
./run.sh check-bridge # entwurf-bridge direct MCP smoke (no backend auth)
|
|
335
|
+
|
|
336
|
+
# ACP plugin LIVE acceptance — need the operator's local Claude auth/credit:
|
|
337
|
+
LIVE=1 ./run.sh smoke-acp-socket-citizen-live # turn-free socket citizenship (S1)
|
|
338
|
+
LIVE=1 ./run.sh smoke-acp-raw-turn-live # pinned ACP pipe + raw 1 turn (S2a)
|
|
339
|
+
LIVE=1 ./run.sh smoke-acp-overlay-live # config overlay + hooks:{} + tool meta (S2b)
|
|
340
|
+
LIVE=1 ./run.sh smoke-acp-provider-live # real pi provider path + progress/L3 (S2c/S2f)
|
|
341
|
+
LIVE=1 ./run.sh smoke-acp-session-reuse-live # process-scoped reuse + codeword recall (S2d)
|
|
342
|
+
LIVE=1 ./run.sh smoke-acp-carrier-augment-live # augment delivery + empty-carrier billing clean (S2e-1)
|
|
343
|
+
|
|
344
|
+
LIVE=1 ./run.sh release-gate /tmp/scratch # the single cut gate (MUST + BEHAVIOR, SKIP=0 for a real cut)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Custom skills
|
|
348
|
+
|
|
349
|
+
Claude sessions accept custom skills through `skillPlugins` — an array of absolute paths to directories matching the Claude Agent SDK plugin layout:
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
<your-plugin-root>/
|
|
353
|
+
├── .claude-plugin/
|
|
354
|
+
│ └── plugin.json
|
|
355
|
+
└── skills/
|
|
356
|
+
└── <skill-name>/
|
|
357
|
+
└── SKILL.md
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
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).
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
{
|
|
364
|
+
"entwurfProvider": {
|
|
365
|
+
"skillPlugins": ["/absolute/path/to/your-plugin-root"]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`Skill` is auto-added to `tools` and `Skill(*)` to `permissionAllow` whenever `skillPlugins` is non-empty. Each entry is validated at settings parse time and throws when the path is missing, not absolute, not a directory, or missing `.claude-plugin/plugin.json`. The Claude session does not start until the violation is fixed. The bridge does not validate `plugin.json` contents or `SKILL.md` bodies — that is the Claude Agent SDK's contract.
|
|
371
|
+
|
|
372
|
+
To verify, start a fresh Claude session and ask the model to list its skills; the names declared in your `SKILL.md` frontmatter should appear among the visible skills. The operator-driven version of this check is `Q-SKILL-CALLABLE` in [VERIFY.md](./VERIFY.md).
|
|
373
|
+
|
|
374
|
+
`skillPlugins` is a Claude-backend-only install surface. Codex exposes skills through native `~/.codex/skills/` passthrough.
|
|
375
|
+
|
|
376
|
+
For a real consumer arranging many skills, see [agent-config](https://github.com/junghan0611/agent-config).
|
|
377
|
+
|
|
378
|
+
## Entwurf orchestration
|
|
379
|
+
|
|
380
|
+
**Entwurf is a pi capability with two surfaces.** Native pi exposes it directly as an extension tool; ACP-backed sessions reach the same capability through entwurf's MCP/Unix-socket bridge. The purpose is not to invent a different sub-agent system, but to preserve the same sibling-based model across backends.
|
|
381
|
+
|
|
382
|
+
A sibling has its own runtime boundary and its own provider/model identity — not a worker, delegate, or sub-agent. Minting a brand-new sibling from nothing is a deferred v2 lane (`spawn-fresh`); today every transport targets an *existing* garden citizen. `entwurf_v2` dispatches over those: it reads the target's liveness as a fact and routes a `fire-and-forget` send (live pi), an `owned-outcome` spawn-bg resume (dormant pi, a real `pi --entwurf-control` child), or a meta-mailbox enqueue (active self-fetch receiver) — under a per-target lock, reporting one honest outcome.
|
|
383
|
+
|
|
384
|
+
A two-pane recording covers the surface end-to-end — sibling resume, cross-process MCP dispatch across a different cwd, and a live peer greeting:
|
|
385
|
+
|
|
386
|
+
<details>
|
|
387
|
+
<summary>Watch (2131×1142 GIF, click to expand)</summary>
|
|
388
|
+
|
|
389
|
+

|
|
390
|
+
|
|
391
|
+
</details>
|
|
392
|
+
|
|
393
|
+
Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable; garden-native meta-sessions carry a trusted `meta-session` envelope and are replyable by garden id. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling — and is rejected only from non-replyable external senders.
|
|
394
|
+
|
|
395
|
+
In ACP-backed sessions, agent tools (`entwurf_v2`, `entwurf_peers`, `entwurf_self`, `entwurf_inbox_read`) auto-attach through `entwurf-bridge`; in native pi sessions the same capability is available directly through the extension surface (`entwurf_v2`, `entwurf_peers` tools + `/entwurf-sessions`, `/gnew` commands). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / dormant-resume / Claude-Code-meta-mailbox. Fresh sibling creation from nothing is a deferred lane. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone — replaced by the v2 dispatch surface.) Garden-native operator commands require `--entwurf-control`. The spawn target allowlist is [`pi/entwurf-targets.json`](./pi/entwurf-targets.json).
|
|
396
|
+
|
|
397
|
+
### `entwurf_v2` — canonical dispatch verb
|
|
398
|
+
|
|
399
|
+
`entwurf_v2` / `runEntwurfV2` is the canonical v2 dispatch verb over **existing** garden targets — record-backed citizens plus live socket-only `pi` endpoints (a record-less but live `pi --entwurf-control` peer is a *target*, intentionally **not** an owned citizen). You give a target garden id plus an intent (`fire-and-forget` or `owned-outcome`); one decider reads the target's liveness as a fact (via the `entwurf_peers` fact surface) and picks the transport from a frozen table keyed on **both** the target's state **and** the intent — never on state alone — then reports one outcome under the v2 lock policy (the pi control-socket and spawn-bg paths take a per-target lock; the meta-mailbox path is lock-free but guarded by active-receiver deliverability):
|
|
400
|
+
|
|
401
|
+
| target state | intent | transport |
|
|
402
|
+
|---|---|---|
|
|
403
|
+
| live pi | fire-and-forget | control-socket send |
|
|
404
|
+
| live pi | owned-outcome | **reject** (a live peer is not an owned spawn target) |
|
|
405
|
+
| dormant pi | owned-outcome | spawn-bg resume (a real `pi --entwurf-control` child) |
|
|
406
|
+
| dormant pi | fire-and-forget | **reject** (`dormant-fire-forget-unsupported`) |
|
|
407
|
+
| active self-fetch receiver | fire-and-forget | meta-mailbox enqueue + doorbell |
|
|
408
|
+
| inactive / terminated self-fetch receiver | fire-and-forget | **reject** (`mailbox-undeliverable` — no `.msg`, no doorbell) |
|
|
409
|
+
| self-fetch | owned-outcome | **reject** (no owned result over a mailbox) |
|
|
410
|
+
|
|
411
|
+
**`entwurf_v2` is the canonical surface for garden-id delivery.** When you have a garden id and want to reach whoever it names — message, reply, or hand-off — `entwurf_v2` is the one surface that reads whether the target is a live pi session, a dormant pi session, or a Claude Code meta-session (they look alike) and routes correctly; *when unsure which transport, use `entwurf_v2`*. This is exactly what prevents an agent from poking a live-socket transport at a Claude Code meta-session that needs the mailbox.
|
|
412
|
+
|
|
413
|
+
What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, under a per-target lock, with an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. What it does **not** do is **fresh sibling creation** — minting a brand-new sibling from a provider/model/prompt is a deferred lane (the `dormant pi → spawn-bg resume` row above resumes an *already-identified* citizen, it does not mint a new one). The meta-mailbox row requires an **active** self-fetch receiver; Claude↔Claude / Claude tmux-live transport is a later lane (the contract enum names `tmux-live` but no production path executes it).
|
|
414
|
+
|
|
415
|
+
A live pi target is addressed by its **control socket**, so a record-less but live `pi --entwurf-control` session (an operator-greeted peer with no meta-record) is accepted as a `fire-and-forget` control-send target, matching what `entwurf_peers` lists as alive. An `owned-outcome` resume, however, needs a record-backed citizen (its cwd/launch authority); a record-less endpoint is a socket-only fire-and-forget target only — record-less dormant resume is a later lane.
|
|
416
|
+
|
|
417
|
+
> **Direction.** An Entwurf core (peer identity / garden id / inbox / liveness / dispatch / replyability / evidence) could later extract into its own repo with per-backend plugins; today this repo holds the v2 core + meta-bridge + ACP plugin together. ACP is one plugin, not the boundary — rationale: [#38](https://github.com/junghan0611/entwurf/issues/38).
|
|
418
|
+
|
|
419
|
+
### Garden launcher
|
|
420
|
+
|
|
421
|
+
A `--entwurf-control` session must be garden-native — its header `id` must be a garden sessionId (`YYYYMMDDTHHMMSS-[0-9a-f]{6}`), not pi's default `uuidv7`. The session id is fixed at launch (pi assigns it before extensions load), so the launcher injects it; `entwurf-control` only enforces. Launch through:
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
pi --session-id "$(/path/to/entwurf/run.sh new-session-id)" \
|
|
425
|
+
--entwurf-control --emacs-agent-socket server
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
`run.sh new-session-id` prints one fresh garden sessionId from the `generateSessionId` SSOT (do not reimplement the format in the shell — it would drift from the validator the guard enforces). An operator alias bakes this in, e.g.:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
pia() { pi --session-id "$(/path/to/entwurf/run.sh new-session-id)" \
|
|
432
|
+
--entwurf-control --emacs-agent-socket server "$@"; }
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**Resuming an existing garden session.** `--session-id` is idempotent — pi documents it as *"exact id, creating it if missing"*, so passing an **existing** garden id resumes (appends to) that session, guard and all. Resume by reusing the id, NOT pi's `--session` / `--resume` pickers: those are a separate, mutually-exclusive flag (`--session-id cannot be combined with --session`) and bypass the garden-id discipline. Same flag for new and resume; only the id source differs (a fresh `new-session-id` vs an existing id):
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
# resume an existing garden session under --entwurf-control
|
|
439
|
+
piar() {
|
|
440
|
+
local sid="$1"; shift
|
|
441
|
+
[ -n "$sid" ] || { echo "usage: piar <garden-session-id> [pi args]" >&2; return 1; }
|
|
442
|
+
pi --session-id "$sid" --entwurf-control --emacs-agent-socket server "$@"
|
|
443
|
+
}
|
|
444
|
+
piar 20260603T191245-a3f09c
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
The resumed session keeps its garden header id (so the guard passes) and carries over the recorded model/identity. In-process `/new`, `/fork`, `/clone` are **blocked** under `--entwurf-control` (they would mint a non-garden uuid — pi's pre-switch hook can only `cancel`, it cannot inject an id).
|
|
448
|
+
|
|
449
|
+
**Starting a new garden session in-process — `/gnew`.** Instead of the blocked `/new`, type `/gnew` (alias `/garden-new`) to birth a fresh garden-native session in the SAME terminal, at zero tokens. It pre-creates an empty garden session file and `switchSession()`es into it, so the new session is born on a garden id from the first bind — header, control socket, and `PI_SESSION_ID` all garden, no torn uuid (the trap `/new`'s `ctx.newSession()` falls into, where the uuid is minted before the id could be re-stamped). The new session immediately carries the `control` resident name and a fresh control socket; the old session's socket is dropped. If you `/gnew` and quit before sending a turn, the empty session remains visible in resume lists with message count 0; that is intentional, because the switch succeeded and the file is now a legitimate resident session. Gate: `run.sh smoke-resident-garden-guard` GNEW section (0-token RPC E2E + a backend-identity `entwurf_self` turn).
|
|
450
|
+
|
|
451
|
+
Enforcement (no uuid / back-compat path): a `--entwurf-control` session whose id is not garden-native is refused at `session_start` and the process **hard-exits before any model turn** (a `uuidv7` from a raw `pi --entwurf-control` blows up immediately — nonzero exit, no socket, no tokens). The status bar reads `🪛 ready` until the first assistant turn writes the session file (model still changeable), then `🪛 <gardenId>` (model locked). The resident session name is set lazily on that first turn, tagged `control` (never `entwurf`, so it is not resumable as an Entwurf child). Gates: `run.sh check-entwurf-session-identity` (deterministic) + `run.sh smoke-resident-garden-guard` (live).
|
|
452
|
+
|
|
453
|
+
The human-greeted 담당자 pattern is first-class: the operator opens a entwurf session in repo B, greets it directly, then passes that `sessionId` to another session, which reaches it via `entwurf_v2`. Resumed citizens and human-opened peers share the same messaging semantics; only how they came to exist differs.
|
|
454
|
+
|
|
455
|
+
**Mitsein over MCP** (공존) — the cross-harness counterpart. Pi may collaborate with an external interactive coding session (Claude Code, Codex, Antigravity used as a human terminal) without spawning it. A plain external host is one-directional in shape: outbound `pi → external` rides whatever the operator already uses (tmux send-keys, manual paste, any interactive input path), while inbound `external → pi` returns through this bridge's `entwurf_v2`. A garden-native meta-session closes that gap — both sides are addressable by garden id through the mailbox, and `wants_reply` is allowed when the sender marker proves the native session identity, so send/inbox is symmetric. The one remaining asymmetry is the followUp channel: async owned-outcome delivery still needs a pi control socket, which a meta-session does not have. This is still not a second harness — no control daemon and no transcript scraping are introduced; the bridge only fronts the mailbox/dispatch surface.
|
|
456
|
+
|
|
457
|
+
After a session is anchored, entwurf locks its model identity: switches that touch `entwurf` are reverted; native-to-native and pre-turn selection remain free. `ensureBridgeSession` refuses direct reuse-path mismatches before backend handoff.
|
|
458
|
+
|
|
459
|
+
Archived pre-0.12 demo notes: [`demo/README.md`](./demo/README.md). They remain as historical evidence until the v2-native demo retake.
|
|
460
|
+
|
|
461
|
+
## Context carriers
|
|
462
|
+
|
|
463
|
+
System / developer carriers and rich pi context are separate.
|
|
464
|
+
|
|
465
|
+
The carrier holds an optional short operator engraving; empty or missing is fine. The runtime default is the bundled `pi-extensions/lib/acp/prompts/engraving.md` (the `# Engraving Here` placeholder, pinned non-empty by a gate); [`prompts/engraving.md`](./prompts/engraving.md) is a documented sample you copy and point the runtime at with `ENTWURF_ACP_ENGRAVING_PATH=/path/to/alt.md`. Template variables: `{{backend}}`, `{{mcp_servers}}`. Do not put AGENTS.md, bridge narrative, or tool catalogs here — large Claude carriers can route OAuth sessions to metered "extra usage" billing.
|
|
466
|
+
|
|
467
|
+
Bridge identity, pi context, `~/AGENTS.md`, `cwd/AGENTS.md`, and date/cwd ride a one-shot first-user prepend (`pi-context-augment.ts`). Entwurf prompts already carry `cwd/AGENTS.md` inside `<project-context ...>`; the augment removes that duplicate. The augment describes capabilities, but the **actual callable schema remains source of truth** — `read` vs `Read` vs `exec_command`, MCP only when schema-visible.
|
|
468
|
+
|
|
469
|
+
## Compaction policy
|
|
470
|
+
|
|
471
|
+
**entwurf does not implement compaction.** When a backend compacts natively, the pi session and mapping survive that. The bridge exposes no backend-specific compaction knobs; operators who need to alter a backend's auto-compaction configure that backend through its own native interface. Do not rely on a pi-side JSONL summary to reduce a backend transcript — it does not.
|
|
472
|
+
|
|
473
|
+
The footer uses ACP `usage_update.used / size` (backend prompt/tools/cache/session included) with `[entwurf:usage]` diagnostics. Near limit, choose a visible action: clear, open a new session with a different model, or let the backend compact on its own.
|
|
474
|
+
|
|
475
|
+
## What this repo owns, and does not
|
|
476
|
+
|
|
477
|
+
Owns: provider registration (`entwurf/...`), ACP subprocess lifecycle + `resume > load > new`, prompt forwarding + ACP event mapping, the bridge surface that exposes pi capabilities such as entwurf to ACP-backed sessions, pi-facing MCP injection via `entwurfProvider.mcpServers`, and bridge-local cleanup and diagnostics.
|
|
478
|
+
|
|
479
|
+
Does not: reconstruct full history, hydrate backend transcripts into pi history, emulate Claude Code or Codex, run broad multi-agent orchestration (entwurf is narrow, registry-gated, identity-locked), or run a second session model competing with pi.
|
|
480
|
+
|
|
481
|
+
Only `pi:<sessionId>` mappings are persisted (`~/.pi/agent/cache/entwurf/sessions/`) — enough to re-attach pi to the same remote ACP session, never enough to act as a second harness. Backend stores (`~/.claude/`, `~/.codex/`) are interoperability side effects, not authority.
|
|
482
|
+
|
|
483
|
+
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.
|
|
484
|
+
|
|
485
|
+
## Verification surfaces
|
|
486
|
+
|
|
487
|
+
- **[VERIFY.md](./VERIFY.md)** — agent-driven. One ACP-bridged identity runs the script against another and records what it sees. Carries the Evidence Levels L0–L5 rung ladder and the Claims Ledger so each claim is parked at the rung it has actually reached.
|
|
488
|
+
- **[BASELINE.md](./BASELINE.md)** — operator-driven. The maintainer runs the interview directly (no agent in the verifier seat) and the result is recorded.
|
|
489
|
+
- **[DELIVERY.md](./DELIVERY.md)** — capability-coordinate. The cross-harness yardstick for one question: can an already-running native session receive an async message without pretending pi owns the backend transcript? Records the per-backend async-delivery level (`D0–D8`) each harness actually reaches instead of collapsing into works/doesn't.
|
|
490
|
+
|
|
491
|
+
VERIFY + BASELINE are the verification pair — use both; either one alone leaves a blind spot the other closes. DELIVERY sits on the orthogonal delivery-capability axis.
|
|
492
|
+
|
|
493
|
+
## References
|
|
494
|
+
|
|
495
|
+
- File map + code-level invariants: [AGENTS.md](./AGENTS.md)
|
|
496
|
+
- Current priority + open decisions: [NEXT.md](https://github.com/junghan0611/entwurf/blob/main/NEXT.md)
|
|
497
|
+
- Release record: [CHANGELOG.md](./CHANGELOG.md)
|
|
498
|
+
- [xenodium/agent-shell](https://github.com/xenodium/agent-shell) — Emacs ACP client, `resume > load > new` idea origin
|
|
499
|
+
- [agentclientprotocol/claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp) — canonical ACP server for Claude Code
|
|
500
|
+
- [agent-config](https://github.com/junghan0611/agent-config) — real consumer repo
|
|
501
|
+
|
|
502
|
+
## License
|
|
503
|
+
|
|
504
|
+
MIT
|