@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,211 @@
1
+ // Deterministic gate for the S2a ACP SDK dependency surface.
2
+ //
3
+ // Pins the ACP runtime deps to the 0.11.0 behavior-oracle versions and locks
4
+ // the peer-resolution that makes the Claude ACP adapter satisfiable:
5
+ //
6
+ // @agentclientprotocol/sdk 0.29.0 wire SDK (acp-bridge import source)
7
+ // @agentclientprotocol/claude-agent-acp 0.50.0 Claude adapter (spawn binary)
8
+ // @anthropic-ai/sdk 0.100.1 peer-resolution pin ONLY (see below)
9
+ //
10
+ // The anthropic SDK is NOT an API client / auth surface here. It is a direct
11
+ // dep solely to satisfy @anthropic-ai/claude-agent-sdk@0.3.186's peer floor
12
+ // (>=0.93.0); drop it and the tree resolves a stale 0.91.1 so the peer goes
13
+ // unmet — a failure that would only surface at the first raw turn. 0.11.0's
14
+ // lockfile proves the same shape. Source-level import / API-client
15
+ // instantiation / credential use stays forbidden — asserted in layer (4).
16
+ // (GPT hard constraint 2, revised 2026-06-18: direct dep allowed ONLY as an
17
+ // exact peer-resolution pin; source-level use remains forbidden.)
18
+ //
19
+ // Layers:
20
+ // (1) package.json exact pins for all three deps;
21
+ // (2) pnpm-lock peer-resolution lock (adapter + claude-agent-sdk → 0.100.1);
22
+ // (2b) runtime peer-resolution probe (claude-agent-sdk context → 0.100.1);
23
+ // (3) @agentclientprotocol/sdk value-export surface (silent-rename gate);
24
+ // (4) no tracked source imports the anthropic SDK or builds an API client.
25
+
26
+ import { strict as assert } from "node:assert";
27
+ import { execFileSync } from "node:child_process";
28
+ import { readFileSync } from "node:fs";
29
+ import { createRequire } from "node:module";
30
+ import { dirname, resolve } from "node:path";
31
+
32
+ // Split literals so this gate's own regexes never self-match in layer (4).
33
+ const ANTHROPIC_SDK = `@anthropic-ai/${"sdk"}`;
34
+ const API_CLIENT_CLASS = `${"Anthropic"}`;
35
+
36
+ const repoRoot = resolve(import.meta.dirname, "..");
37
+ const read = (p: string): string => readFileSync(resolve(repoRoot, p), "utf8");
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // (1) package.json — exact pins (no caret/range; S2a freezes the oracle set)
41
+ // ---------------------------------------------------------------------------
42
+ const pkg = JSON.parse(read("package.json")) as { dependencies?: Record<string, string> };
43
+ const deps = pkg.dependencies ?? {};
44
+ const PINS: Record<string, string> = {
45
+ "@agentclientprotocol/sdk": "0.29.0",
46
+ "@agentclientprotocol/claude-agent-acp": "0.50.0",
47
+ [ANTHROPIC_SDK]: "0.100.1",
48
+ };
49
+ for (const [name, ver] of Object.entries(PINS)) {
50
+ assert.equal(
51
+ deps[name],
52
+ ver,
53
+ `package.json dependencies["${name}"] must be exact "${ver}" (got "${deps[name]}") — S2a pins the 0.11.0 oracle versions`,
54
+ );
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // (2) pnpm-lock — peer-resolution lock
59
+ // The adapter and claude-agent-sdk MUST peer-resolve the anthropic SDK to
60
+ // 0.100.1; a 0.91.1 resolution means the >=0.93.0 peer is unmet (the bug
61
+ // this whole pin exists to prevent).
62
+ // ---------------------------------------------------------------------------
63
+ const lock = read("pnpm-lock.yaml");
64
+ assert.match(
65
+ lock,
66
+ /@agentclientprotocol\/claude-agent-acp@0\.50\.0\(@anthropic-ai\/sdk@0\.100\.1/,
67
+ "pnpm-lock: claude-agent-acp@0.50.0 must peer-resolve @anthropic-ai/sdk@0.100.1 (peer-pin), not the stale 0.91.1",
68
+ );
69
+ assert.match(
70
+ lock,
71
+ /@anthropic-ai\/claude-agent-sdk@0\.3\.186\(@anthropic-ai\/sdk@0\.100\.1/,
72
+ "pnpm-lock: claude-agent-sdk@0.3.186 must peer-resolve @anthropic-ai/sdk@0.100.1 — else its >=0.93.0 peer floor is unmet",
73
+ );
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // (2b) runtime peer-resolution probe — the actual Node resolver, not lock text.
77
+ // Layer (2) freezes the publish/install floor; this probes that the
78
+ // claude-agent-sdk → @anthropic-ai/sdk peer edge really resolves to
79
+ // 0.100.1 in a live module graph. Two different failures, both needed.
80
+ // Note: a top-level/adapter-context resolve of the anthropic SDK may see
81
+ // 0.91.1 (pi's own transitive) — that is normal. The edge that must be
82
+ // 0.100.1 is the one *inside* claude-agent-sdk's context.
83
+ // ---------------------------------------------------------------------------
84
+ const pkgInfoFromEntry = (entryPath: string): { name: string; version: string; dir: string } => {
85
+ // A `<pkg>/package.json` subpath resolve can fail under "exports"; walk up
86
+ // from the resolved entry to the nearest package.json instead.
87
+ let dir = dirname(entryPath);
88
+ for (;;) {
89
+ try {
90
+ const pj = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf8")) as {
91
+ name?: string;
92
+ version?: string;
93
+ };
94
+ if (pj.name && pj.version) return { name: pj.name, version: pj.version, dir };
95
+ } catch {
96
+ // no package.json here (or unreadable) — keep walking up.
97
+ }
98
+ const parent = dirname(dir);
99
+ if (parent === dir) throw new Error(`no package.json found walking up from ${entryPath}`);
100
+ dir = parent;
101
+ }
102
+ };
103
+
104
+ const rootRequire = createRequire(resolve(repoRoot, "package.json"));
105
+ const adapterPkgJson = rootRequire.resolve("@agentclientprotocol/claude-agent-acp/package.json");
106
+ const adapterRequire = createRequire(adapterPkgJson);
107
+ const casEntry = adapterRequire.resolve("@anthropic-ai/claude-agent-sdk");
108
+ const casInfo = pkgInfoFromEntry(casEntry);
109
+ assert.equal(
110
+ casInfo.version,
111
+ "0.3.186",
112
+ `@anthropic-ai/claude-agent-sdk must runtime-resolve to 0.3.186 from the adapter context (got ${casInfo.version})`,
113
+ );
114
+ const casRequire = createRequire(resolve(casInfo.dir, "package.json"));
115
+ const sdkEntry = casRequire.resolve(ANTHROPIC_SDK);
116
+ const sdkInfo = pkgInfoFromEntry(sdkEntry);
117
+ assert.equal(
118
+ sdkInfo.version,
119
+ "0.100.1",
120
+ `${ANTHROPIC_SDK} must peer-resolve to 0.100.1 from the claude-agent-sdk context (got ${sdkInfo.version}) — a 0.91.1 here means the >=0.93.0 peer is unmet and the raw turn would break`,
121
+ );
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // (2c) adapter-context wire-SDK + MCP-SDK runtime resolve — the 0.50 bump's real
125
+ // fault surface. Lock text (layer 2) freezes the publish floor; these probe
126
+ // the LIVE module graph the adapter actually traverses: the adapter must SEE
127
+ // the same wire SDK the backend imports at root (0.29.0), and claude-agent-sdk
128
+ // must SEE its declared MCP peer (1.29.x). Cheap edges, both newly relevant
129
+ // after the 0.39→0.50 / 0.22→0.29 bump.
130
+ // ---------------------------------------------------------------------------
131
+ const wireEntry = adapterRequire.resolve("@agentclientprotocol/sdk");
132
+ const wireInfo = pkgInfoFromEntry(wireEntry);
133
+ assert.equal(
134
+ wireInfo.version,
135
+ "0.29.0",
136
+ `@agentclientprotocol/sdk must runtime-resolve to 0.29.0 from the adapter context (got ${wireInfo.version}) — the adapter and the backend must share one wire SDK`,
137
+ );
138
+ // @modelcontextprotocol/sdk gates its bare specifier behind "exports", so a
139
+ // require.resolve of the package ROOT throws (no resolvable entry) — read the
140
+ // installed package.json directly instead. pnpm hoists ONE mcp instance shared
141
+ // by our root dep, pi, and claude-agent-sdk's ^1.29.0 peer, so the installed
142
+ // copy IS the version the adapter/claude-agent-sdk edge resolves.
143
+ const mcpPkg = JSON.parse(read("node_modules/@modelcontextprotocol/sdk/package.json")) as { version?: string };
144
+ assert.ok(
145
+ typeof mcpPkg.version === "string" && mcpPkg.version.startsWith("1.29."),
146
+ `@modelcontextprotocol/sdk must be 1.29.x (got ${mcpPkg.version}) — claude-agent-sdk 0.3.186 declares a ^1.29.0 peer`,
147
+ );
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // (3) @agentclientprotocol/sdk value-export surface (silent-rename gate)
151
+ // The 0.11.0 acp-bridge imports these from the wire SDK; a silent upstream
152
+ // rename would not fail typecheck (type-only erasure) but would break the
153
+ // raw turn. Assert the *value* exports exist at runtime.
154
+ // ---------------------------------------------------------------------------
155
+ // These are the value imports the real ACP code uses: the connectAcpClient
156
+ // adapter (acp-client.ts) drives `client` + the `AGENT_METHODS`/`CLIENT_METHODS`
157
+ // method tables; the backend + raw-turn smoke value-import `ndJsonStream` (the
158
+ // stdio transport) and `PROTOCOL_VERSION`. The deprecated `ClientSideConnection`
159
+ // is no longer used, so it is no longer gated. (Per GPT: gate only the value
160
+ // imports the real code uses — type-only imports are erased.)
161
+ const acpSdk = (await import("@agentclientprotocol/sdk")) as Record<string, unknown>;
162
+ for (const sym of ["client", "ndJsonStream", "PROTOCOL_VERSION", "AGENT_METHODS", "CLIENT_METHODS"]) {
163
+ assert.ok(
164
+ sym in acpSdk,
165
+ `@agentclientprotocol/sdk lost value export "${sym}" — silent upstream rename; the raw ACP turn would break`,
166
+ );
167
+ }
168
+
169
+ // ---------------------------------------------------------------------------
170
+ // (4) no source-level anthropic SDK import / API client
171
+ // peer-pin only. The credential boundary (AGENTS §Operating boundaries)
172
+ // forbids the bridge from importing the SDK or instantiating an API client.
173
+ // Line-based: only real `import ... from` / `require(...)` statements count,
174
+ // so this gate's own split-literal regexes above are never offenders.
175
+ // ---------------------------------------------------------------------------
176
+ const tracked = execFileSync("git", ["ls-files", "*.ts", "*.js", "*.mjs", "*.cjs"], {
177
+ cwd: repoRoot,
178
+ encoding: "utf8",
179
+ })
180
+ .split("\n")
181
+ .filter(Boolean);
182
+
183
+ // Specifier-shaped: any module binding to the anthropic SDK, in any of the
184
+ // forms a source file could reach it — static `from`, `export ... from`,
185
+ // side-effect `import "X"`, dynamic `import("X")`, and `require("X")`.
186
+ const spec = `["']${ANTHROPIC_SDK.replace("/", "\\/")}["']`;
187
+ const specifierPatterns: ReadonlyArray<{ re: RegExp; kind: string }> = [
188
+ { re: new RegExp(String.raw`\bfrom\s+${spec}`), kind: `import/export from ${ANTHROPIC_SDK}` },
189
+ { re: new RegExp(String.raw`^\s*import\s+${spec}`), kind: `side-effect import ${ANTHROPIC_SDK}` },
190
+ { re: new RegExp(String.raw`\bimport\(\s*${spec}\s*\)`), kind: `dynamic import(${ANTHROPIC_SDK})` },
191
+ { re: new RegExp(String.raw`\brequire\(\s*${spec}\s*\)`), kind: `require(${ANTHROPIC_SDK})` },
192
+ ];
193
+ const clientRe = new RegExp(String.raw`\bnew\s+${API_CLIENT_CLASS}\s*\(`);
194
+
195
+ const offenders: string[] = [];
196
+ for (const f of tracked) {
197
+ const src = read(f);
198
+ for (const line of src.split("\n")) {
199
+ for (const { re, kind } of specifierPatterns) {
200
+ if (re.test(line)) offenders.push(`${f}: ${kind}`);
201
+ }
202
+ if (clientRe.test(line)) offenders.push(`${f}: new ${API_CLIENT_CLASS}() API client`);
203
+ }
204
+ }
205
+ assert.equal(
206
+ offenders.length,
207
+ 0,
208
+ `${ANTHROPIC_SDK} is a peer-resolution pin ONLY — no source import / API client allowed:\n ${offenders.join("\n ")}`,
209
+ );
210
+
211
+ console.log("[check-acp-sdk-surface] ACP dep pins + peer-resolution lock + wire-SDK export surface + no-client-use ok");