@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,208 @@
1
+ /**
2
+ * entwurf-control-rpc — the `--entwurf-control` socket protocol SSOT: the wire types
3
+ * (`SenderEnvelope`, `RpcResponse`, the `Rpc*Command` union) AND the newline-delimited-JSON
4
+ * client `sendRpcCommand`, extracted VERBATIM from `entwurf-control.ts` (no logic change).
5
+ *
6
+ * Why a ctx-free lib: the 5d entwurf_v2 production `sendOverSocket` dep needs the same RPC
7
+ * client the legacy control-send + session commands use, but the v2 production assembly
8
+ * lives in `lib/` (ctx-free, gate-/smoke-testable) and must NOT import the surface file's
9
+ * `ExtensionContext`/`ExtensionAPI` world. Keeping ONE wire-protocol definition here means
10
+ * the legacy callers and the v2 producer share one SSOT — the types and the framing never
11
+ * fork. `entwurf-control.ts` re-exports `SenderEnvelope` so its public surface is unchanged.
12
+ *
13
+ * This module performs NO IO of its own beyond the single `net` client connection it is
14
+ * asked to open, and touches nothing pi-specific — it is a pure transport over a path.
15
+ */
16
+
17
+ import * as net from "node:net";
18
+
19
+ // `sender` carries the transparency envelope (agentId / sessionId / cwd /
20
+ // timestamp). All four fields are mandatory whenever `sender` is present —
21
+ // see handleCommand("send") for the reject path. `wants_reply` is a separate
22
+ // etiquette marker (NOT part of the envelope), default false — see
23
+ // handleCommand("send") + parseSenderInfo and the SenderInfo block below for
24
+ // the full semantics: human-conversation hint only, no wait, no polling, no
25
+ // delivery tracking. `<sender_info>` JSON synthesis happens at the receiver
26
+ // side so callers never have to mangle the message body; entwurf-bridge
27
+ // passes the envelope through and the receiving pi prepends the canonical
28
+ // XML-style payload before handing the customMessage to pi.sendMessage.
29
+ export interface SenderEnvelope {
30
+ sessionId: string;
31
+ agentId: string;
32
+ cwd: string;
33
+ timestamp: string; // ISO 8601 UTC
34
+ origin?: "pi-session" | "external-mcp" | "meta-session";
35
+ replyable?: boolean;
36
+ }
37
+
38
+ export interface RpcResponse {
39
+ type: "response";
40
+ command: string;
41
+ success: boolean;
42
+ error?: string;
43
+ data?: unknown;
44
+ id?: string;
45
+ }
46
+
47
+ // Unified command structure
48
+ export interface RpcSendCommand {
49
+ type: "send";
50
+ message: string;
51
+ mode?: "steer" | "follow_up";
52
+ sender?: SenderEnvelope;
53
+ wants_reply?: boolean;
54
+ id?: string;
55
+ }
56
+
57
+ export interface RpcGetMessageCommand {
58
+ type: "get_message";
59
+ id?: string;
60
+ }
61
+
62
+ export interface RpcClearCommand {
63
+ type: "clear";
64
+ summarize?: boolean;
65
+ id?: string;
66
+ }
67
+
68
+ export interface RpcAbortCommand {
69
+ type: "abort";
70
+ id?: string;
71
+ }
72
+
73
+ export interface RpcGetInfoCommand {
74
+ type: "get_info";
75
+ id?: string;
76
+ }
77
+
78
+ export type RpcCommand = RpcSendCommand | RpcGetMessageCommand | RpcClearCommand | RpcAbortCommand | RpcGetInfoCommand;
79
+
80
+ export interface RpcClientOptions {
81
+ timeout?: number;
82
+ }
83
+
84
+ export interface ControlSocketRuntimeInfo {
85
+ cwd?: string;
86
+ modelId?: string;
87
+ modelProvider?: string;
88
+ idle?: boolean;
89
+ }
90
+
91
+ export function parseGetInfoResponseData(data: unknown): ControlSocketRuntimeInfo {
92
+ const value = data as
93
+ | {
94
+ cwd?: unknown;
95
+ model?: { id?: unknown; provider?: unknown } | null;
96
+ idle?: unknown;
97
+ }
98
+ | undefined;
99
+ return {
100
+ cwd: typeof value?.cwd === "string" ? value.cwd : undefined,
101
+ modelId: typeof value?.model?.id === "string" ? value.model.id : undefined,
102
+ modelProvider: typeof value?.model?.provider === "string" ? value.model.provider : undefined,
103
+ idle: typeof value?.idle === "boolean" ? value.idle : undefined,
104
+ };
105
+ }
106
+
107
+ export function formatRuntimeModel(
108
+ info: Pick<ControlSocketRuntimeInfo, "modelId" | "modelProvider">,
109
+ ): string | undefined {
110
+ if (info.modelProvider && info.modelId) return `${info.modelProvider}/${info.modelId}`;
111
+ return info.modelId;
112
+ }
113
+
114
+ export async function fetchControlSocketRuntimeInfo(
115
+ socketPath: string,
116
+ options: RpcClientOptions = {},
117
+ ): Promise<ControlSocketRuntimeInfo> {
118
+ const result = await sendRpcCommand(socketPath, { type: "get_info" }, { timeout: options.timeout ?? 1500 });
119
+ if (!result.response.success) {
120
+ throw new Error(result.response.error ?? "get_info failed");
121
+ }
122
+ return parseGetInfoResponseData(result.response.data);
123
+ }
124
+
125
+ export async function sendRpcCommand(
126
+ socketPath: string,
127
+ command: RpcCommand,
128
+ options: RpcClientOptions = {},
129
+ ): Promise<{ response: RpcResponse }> {
130
+ const { timeout = 5000 } = options;
131
+
132
+ return new Promise((resolve, reject) => {
133
+ const socket = net.createConnection(socketPath);
134
+ socket.setEncoding("utf8");
135
+
136
+ const timeoutHandle = setTimeout(() => {
137
+ socket.destroy(new Error("timeout"));
138
+ }, timeout);
139
+
140
+ let buffer = "";
141
+ // settled guard: a single Promise can only be resolved or rejected
142
+ // once. close/error/timeout/data can all race to terminate the RPC,
143
+ // so every terminal path goes through doResolve/doReject which
144
+ // short-circuits if we have already settled. Without this, the
145
+ // natural close event that follows a clean resolve would try to
146
+ // reject a settled promise (silent under V8) or — worse — duplicate
147
+ // listeners would attempt to write on a destroyed socket.
148
+ let settled = false;
149
+
150
+ const doResolve = (value: { response: RpcResponse }) => {
151
+ if (settled) return;
152
+ settled = true;
153
+ clearTimeout(timeoutHandle);
154
+ socket.removeAllListeners();
155
+ socket.end();
156
+ resolve(value);
157
+ };
158
+
159
+ const doReject = (error: Error) => {
160
+ if (settled) return;
161
+ settled = true;
162
+ clearTimeout(timeoutHandle);
163
+ socket.removeAllListeners();
164
+ socket.destroy();
165
+ reject(error);
166
+ };
167
+
168
+ socket.on("connect", () => {
169
+ socket.write(`${JSON.stringify(command)}\n`);
170
+ });
171
+
172
+ socket.on("data", (chunk) => {
173
+ buffer += chunk;
174
+ let newlineIndex = buffer.indexOf("\n");
175
+ while (newlineIndex !== -1) {
176
+ const line = buffer.slice(0, newlineIndex).trim();
177
+ buffer = buffer.slice(newlineIndex + 1);
178
+ newlineIndex = buffer.indexOf("\n");
179
+ if (!line) continue;
180
+
181
+ try {
182
+ const msg = JSON.parse(line);
183
+ if (msg.type === "response" && msg.command === command.type) {
184
+ doResolve({ response: msg });
185
+ return;
186
+ }
187
+ } catch {
188
+ // Ignore parse errors, keep waiting
189
+ }
190
+ }
191
+ });
192
+
193
+ // Server closed the connection before any response arrived. Without
194
+ // this branch the caller's only failure signal would be the
195
+ // configured wait timeout, which is exactly the failure mode the
196
+ // 2026-05-18 receiver-side stuck incident surfaced through. The
197
+ // settled guard makes this a no-op when we already resolved
198
+ // cleanly — every successful RPC ends with socket.end() and
199
+ // triggers a natural close.
200
+ socket.on("close", () => {
201
+ doReject(new Error("connection closed before response"));
202
+ });
203
+
204
+ socket.on("error", (error) => {
205
+ doReject(error);
206
+ });
207
+ });
208
+ }