@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.
Files changed (183) hide show
  1. package/AGENTS.md +240 -0
  2. package/BASELINE.md +227 -0
  3. package/CHANGELOG.md +1210 -0
  4. package/CONTRIBUTING.md +63 -0
  5. package/DELIVERY.md +209 -0
  6. package/LICENSE +21 -0
  7. package/README.md +504 -0
  8. package/VERIFY.md +260 -0
  9. package/demo/README.md +188 -0
  10. package/demo/demo-baseline.sh +156 -0
  11. package/demo/demo.sh +183 -0
  12. package/docs/assets/entwurf-demo.gif +0 -0
  13. package/docs/assets/entwurf-doomemacs.gif +0 -0
  14. package/docs/assets/entwurf-entwurf.gif +0 -0
  15. package/docs/assets/entwurf-hero.jpg +0 -0
  16. package/docs/setup-clean-host.md +305 -0
  17. package/mcp/entwurf-bridge/src/index.ts +513 -0
  18. package/mcp/entwurf-bridge/start.sh +25 -0
  19. package/mcp/entwurf-bridge/test.sh +54 -0
  20. package/mcp/tsconfig.json +29 -0
  21. package/package.json +130 -0
  22. package/pi/entwurf-capabilities.json +9 -0
  23. package/pi/entwurf-targets.json +20 -0
  24. package/pi/meta-bridge/.claude-plugin/marketplace.json +12 -0
  25. package/pi/meta-bridge/entwurf-meta-receive/.claude-plugin/plugin.json +5 -0
  26. package/pi/meta-bridge/entwurf-meta-receive/hooks/hooks.json +50 -0
  27. package/pi/meta-bridge/entwurf-meta-receive/scripts/doorbell.sh +54 -0
  28. package/pi/settings.reference.json +43 -0
  29. package/pi/skill-plugin-example/.claude-plugin/plugin.json +4 -0
  30. package/pi/skill-plugin-example/skills/hello/SKILL.md +18 -0
  31. package/pi-extensions/acp-provider.ts +63 -0
  32. package/pi-extensions/entwurf-control.ts +1692 -0
  33. package/pi-extensions/lib/acp/acp-client.ts +90 -0
  34. package/pi-extensions/lib/acp/augment.ts +238 -0
  35. package/pi-extensions/lib/acp/backend-adapter.ts +319 -0
  36. package/pi-extensions/lib/acp/backend.ts +873 -0
  37. package/pi-extensions/lib/acp/config.ts +552 -0
  38. package/pi-extensions/lib/acp/context.ts +177 -0
  39. package/pi-extensions/lib/acp/engraving.ts +123 -0
  40. package/pi-extensions/lib/acp/event-mapper.ts +339 -0
  41. package/pi-extensions/lib/acp/models.ts +102 -0
  42. package/pi-extensions/lib/acp/overlay.ts +220 -0
  43. package/pi-extensions/lib/acp/prompts/engraving.md +1 -0
  44. package/pi-extensions/lib/acp/session-store.ts +418 -0
  45. package/pi-extensions/lib/acp/tool-surface.ts +184 -0
  46. package/pi-extensions/lib/entwurf-control-rpc.ts +208 -0
  47. package/pi-extensions/lib/entwurf-core.ts +2033 -0
  48. package/pi-extensions/lib/entwurf-deliverability.ts +123 -0
  49. package/pi-extensions/lib/entwurf-fact-provider.ts +158 -0
  50. package/pi-extensions/lib/entwurf-facts.ts +251 -0
  51. package/pi-extensions/lib/entwurf-mailbox-guard.ts +100 -0
  52. package/pi-extensions/lib/entwurf-peers-render.ts +154 -0
  53. package/pi-extensions/lib/entwurf-preflight.ts +247 -0
  54. package/pi-extensions/lib/entwurf-resume-args.ts +88 -0
  55. package/pi-extensions/lib/entwurf-self-address.ts +117 -0
  56. package/pi-extensions/lib/entwurf-v2-contract.ts +427 -0
  57. package/pi-extensions/lib/entwurf-v2-decider.ts +441 -0
  58. package/pi-extensions/lib/entwurf-v2-lock.ts +412 -0
  59. package/pi-extensions/lib/entwurf-v2-mailbox.ts +87 -0
  60. package/pi-extensions/lib/entwurf-v2-production.ts +326 -0
  61. package/pi-extensions/lib/entwurf-v2-release.ts +149 -0
  62. package/pi-extensions/lib/entwurf-v2-resume-marker.ts +34 -0
  63. package/pi-extensions/lib/entwurf-v2-runner.ts +196 -0
  64. package/pi-extensions/lib/entwurf-v2-send-fallback.ts +166 -0
  65. package/pi-extensions/lib/entwurf-v2-send.ts +271 -0
  66. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +337 -0
  67. package/pi-extensions/lib/entwurf-v2-spawn.ts +323 -0
  68. package/pi-extensions/lib/entwurf-v2-surface.ts +205 -0
  69. package/pi-extensions/lib/meta-mailbox-body.ts +80 -0
  70. package/pi-extensions/lib/meta-session.ts +1951 -0
  71. package/pi-extensions/lib/project-trust-handler.ts +154 -0
  72. package/pi-extensions/lib/session-id.js +57 -0
  73. package/pi-extensions/lib/socket-discovery.ts +346 -0
  74. package/pi-extensions/lib/socket-probe.ts +92 -0
  75. package/pi-extensions/meta-bridge-hook.ts +250 -0
  76. package/pi-extensions/model-lock.ts +235 -0
  77. package/prompts/engraving.md +27 -0
  78. package/protocol.js +31 -0
  79. package/run.sh +2832 -0
  80. package/scripts/check-acp-backend-preflight.ts +130 -0
  81. package/scripts/check-acp-carrier-augment.ts +297 -0
  82. package/scripts/check-acp-config.ts +322 -0
  83. package/scripts/check-acp-event-mapper.ts +253 -0
  84. package/scripts/check-acp-overlay.ts +154 -0
  85. package/scripts/check-acp-prompt-builder.ts +191 -0
  86. package/scripts/check-acp-provider-surface.ts +159 -0
  87. package/scripts/check-acp-sdk-surface.ts +211 -0
  88. package/scripts/check-acp-session-reuse.ts +756 -0
  89. package/scripts/check-acp-session-store.ts +387 -0
  90. package/scripts/check-acp-tool-surface.ts +159 -0
  91. package/scripts/check-entwurf-bridge-boot.ts +199 -0
  92. package/scripts/check-entwurf-capabilities.ts +123 -0
  93. package/scripts/check-entwurf-control-rpc.ts +187 -0
  94. package/scripts/check-entwurf-deliverability.ts +158 -0
  95. package/scripts/check-entwurf-fact-provider.ts +316 -0
  96. package/scripts/check-entwurf-facts.ts +358 -0
  97. package/scripts/check-entwurf-mailbox-guard.ts +264 -0
  98. package/scripts/check-entwurf-peers-surface.ts +235 -0
  99. package/scripts/check-entwurf-resume-args.ts +149 -0
  100. package/scripts/check-entwurf-self-address.ts +209 -0
  101. package/scripts/check-entwurf-session-identity.ts +703 -0
  102. package/scripts/check-entwurf-v2-contract.ts +536 -0
  103. package/scripts/check-entwurf-v2-decider.ts +795 -0
  104. package/scripts/check-entwurf-v2-lock.ts +368 -0
  105. package/scripts/check-entwurf-v2-mailbox.ts +228 -0
  106. package/scripts/check-entwurf-v2-matrix.ts +437 -0
  107. package/scripts/check-entwurf-v2-production.ts +406 -0
  108. package/scripts/check-entwurf-v2-release.ts +245 -0
  109. package/scripts/check-entwurf-v2-runner.ts +452 -0
  110. package/scripts/check-entwurf-v2-send-fallback.ts +371 -0
  111. package/scripts/check-entwurf-v2-send.ts +454 -0
  112. package/scripts/check-entwurf-v2-spawn-production.ts +452 -0
  113. package/scripts/check-entwurf-v2-spawn.ts +398 -0
  114. package/scripts/check-entwurf-v2-surface.ts +352 -0
  115. package/scripts/check-keyset-overlap.py +120 -0
  116. package/scripts/check-mailbox-receipt-state.ts +170 -0
  117. package/scripts/check-meta-capability-source.ts +112 -0
  118. package/scripts/check-meta-dual-consumers.ts +154 -0
  119. package/scripts/check-meta-dual-read.ts +158 -0
  120. package/scripts/check-meta-listing.ts +138 -0
  121. package/scripts/check-meta-mailbox-state-write.ts +135 -0
  122. package/scripts/check-meta-migration.ts +212 -0
  123. package/scripts/check-meta-receiver-marker.ts +185 -0
  124. package/scripts/check-meta-record-v2.ts +191 -0
  125. package/scripts/check-meta-session.ts +673 -0
  126. package/scripts/check-model-lock.ts +408 -0
  127. package/scripts/check-package-source-routing.ts +253 -0
  128. package/scripts/check-pi-preflight.ts +304 -0
  129. package/scripts/check-project-trust-handler.ts +265 -0
  130. package/scripts/check-shell-quote.ts +121 -0
  131. package/scripts/check-socket-discovery.ts +428 -0
  132. package/scripts/check-socket-probe.ts +106 -0
  133. package/scripts/fixtures/probe-mcp-server.ts +33 -0
  134. package/scripts/gnew-rpc-drive.ts +211 -0
  135. package/scripts/lib/acp-child-cleanup.ts +116 -0
  136. package/scripts/meta-bridge-doctor.sh +315 -0
  137. package/scripts/meta-bridge-hook-log.sh +26 -0
  138. package/scripts/meta-bridge-install.sh +135 -0
  139. package/scripts/meta-bridge-prune.ts +199 -0
  140. package/scripts/meta-bridge-state.py +549 -0
  141. package/scripts/meta-bridge-statusline.sh +192 -0
  142. package/scripts/meta-bridge-store-doctor.ts +64 -0
  143. package/scripts/meta-bridge-uninstall.sh +39 -0
  144. package/scripts/new-session-id.ts +25 -0
  145. package/scripts/postinstall-chmod.cjs +58 -0
  146. package/scripts/raw-async-delivery/README.md +258 -0
  147. package/scripts/raw-async-delivery/cc-enqueue-addressed.sh +35 -0
  148. package/scripts/raw-async-delivery/cc-mailbox-rewake.sh +38 -0
  149. package/scripts/raw-async-delivery/cc-watch-filechanged.sh +19 -0
  150. package/scripts/raw-async-delivery/cc-watch-sessionstart.sh +15 -0
  151. package/scripts/raw-async-delivery/codex-local-appserver.sh +39 -0
  152. package/scripts/raw-async-delivery/plugin-entwurf-receive/.claude-plugin/plugin.json +5 -0
  153. package/scripts/raw-async-delivery/plugin-entwurf-receive/hooks/hooks.json +28 -0
  154. package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-filechanged.sh +28 -0
  155. package/scripts/raw-async-delivery/plugin-entwurf-receive/scripts/watch-sessionstart.sh +27 -0
  156. package/scripts/raw-async-delivery/raw-agy-send.sh +29 -0
  157. package/scripts/raw-async-delivery/raw-claude-enqueue.sh +30 -0
  158. package/scripts/raw-async-delivery/raw-codex-ws-turn-start.py +164 -0
  159. package/scripts/raw-async-delivery/repro-addressed-routing.sh +96 -0
  160. package/scripts/raw-async-delivery/repro-plugin-idle-wake.sh +104 -0
  161. package/scripts/resolve-acp-bridge.ts +25 -0
  162. package/scripts/smoke-acp-bundled-mcp-live.ts +292 -0
  163. package/scripts/smoke-acp-carrier-augment-live.ts +192 -0
  164. package/scripts/smoke-acp-mcp-live.ts +129 -0
  165. package/scripts/smoke-acp-memory-containment-live.ts +389 -0
  166. package/scripts/smoke-acp-overlay-live.ts +314 -0
  167. package/scripts/smoke-acp-provider-live.ts +162 -0
  168. package/scripts/smoke-acp-raw-turn-live.ts +261 -0
  169. package/scripts/smoke-acp-session-reuse-live.ts +172 -0
  170. package/scripts/smoke-acp-skill-live.ts +144 -0
  171. package/scripts/smoke-acp-socket-citizen-live.ts +168 -0
  172. package/scripts/smoke-claude-native-resume-live.sh +198 -0
  173. package/scripts/smoke-entwurf-v2-matrix-live.ts +398 -0
  174. package/scripts/smoke-entwurf-v2-spawn-live.ts +175 -0
  175. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +428 -0
  176. package/scripts/smoke-meta-async-drift.sh +171 -0
  177. package/scripts/smoke-meta-honesty.sh +147 -0
  178. package/scripts/smoke-meta-install-state.sh +403 -0
  179. package/scripts/smoke-meta-keyset-guard.sh +111 -0
  180. package/scripts/smoke-meta-prune.sh +174 -0
  181. package/scripts/smoke-resident-garden-guard.sh +433 -0
  182. package/scripts/smoke-session-id-name.ts +187 -0
  183. package/scripts/tsconfig.json +34 -0
@@ -0,0 +1,63 @@
1
+ # Contributing to entwurf
2
+
3
+ This is a daily-driver bridge. Correctness beats feature breadth. Read this before opening a PR.
4
+
5
+ ## What this repo is
6
+
7
+ `entwurf` is a **garden-citizen dispatch bridge** — entwurf-core (v2 dispatch) + a meta-bridge + a pi adapter + a **Claude-first ACP plugin** — that lets already-running harnesses address one another by garden id; pi is one adapter, not the subject. The ACP plugin borrows the backend's identity (system prompt preset, model behavior, tool implementations) and shapes the *operating surface* — what tools, MCP, skills, and permissions are visible — to match pi's own policy. Claude is the shipped ACP backend, Codex is a native garden citizen, and the Gemini path is deprecated. That is the entire scope.
8
+
9
+ If a change moves the bridge toward "second harness" — prompt reconstruction, transcript hydration, ambient discovery, silent fallback — it does not belong here.
10
+
11
+ ## Hard invariants
12
+
13
+ These are enforced by code, gates, and review. Do not weaken them in a PR; if you want to argue against one, open an issue first.
14
+
15
+ 1. **Bootstrap order**: `resume > load > new`. Always.
16
+ 2. **Session persistence**: only `pi:<sessionId>` is persisted. `cwd:<cwd>` is never persisted.
17
+ 3. **MCP injection**: only via `entwurfProvider.mcpServers`. No ambient `~/.mcp.json` scanning, no `~/.claude/settings.json` MCP inheritance.
18
+ 4. **Operating surface, not config inheritance**: the user's filesystem Claude Code config (`~/.claude/settings.json` hooks, env, plugins, `permissions.defaultMode`) is intentionally *not* inherited. Skills come from `skillPlugins`, permissions from `permissionAllow`, deferred-tool surface from `disallowedTools`. The `CLAUDE_CONFIG_DIR` overlay enforces this even where the SDK reads filesystem independently of `settingSources`.
19
+ 5. **Backend-specific knobs stay explicit and namespaced**: Codex/Gemini-era ACP knobs are not part of the current Claude-first shipped path. If a future backend lane reintroduces a sandbox or mode knob, it must use the `ENTWURF_ACP_*` namespace and invalid values must throw, never fall back.
20
+ 6. **Bridge does not implement compaction**: When a backend compacts natively, the pi session and mapping survive that. Pi-side JSONL compaction must not be presented as backend-transcript reduction, and backend-specific compaction controls belong to the backend's own native interface. Legacy `PI_SHELL_ACP_*` compaction knobs must not reappear.
21
+ 7. **Backend coverage honesty**: changes to operating surface, session lifecycle, or persistence must state which shipped/probed backend surfaces they cover. A claim that silently drops a covered backend is a regression; if one backend is genuinely not covered, record that carve-out explicitly.
22
+ 8. **This bridge is not a second harness**: no prompt reconstruction, no transcript hydration, no tool result ledger, no Claude Code emulation.
23
+
24
+ ## Required gate before opening a PR
25
+
26
+ ```bash
27
+ pnpm check
28
+ ```
29
+
30
+ This wraps the entire static-quality surface (biome, tsc, all `check-*` gates including `check-dep-versions`). It is wired into the pre-commit hook (`.husky/pre-commit`), so a clean local commit is the first sign your change holds.
31
+
32
+ For changes that touch backend launch, session lifecycle, or `_meta` shape, also run:
33
+
34
+ ```bash
35
+ ./run.sh smoke-all /path/to/your-fixture-project
36
+ ./run.sh verify-resume /path/to/your-fixture-project
37
+ ```
38
+
39
+ These need a real ACP subprocess, so they stay manual — the hook does not run them.
40
+
41
+ ## What gets PRs rejected
42
+
43
+ - adds ambient MCP discovery (project `.mcp.json`, `~/.mcp.json`, etc.) without an explicit `entwurfProvider.mcpServers` opt-in path
44
+ - inherits user / project / local backend config by default (i.e. flips `settingSources` away from `[]`, drops the `CLAUDE_CONFIG_DIR` overlay, removes the codex `-c` config flags)
45
+ - weakens `resume > load > new` (e.g. silently downgrading to `new` without a logged invalidation reason)
46
+ - introduces `console.warn` / silent fallback where the bridge should `throw` (see `AGENTS.md` "Never warn. Throw.")
47
+ - changes the Claude, Codex, or Gemini operating surface (tools, skills, MCP, permissions, sandbox) without accounting for all three backends or recording an explicit carve-out
48
+ - adds a second transcript ledger, a prompt reconstruction layer, or any state that competes with pi's session as the source of truth
49
+ - skews version pins across `package.json`, `run.sh`, and `README.md` (the `check-dep-versions` gate catches this; if it complains, fix all three)
50
+
51
+ ## Style and code shape
52
+
53
+ - Read `AGENTS.md` for the full code-shape rules. Highlights:
54
+ - fail-fast: throw on bad config, never warn-and-continue
55
+ - no `try/catch` swallowing — `catch {}` is allowed only for environment probing
56
+ - send-is-throw — messages aren't awaited
57
+ - one surface name (`entwurf`)
58
+ - Comments explain *why*, not *what*. Reach for them at non-obvious decisions, especially around SDK / claude-agent-acp / codex-rs interaction edges that future maintainers won't know to look up.
59
+ - Keep changes single-responsibility per commit; bundling a refactor with a behavior change makes review and bisect painful.
60
+
61
+ ## When in doubt
62
+
63
+ Open an issue describing the backend boundary you want to touch and the failure mode you observed. The repo is small; over-coordination is cheap, regression on a daily-driver tool is expensive.
package/DELIVERY.md ADDED
@@ -0,0 +1,209 @@
1
+ # DELIVERY.md — Async delivery capability levels
2
+
3
+ `DELIVERY.md` is the cross-harness yardstick for one question:
4
+
5
+ > Can an already-running native agent session receive an async message, without
6
+ > pretending that pi owns the backend transcript?
7
+
8
+ It is **not** a product promise and not a benchmark. It is a diagnostic coordinate
9
+ system. When Claude Code, Antigravity, Codex, pi-native Entwurf, or a future
10
+ harness behaves differently, record the exact delivery level it reaches instead
11
+ of collapsing the result into "works" / "doesn't work".
12
+
13
+ Companion surfaces:
14
+
15
+ - [VERIFY.md](./VERIFY.md) — agent-driven bridge verification and evidence quality (`L0–L5`).
16
+ - [BASELINE.md](./BASELINE.md) — operator-driven identity / overlay baseline interviews.
17
+ - [`scripts/raw-async-delivery/`](./scripts/raw-async-delivery/) — reproducible raw delivery probes.
18
+
19
+ ## Scope and non-goals
20
+
21
+ This document is about **native live-session delivery** for the 0.12.0
22
+ meta-bridge direction: a garden meta-session points at a backend-owned native
23
+ session, and async messages reach that session through the backend's own
24
+ supported surfaces.
25
+
26
+ Non-goals:
27
+
28
+ - no tmux / pty `send-keys` as evidence for native delivery;
29
+ - no backend transcript hydration into pi JSONL;
30
+ - no direct writes into backend transcript databases / JSONL / protobuf files;
31
+ - no new prompt spawn (`claude -p`, fresh Codex thread, etc.) masquerading as
32
+ delivery into an already-running subscription/native session;
33
+ - no fake pi session or tool-result ledger for an external backend.
34
+
35
+ A backend may use a socket, filesystem watch, JSON-RPC app server, lifecycle
36
+ hook, or another official surface. The transport differs; the levels below keep
37
+ the judgement comparable.
38
+
39
+ ## State vocabulary
40
+
41
+ Use these words precisely in scripts and docs:
42
+
43
+ | State | Meaning |
44
+ |---|---|
45
+ | `queued` | Message is durably written to a mailbox / sender queue. The backend has not necessarily seen it. |
46
+ | `triggered` | A backend-supported event fired: socket RPC accepted, hook fired, file watch event observed, etc. |
47
+ | `woke` | An idle interactive session started a new turn without user typing / pty injection. |
48
+ | `injected` | The message or a doorbell pointing at it reached model-visible context through an official channel. |
49
+ | `processed` | The turn ended or the backend acknowledged completion through a supported hook/event. |
50
+ | `replied` | A result returned to the garden/pi side through an explicit reply path (MCP send, outbox, API result). |
51
+
52
+ Avoid bare `delivered` unless you define it. Preferred decomposition:
53
+ `queued → triggered → woke → injected → processed → replied`.
54
+
55
+ ## Delivery levels (D0–D8)
56
+
57
+ These are a separate namespace from VERIFY.md evidence levels (`L0–L5`) and
58
+ BASELINE.md overlay layers (`Q-L1` etc.). Mark the highest level reached and any
59
+ partial levels.
60
+
61
+ | Level | Name | PASS criterion | Typical failure / partial |
62
+ |---|---|---|---|
63
+ | **D0** | Live session identity | Can identify the target live session: native id, cwd/project, backend, and enough liveness metadata to address it. | Only transcript files exist; no live-session join key. |
64
+ | **D1** | Native/free continuation | Delivery targets an already-running native/subscription session; no fresh prompt spawn or metered worker is created for the message. | Uses `claude -p`, a fresh Codex thread, or a new pi child instead of the live session. |
65
+ | **D2** | Receiver armed | The receiving session registers an official receive surface: hook/watch path/socket/app-server subscription. | A mailbox exists but no live session is watching or reachable. |
66
+ | **D3** | Addressed enqueue | Sender can queue a message for exactly one target session id; siblings are not broadcast-woken. | Shared signal wakes every session; no per-session address. |
67
+ | **D4** | Idle active wake | An idle interactive session wakes from an external signal with no user typing and no pty/tmux injection. | Piggyback only: message waits until the next human/user turn. |
68
+ | **D5** | Context injection | A unique token / message body reaches model-visible context via an official hook/API path; the model can acknowledge it. | Hook logs show activity, but the model never sees the message. |
69
+ | **D6** | Same session/model continuity | The response comes from the same native session/conversation and same model/subscription path. | A new conversation/process handles the message; model changed silently. |
70
+ | **D7** | Completion / reply observation | Completion or reply can be observed without transcript scraping: Stop/SessionEnd/PostInvocation, outbox, MCP reply, API result, etc. | Wake and context work, but the garden side cannot know when the turn finished except by watching the UI. |
71
+ | **D8** | Operational robustness | Duplicate suppression, delivery markers, loop guards, level-triggered body drain, ordering policy, stale-session handling, and crash recovery are implemented/tested. | Demo works once but can loop, duplicate, reorder, leave unread backlogs, or lose messages. |
72
+
73
+ ### Script result contract
74
+
75
+ Raw probes should print a summary block that a human or later parser can compare
76
+ across harnesses:
77
+
78
+ ```text
79
+ DELIVERY_LEVELS:
80
+ harness=claude-code
81
+ transport=filechanged-watchpaths-asyncrewake
82
+ D0 live_session: pass
83
+ D1 native_free_continuation: pass
84
+ D2 receiver_armed: pass
85
+ D3 addressed_enqueue: pass
86
+ D4 idle_active_wake: pass
87
+ D5 context_injection: pass token=AGY-PARITY-3399
88
+ D6 continuity: pass session_id=<native-id> model=claude-opus-4-8
89
+ D7 completion_reply: partial reason="no garden outbox yet"
90
+ D8 robustness: partial reason="loop guard present; crash recovery not tested"
91
+ ```
92
+
93
+ When a level is **not applicable** or **conditional**, say so explicitly. For
94
+ example, Codex app-server delivery is conditional on a loaded thread and control
95
+ socket; direct Codex TUI is a different surface.
96
+
97
+ ## Current capability matrix (2026-06-24)
98
+
99
+ This matrix is a snapshot of what the raw probes have established. It should be
100
+ updated when a backend version changes the delivery surface.
101
+
102
+ The **Status** column is the 0.12.0 release framing, kept separate from the
103
+ `D0–D8` capability level:
104
+
105
+ - **shipped** — a supported entwurf 0.12.0 lane: wired, gated, and addressable through the bridge today.
106
+ - **verified-probe** — async delivery proven by a raw probe, but not yet a shipped/supported lane in 0.12.0 (documented, ships after this cut).
107
+ - **deferred** — not addressable as-is, or needs an extra managed install / cloud surface that is out of 0.12.0 scope.
108
+
109
+ | Harness / surface | Status | Highest current level | Transport | Notes |
110
+ |---|---|---:|---|---|
111
+ | **pi native Entwurf** | shipped | D7+ | Unix control socket + pi followUp/custom messages | Replyable pi session. This is the resident baseline, not an external meta-session. 0.12.0 `entwurf_v2` treats a record-less but live pi control socket as a socket-only `fire-and-forget` target (addressed by its socket, not a meta-record); record-less *dormant* resume is intentionally not claimed. |
112
+ | **Claude Code interactive 2.1.163** | shipped | D6, D7 partial, D8 partial | Plugin/global `SessionStart` arms `watchPaths`; external write triggers `FileChanged`; `asyncRewake` wakes idle session | Active idle wake proven without pty. `Stop` alone is piggyback-only. `asyncRewake` is a doorbell; body is self-fetched from mailbox. D8 partial: duplicate/read idempotence, honest unread counts, and level-triggered body drain are gated; empirical wake-edge bounds and unread-heartbeat backstop remain open (#34). |
113
+ | **Antigravity / agy** | verified-probe | D6+ | Native LS gRPC `agentapi send-message` | Active push into live conversation. Same judgement levels; transport differs from Claude. Delivery proven; a shipped adapter lane lands after the 0.12.0 doc cut. |
114
+ | **Codex app-server-backed TUI 0.136.0** | verified-probe | D6, D7 (status) | WebSocket-over-UDS `turn/start` into the live `threadId` | **Demonstrated, no managed standalone, no cloud.** `codex app-server --listen unix://<owned 0700 dir>` + plain `codex` auto-attach (or `--remote unix://`). Full message injection (agy-like, not a doorbell); `thread/status/changed` gives completion observation. D8 robustness (dedupe / crash recovery / ordering policy) is not tested. `turn/steer` is active-turn steering, not idle wake. |
115
+ | **Codex embedded TUI 0.136.0** | deferred | D0 partial | Native state DB / rollout transcript only | Standalone Embedded TUI binds no socket; no `FileChanged`/`asyncRewake` in Codex hooks; not retrofittable. Identify-only via state DB / rollout. |
116
+ | **Codex managed-daemon / remote-control 0.136.0** | deferred | D4–D6 conditional | `app-server proxy` newline JSON-RPC over the daemon control socket | Needs the managed standalone install; `remote-control` also enables the **cloud** bridge. Use the bare `--listen` path above for a purely-local setup. |
117
+ | **ACP Claude / Cortex (candidate)** | deferred | — | ACP (via entwurf's pi adapter) | Shipped as an ACP *runtime* lane (you can run Claude through ACP — see VERIFY/README), but **not a native-async-*delivery* target**: ACP sessions are bridge-spawned children, not already-running native sessions to wake, so they fall outside this doc's delivery question. `deferred` here means "no async-delivery lane," not "unsupported." Cortex is the candidate that would land on the ACP path next. |
118
+
119
+ ## Backend notes
120
+
121
+ ### Claude Code — filesystem event wake, not socket push
122
+
123
+ A missing local listening socket does **not** imply idle wake is impossible.
124
+ Claude Code interactive can be woken by a supported filesystem-event path:
125
+
126
+ 1. a plugin or settings hook runs at `SessionStart`;
127
+ 2. it emits `watchPaths` for a per-session signal file;
128
+ 3. an external sender writes a per-session message and pokes that signal;
129
+ 4. `FileChanged` fires while the session is idle;
130
+ 5. the hook exits with `asyncRewake` and writes the doorbell to **stderr**;
131
+ 6. the same session/model wakes and self-fetches the message body.
132
+
133
+ #### D8 partial — signal/body separation is level-triggered
134
+
135
+ Claude's `FileChanged` signal is an edge: rapid signal writes may coalesce, and a
136
+ true missed edge can leave an idle session with unread mail until another wake or
137
+ backstop occurs. The body is not carried in that edge. Bodies are durable mailbox
138
+ files (`*.msg` before the doorbell, `*.msg.delivered` after the doorbell), and
139
+ `entwurf_inbox_read` drains the whole unread set in one read and archives them as
140
+ `*.read`. Therefore a coalesced doorbell does not drop message bodies: once the
141
+ receiver self-fetches, it consumes all queued bodies, not "one event = one body".
142
+
143
+ Deterministic gates: `check-meta-session` asserts mixed fresh/delivered bodies are
144
+ drained together and re-read is empty; `smoke-meta-honesty` asserts the doorbell's
145
+ unread count matches what the inbox reader will drain. Remaining D8 work is still
146
+ honest/open in #34: empirical FileChanged coalescing bounds, active-turn arrival,
147
+ watchPath edge cases, compact-window re-arm gaps, and a heartbeat/re-poke backstop
148
+ for live sessions with unread mail.
149
+
150
+ Important gotchas live in [`scripts/raw-async-delivery/README.md`](./scripts/raw-async-delivery/README.md):
151
+ `Stop` hooks do not wake idle sessions, bare skills cannot arm startup watches,
152
+ plugins can, and imperatives in injected text can be flagged as prompt injection.
153
+
154
+ ### Antigravity / agy — native push
155
+
156
+ Antigravity reaches the same delivery levels through a different transport:
157
+ `agy agentapi send-message` over the native LS gRPC surface. This is not a reason
158
+ to make the garden layer backend-specific; it is exactly why the adapter contract
159
+ must describe capability (`D0–D8`) separately from transport.
160
+
161
+ ### Codex — split by launch mode, not by "Codex"
162
+
163
+ Do not describe "Codex" as one delivery shape. The split is the TUI's launch mode:
164
+
165
+ - **standalone Embedded TUI**: binds no socket, no `FileChanged`/`asyncRewake` in
166
+ Codex hooks, decision fixed at `run_main` → not addressable, not retrofittable;
167
+ - **app-server-backed TUI**: idle-wake **works**. Run a bare
168
+ `codex app-server --listen unix://$HOME/.codex/app-server-control/app-server-control.sock`
169
+ (no managed standalone, no cloud — only the official daemon path needs the
170
+ managed install). Plain `codex` (no `-c`) auto-attaches to that default socket;
171
+ an external WebSocket-over-UDS client sends `turn/start` to the live `threadId`.
172
+ Measured: idle thread woke with zero typing, body injected, model replied,
173
+ completion observed via `thread/status/changed`.
174
+
175
+ Sender: `raw-codex-ws-turn-start.py` → bare `--listen` socket (WebSocket, no managed
176
+ standalone, no cloud). A second surface exists but is out of scope here — the managed
177
+ **daemon** control socket (via `codex app-server proxy`) needs the managed standalone
178
+ install, and `remote-control` enables the cloud bridge; we ship only the bare-local
179
+ path. Per-folder `config.toml` `[projects."<path>"]` trust gates project-hook loading,
180
+ not addressability.
181
+
182
+ A Codex adapter must declare which launch mode + which socket it targets.
183
+
184
+ ## How to use this in 0.12.0 design
185
+
186
+ For meta-sessions, peer records should expose capability rather than hiding
187
+ backend differences:
188
+
189
+ ```ts
190
+ type WakeMode = "socket" | "file-watch" | "native-push" | "app-server" | "piggyback" | "none";
191
+
192
+ type DeliveryPeer = {
193
+ sessionId: string; // garden id
194
+ kind: "pi-session" | "meta-session";
195
+ backend: "pi" | "claude-code" | "agy" | "codex" | string;
196
+ replyable: boolean;
197
+ wakeMode: WakeMode;
198
+ deliveryLevel: "D0" | "D1" | "D2" | "D3" | "D4" | "D5" | "D6" | "D7" | "D8";
199
+ };
200
+ ```
201
+
202
+ MVP rule of thumb:
203
+
204
+ - expose what is proven;
205
+ - mark partial/conditional honestly;
206
+ - keep transcript ownership native;
207
+ - treat liveness as best-effort hint (`last_seen` + native presence), not as a
208
+ single authoritative socket/WAL/file check;
209
+ - keep lookup authority in the meta-record scan, not a derived index.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Junghan Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.