@stigmer/runner 3.40.1 → 3.41.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 (248) hide show
  1. package/README.md +3 -1
  2. package/dist/.build-fingerprint +1 -1
  3. package/dist/__test-utils__/execution-record-fixture.d.ts +2 -1
  4. package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
  5. package/dist/activities/call-agent.js +1 -1
  6. package/dist/activities/discover-mcp-server.d.ts +7 -1
  7. package/dist/activities/discover-mcp-server.js +13 -29
  8. package/dist/activities/discover-mcp-server.js.map +1 -1
  9. package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +4 -2
  10. package/dist/activities/execute-cursor/__test-utils__/fold.js +3 -4
  11. package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -1
  12. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +1 -0
  13. package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
  14. package/dist/activities/execute-cursor/approval-policy.d.ts +1 -29
  15. package/dist/activities/execute-cursor/approval-policy.js +11 -35
  16. package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
  17. package/dist/activities/execute-cursor/boundary-rows.js +4 -4
  18. package/dist/activities/execute-cursor/boundary-rows.js.map +1 -1
  19. package/dist/activities/execute-cursor/translator.d.ts +12 -17
  20. package/dist/activities/execute-cursor/translator.js +13 -30
  21. package/dist/activities/execute-cursor/translator.js.map +1 -1
  22. package/dist/activities/execute-cursor/turn-stream.js +4 -2
  23. package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
  24. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +11 -1
  25. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -3
  26. package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
  27. package/dist/activities/execute-deep-agent/inline-publisher.d.ts +33 -2
  28. package/dist/activities/execute-deep-agent/inline-publisher.js +33 -7
  29. package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
  30. package/dist/activities/execute-deep-agent/platform-route.d.ts +48 -13
  31. package/dist/activities/execute-deep-agent/platform-route.js +87 -19
  32. package/dist/activities/execute-deep-agent/platform-route.js.map +1 -1
  33. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +20 -3
  34. package/dist/activities/execute-deep-agent/streaming-side-effects.js +26 -4
  35. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  36. package/dist/activities/execute-deep-agent/translator.d.ts +5 -4
  37. package/dist/activities/execute-deep-agent/translator.js +5 -4
  38. package/dist/activities/execute-deep-agent/translator.js.map +1 -1
  39. package/dist/activities/execute-deep-agent/turn-settle.js +6 -6
  40. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
  41. package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
  42. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
  43. package/dist/activities/execute-deep-agent/turn-stream.d.ts +10 -6
  44. package/dist/activities/execute-deep-agent/turn-stream.js +6 -16
  45. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
  46. package/dist/activities/execute-deep-agent/turn.d.ts +4 -1
  47. package/dist/activities/execute-deep-agent/turn.js +15 -6
  48. package/dist/activities/execute-deep-agent/turn.js.map +1 -1
  49. package/dist/activities/workflow-event-activities.js +2 -0
  50. package/dist/activities/workflow-event-activities.js.map +1 -1
  51. package/dist/config.d.ts +9 -0
  52. package/dist/config.js +2 -0
  53. package/dist/config.js.map +1 -1
  54. package/dist/exit-backstop.d.ts +64 -0
  55. package/dist/exit-backstop.js +79 -0
  56. package/dist/exit-backstop.js.map +1 -0
  57. package/dist/harness/terminal-table.d.ts +12 -5
  58. package/dist/harness/terminal-table.js +13 -13
  59. package/dist/harness/terminal-table.js.map +1 -1
  60. package/dist/harness/transcript/builder.d.ts +21 -0
  61. package/dist/harness/transcript/builder.js +53 -31
  62. package/dist/harness/transcript/builder.js.map +1 -1
  63. package/dist/harness/transcript/events.d.ts +22 -26
  64. package/dist/harness/transcript/events.js +6 -5
  65. package/dist/harness/transcript/events.js.map +1 -1
  66. package/dist/harness/turn-context.d.ts +3 -1
  67. package/dist/harness/turn-context.js +5 -3
  68. package/dist/harness/turn-context.js.map +1 -1
  69. package/dist/main.js +10 -4
  70. package/dist/main.js.map +1 -1
  71. package/dist/middleware/cost-advisory.d.ts +6 -3
  72. package/dist/middleware/cost-advisory.js +6 -3
  73. package/dist/middleware/cost-advisory.js.map +1 -1
  74. package/dist/middleware/execution-budget.d.ts +22 -7
  75. package/dist/middleware/execution-budget.js +57 -35
  76. package/dist/middleware/execution-budget.js.map +1 -1
  77. package/dist/middleware/index.d.ts +10 -7
  78. package/dist/middleware/index.js +10 -7
  79. package/dist/middleware/index.js.map +1 -1
  80. package/dist/middleware/types.d.ts +6 -1
  81. package/dist/runner-manager.js +3 -2
  82. package/dist/runner-manager.js.map +1 -1
  83. package/dist/runner.d.ts +25 -0
  84. package/dist/runner.js +41 -14
  85. package/dist/runner.js.map +1 -1
  86. package/dist/shared/approval-policy.d.ts +23 -13
  87. package/dist/shared/approval-policy.js +47 -14
  88. package/dist/shared/approval-policy.js.map +1 -1
  89. package/dist/shared/caller-identity.d.ts +4 -5
  90. package/dist/shared/caller-identity.js +4 -5
  91. package/dist/shared/caller-identity.js.map +1 -1
  92. package/dist/shared/channel-attachment.js +3 -2
  93. package/dist/shared/channel-attachment.js.map +1 -1
  94. package/dist/shared/connect-backfill.d.ts +2 -1
  95. package/dist/shared/connect-backfill.js +2 -2
  96. package/dist/shared/connect-backfill.js.map +1 -1
  97. package/dist/shared/mcp-resolver.d.ts +10 -1
  98. package/dist/shared/mcp-resolver.js +11 -2
  99. package/dist/shared/mcp-resolver.js.map +1 -1
  100. package/dist/shared/memory-attachment.js +3 -2
  101. package/dist/shared/memory-attachment.js.map +1 -1
  102. package/dist/shared/model-error.d.ts +25 -3
  103. package/dist/shared/model-error.js +78 -10
  104. package/dist/shared/model-error.js.map +1 -1
  105. package/dist/shared/platform-server-address.d.ts +71 -0
  106. package/dist/shared/platform-server-address.js +103 -0
  107. package/dist/shared/platform-server-address.js.map +1 -0
  108. package/dist/shared/synthesized-attachment.d.ts +0 -5
  109. package/dist/shared/synthesized-attachment.js +0 -12
  110. package/dist/shared/synthesized-attachment.js.map +1 -1
  111. package/dist/shared/tool-rounds.d.ts +51 -23
  112. package/dist/shared/tool-rounds.js +67 -24
  113. package/dist/shared/tool-rounds.js.map +1 -1
  114. package/dist/shared/workspace/platform-dir.d.ts +15 -0
  115. package/dist/shared/workspace/platform-dir.js +18 -1
  116. package/dist/shared/workspace/platform-dir.js.map +1 -1
  117. package/dist/shared/workspace/stigmer-link.d.ts +21 -14
  118. package/dist/shared/workspace/stigmer-link.js +91 -29
  119. package/dist/shared/workspace/stigmer-link.js.map +1 -1
  120. package/dist/tools/url-guard.d.ts +5 -1
  121. package/dist/tools/url-guard.js +9 -2
  122. package/dist/tools/url-guard.js.map +1 -1
  123. package/dist/worker.d.ts +11 -2
  124. package/dist/worker.js +1 -1
  125. package/dist/worker.js.map +1 -1
  126. package/dist/workflow-engine/tasks/human-input.d.ts +16 -0
  127. package/dist/workflow-engine/tasks/human-input.js +79 -37
  128. package/dist/workflow-engine/tasks/human-input.js.map +1 -1
  129. package/dist/workflow-engine/types.d.ts +14 -4
  130. package/dist/workflow-engine/types.js.map +1 -1
  131. package/dist/workflows/call-agent-orchestrator.d.ts +13 -0
  132. package/dist/workflows/call-agent-orchestrator.js.map +1 -1
  133. package/dist/workflows/engine-core.d.ts +25 -0
  134. package/dist/workflows/engine-core.js.map +1 -1
  135. package/dist/workflows/execute-from-execution.d.ts +3 -0
  136. package/dist/workflows/execute-from-execution.js.map +1 -1
  137. package/dist/workflows/human-input-orchestrator.d.ts +9 -12
  138. package/dist/workflows/human-input-orchestrator.js +12 -44
  139. package/dist/workflows/human-input-orchestrator.js.map +1 -1
  140. package/package.json +5 -5
  141. package/src/__test-utils__/config-fixture.ts +1 -0
  142. package/src/__test-utils__/execution-record-fixture.ts +2 -1
  143. package/src/__test-utils__/harness-contract/runtime-contract.ts +5 -3
  144. package/src/__test-utils__/test-gate.ts +8 -0
  145. package/src/__tests__/exit-backstop.test.ts +91 -0
  146. package/src/__tests__/golden-e2e.test.ts +80 -62
  147. package/src/__tests__/history-encryption-e2e.test.ts +51 -27
  148. package/src/__tests__/runner.test.ts +60 -2
  149. package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
  150. package/src/activities/__tests__/discover-mcp-server.hang.test.ts +5 -1
  151. package/src/activities/__tests__/discover-mcp-server.test.ts +90 -49
  152. package/src/activities/__tests__/workflow-event-activities.test.ts +28 -7
  153. package/src/activities/call-agent.ts +1 -1
  154. package/src/activities/discover-mcp-server.ts +27 -37
  155. package/src/activities/execute-cursor/__test-utils__/fold.ts +6 -4
  156. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +1 -0
  157. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +4 -29
  158. package/src/activities/execute-cursor/__tests__/hermetic/bypassed-tool-call.test.ts +120 -0
  159. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -5
  160. package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +0 -2
  161. package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
  162. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +0 -3
  163. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +0 -2
  164. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +0 -2
  165. package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +0 -2
  166. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +0 -3
  167. package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +9 -7
  168. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +6 -1
  169. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +8 -11
  170. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +1 -3
  171. package/src/activities/execute-cursor/__tests__/translator.test.ts +68 -15
  172. package/src/activities/execute-cursor/approval-policy.ts +11 -36
  173. package/src/activities/execute-cursor/boundary-rows.ts +3 -3
  174. package/src/activities/execute-cursor/translator.ts +15 -32
  175. package/src/activities/execute-cursor/turn-stream.ts +4 -1
  176. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +33 -2
  177. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +1 -0
  178. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +4 -0
  179. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +1 -1
  180. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +1 -1
  181. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +1 -1
  182. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +8 -73
  183. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
  184. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +17 -0
  185. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +86 -26
  186. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +9 -5
  187. package/src/activities/execute-deep-agent/__tests__/hermetic/thrown-turn-drains-publishes.test.ts +159 -0
  188. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +84 -0
  189. package/src/activities/execute-deep-agent/__tests__/platform-route.test.ts +47 -10
  190. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +34 -0
  191. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +13 -5
  192. package/src/activities/execute-deep-agent/inline-publisher.ts +47 -7
  193. package/src/activities/execute-deep-agent/platform-route.ts +109 -20
  194. package/src/activities/execute-deep-agent/streaming-side-effects.ts +27 -4
  195. package/src/activities/execute-deep-agent/translator.ts +5 -4
  196. package/src/activities/execute-deep-agent/turn-settle.ts +6 -6
  197. package/src/activities/execute-deep-agent/turn-setup.ts +7 -7
  198. package/src/activities/execute-deep-agent/turn-stream.ts +15 -22
  199. package/src/activities/execute-deep-agent/turn.ts +16 -7
  200. package/src/activities/workflow-event-activities.ts +2 -0
  201. package/src/config.ts +11 -0
  202. package/src/exit-backstop.ts +97 -0
  203. package/src/harness/README.md +5 -5
  204. package/src/harness/terminal-table.ts +13 -13
  205. package/src/harness/transcript/__tests__/builder.test.ts +93 -21
  206. package/src/harness/transcript/builder.ts +53 -30
  207. package/src/harness/transcript/events.ts +22 -27
  208. package/src/harness/turn-context.ts +5 -3
  209. package/src/main.ts +9 -5
  210. package/src/middleware/__tests__/execution-budget.test.ts +181 -103
  211. package/src/middleware/cost-advisory.ts +6 -3
  212. package/src/middleware/execution-budget.ts +60 -34
  213. package/src/middleware/index.ts +10 -7
  214. package/src/middleware/types.ts +6 -1
  215. package/src/runner-manager.ts +3 -2
  216. package/src/runner.ts +57 -15
  217. package/src/shared/__tests__/approval-policy.test.ts +41 -0
  218. package/src/shared/__tests__/artifact-storage.test.ts +1 -0
  219. package/src/shared/__tests__/connect-backfill.test.ts +15 -11
  220. package/src/shared/__tests__/mcp-resolver.test.ts +72 -10
  221. package/src/shared/__tests__/model-error.test.ts +71 -3
  222. package/src/shared/__tests__/platform-server-address.test.ts +122 -0
  223. package/src/shared/__tests__/tool-rounds.test.ts +64 -14
  224. package/src/shared/approval-policy.ts +57 -14
  225. package/src/shared/caller-identity.ts +4 -5
  226. package/src/shared/channel-attachment.ts +2 -2
  227. package/src/shared/connect-backfill.ts +5 -1
  228. package/src/shared/mcp-resolver.ts +11 -1
  229. package/src/shared/memory-attachment.ts +2 -2
  230. package/src/shared/model-error.ts +87 -9
  231. package/src/shared/platform-server-address.ts +123 -0
  232. package/src/shared/synthesized-attachment.ts +0 -12
  233. package/src/shared/tool-rounds.ts +70 -24
  234. package/src/shared/workspace/__tests__/stigmer-link.test.ts +107 -10
  235. package/src/shared/workspace/platform-dir.ts +19 -1
  236. package/src/shared/workspace/stigmer-link.ts +99 -29
  237. package/src/tools/__tests__/url-guard.test.ts +19 -1
  238. package/src/tools/url-guard.ts +9 -1
  239. package/src/worker.ts +12 -2
  240. package/src/workflow-engine/__tests__/golden-execution.test.ts +2 -1
  241. package/src/workflow-engine/__tests__/loader.test.ts +1 -1
  242. package/src/workflow-engine/__tests__/tasks/human-input.test.ts +133 -57
  243. package/src/workflow-engine/tasks/human-input.ts +92 -42
  244. package/src/workflow-engine/types.ts +16 -4
  245. package/src/workflows/call-agent-orchestrator.ts +7 -0
  246. package/src/workflows/engine-core.ts +18 -0
  247. package/src/workflows/execute-from-execution.ts +2 -1
  248. package/src/workflows/human-input-orchestrator.ts +15 -56
@@ -4,6 +4,10 @@ import type {
4
4
  DiscoveredToolResult,
5
5
  DiscoverMcpServerOutput,
6
6
  } from "../discover-mcp-server.js";
7
+ import { testConfig } from "../../__test-utils__/config-fixture.js";
8
+
9
+ /** The runner endpoints discovery fills STIGMER_SERVER_ADDRESS from. */
10
+ const PLATFORM_ENDPOINTS = testConfig();
7
11
 
8
12
  vi.mock("../../idle-watchdog.js", () => ({
9
13
  activityStarted: vi.fn(),
@@ -221,47 +225,83 @@ describe("DiscoverMcpServer activity", () => {
221
225
  });
222
226
 
223
227
  // ─────────────────────────────────────────────────────────────────────────
224
- // injectPlatformEnv
228
+ // STIGMER_SERVER_ADDRESS — the platform fill (shared/platform-server-address.ts)
225
229
  // ─────────────────────────────────────────────────────────────────────────
226
230
 
227
- describe("injectPlatformEnv", () => {
228
- const savedEnv = process.env;
231
+ describe("the platform STIGMER_SERVER_ADDRESS fill", () => {
232
+ async function connectionConfigFor(
233
+ spec: any,
234
+ executionContext: any,
235
+ platformEndpoints = PLATFORM_ENDPOINTS,
236
+ ): Promise<any> {
237
+ const { discoverMcpServer } = await import("../discover-mcp-server.js");
238
+ const { MultiServerMCPClient } = await import("@langchain/mcp-adapters");
239
+ const mockClient = makeMockStigmerClient({
240
+ mcpServer: makeMcpServer({ metadata: { slug: "stigmer" }, spec }),
241
+ executionContext,
242
+ });
243
+ mockInitializeConnections.mockResolvedValue({});
244
+ mockGetClient.mockResolvedValue(makeMockMcpClient({ tools: [] }));
245
+ await discoverMcpServer(
246
+ { mcpServerId: "mcp-stigmer", executionContextId: "ctx-addr" },
247
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints },
248
+ );
249
+ return (vi.mocked(MultiServerMCPClient).mock.calls[0][0] as any).stigmer;
250
+ }
251
+
252
+ it("fills a stdio server's required address from the backend endpoint instead of failing closed", async () => {
253
+ const spec = makeStdioSpec("stigmer", ["mcp-server"]);
254
+ spec.env = { STIGMER_SERVER_ADDRESS: { description: "gRPC host:port" } };
229
255
 
230
- afterEach(() => {
231
- process.env = savedEnv;
232
- });
256
+ const config = await connectionConfigFor(spec, { spec: { data: {} } });
233
257
 
234
- it("returns envVars unchanged when no declared keys match", async () => {
235
- const { injectPlatformEnv } = await import("../discover-mcp-server.js");
236
- const env = { SOME_KEY: "value" };
237
- const result = injectPlatformEnv(new Set(["SOME_KEY"]), env);
238
- expect(result).toBe(env);
258
+ // testConfig's backend endpoint is http://127.0.0.1:1.
259
+ expect(config.env.STIGMER_SERVER_ADDRESS).toBe("127.0.0.1:1");
239
260
  });
240
261
 
241
- it("injects STIGMER_SERVER_ADDRESS from STIGMER_MCP_PUBLIC_ENDPOINT", async () => {
242
- const { injectPlatformEnv } = await import("../discover-mcp-server.js");
243
- process.env = { ...savedEnv, STIGMER_MCP_PUBLIC_ENDPOINT: "https://mcp.stigmer.ai" };
244
- const result = injectPlatformEnv(
245
- new Set(["STIGMER_SERVER_ADDRESS"]),
246
- { STIGMER_SERVER_ADDRESS: "stale-value" },
262
+ it("keeps an address the execution context delivered", async () => {
263
+ const spec = makeStdioSpec("stigmer", ["mcp-server"]);
264
+ spec.env = { STIGMER_SERVER_ADDRESS: { description: "gRPC host:port" } };
265
+
266
+ const config = await connectionConfigFor(
267
+ spec,
268
+ { spec: { data: { STIGMER_SERVER_ADDRESS: { value: "other.example:7234" } } } },
269
+ testConfig({ mcpPublicEndpoint: "https://api.example.com" }),
247
270
  );
248
- expect(result.STIGMER_SERVER_ADDRESS).toBe("https://mcp.stigmer.ai");
271
+
272
+ expect(config.env.STIGMER_SERVER_ADDRESS).toBe("other.example:7234");
249
273
  });
250
274
 
251
- it("does not inject when source env var is not set", async () => {
252
- const { injectPlatformEnv } = await import("../discover-mcp-server.js");
253
- process.env = { ...savedEnv };
254
- delete process.env.STIGMER_MCP_PUBLIC_ENDPOINT;
255
- const env = { STIGMER_SERVER_ADDRESS: "original" };
256
- const result = injectPlatformEnv(new Set(["STIGMER_SERVER_ADDRESS"]), env);
257
- expect(result).toBe(env);
275
+ it("templates an http server's header from the operator's public endpoint", async () => {
276
+ const spec = makeHttpSpec("https://mcp.example.com");
277
+ spec.serverType.value.headers = { "X-Stigmer-Server": "${STIGMER_SERVER_ADDRESS}" };
278
+ spec.env = { STIGMER_SERVER_ADDRESS: {} };
279
+
280
+ const config = await connectionConfigFor(
281
+ spec,
282
+ { spec: { data: {} } },
283
+ testConfig({ mcpPublicEndpoint: "https://api.example.com" }),
284
+ );
285
+
286
+ expect(config.headers["X-Stigmer-Server"]).toBe("api.example.com:443");
258
287
  });
259
288
 
260
- it("returns envVars unchanged when declaredEnvKeys is empty", async () => {
261
- const { injectPlatformEnv } = await import("../discover-mcp-server.js");
262
- const env = { SOME_KEY: "value" };
263
- const result = injectPlatformEnv(new Set(), env);
264
- expect(result).toBe(env);
289
+ it("never hands an http server the backend endpoint: without a public one its required address is a credential the connect flow owes", async () => {
290
+ const { discoverMcpServer, CredentialResolutionError } = await import("../discover-mcp-server.js");
291
+ const spec = makeHttpSpec("https://mcp.example.com");
292
+ spec.env = { STIGMER_SERVER_ADDRESS: {} };
293
+ const mockClient = makeMockStigmerClient({
294
+ mcpServer: makeMcpServer({ metadata: { slug: "stigmer" }, spec }),
295
+ executionContext: { spec: { data: {} } },
296
+ });
297
+
298
+ const promise = discoverMcpServer(
299
+ { mcpServerId: "mcp-stigmer", executionContextId: "ctx-addr" },
300
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
301
+ );
302
+
303
+ await expect(promise).rejects.toThrow(CredentialResolutionError);
304
+ await expect(promise).rejects.toThrow(/STIGMER_SERVER_ADDRESS/);
265
305
  });
266
306
  });
267
307
 
@@ -291,7 +331,7 @@ describe("DiscoverMcpServer activity", () => {
291
331
 
292
332
  const result = await discoverMcpServer(
293
333
  { mcpServerId: "mcp-123" },
294
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
334
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
295
335
  );
296
336
 
297
337
  expect(result.tools).toHaveLength(2);
@@ -320,7 +360,7 @@ describe("DiscoverMcpServer activity", () => {
320
360
  await expect(
321
361
  discoverMcpServer(
322
362
  { mcpServerId: "mcp-123" },
323
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
363
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
324
364
  ),
325
365
  ).rejects.toThrow(McpTransportError);
326
366
  expect(mockInitializeConnections).not.toHaveBeenCalled();
@@ -348,7 +388,7 @@ describe("DiscoverMcpServer activity", () => {
348
388
 
349
389
  const result = await discoverMcpServer(
350
390
  { mcpServerId: "mcp-456" },
351
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
391
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
352
392
  );
353
393
 
354
394
  expect(result.tools).toHaveLength(1);
@@ -369,7 +409,7 @@ describe("DiscoverMcpServer activity", () => {
369
409
  });
370
410
 
371
411
  await expect(
372
- discoverMcpServer({ mcpServerId: "mcp-bad" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" }),
412
+ discoverMcpServer({ mcpServerId: "mcp-bad" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS }),
373
413
  ).rejects.toThrow("not found or has no spec");
374
414
  });
375
415
 
@@ -384,7 +424,7 @@ describe("DiscoverMcpServer activity", () => {
384
424
  });
385
425
 
386
426
  await expect(
387
- discoverMcpServer({ mcpServerId: "mcp-noconfig" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" }),
427
+ discoverMcpServer({ mcpServerId: "mcp-noconfig" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS }),
388
428
  ).rejects.toThrow("no valid server type configured");
389
429
  });
390
430
 
@@ -412,7 +452,7 @@ describe("DiscoverMcpServer activity", () => {
412
452
 
413
453
  const result = await discoverMcpServer(
414
454
  { mcpServerId: "mcp-env", executionContextId: "ctx-abc" },
415
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
455
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
416
456
  );
417
457
 
418
458
  expect(result.tools).toEqual([]);
@@ -448,7 +488,7 @@ describe("DiscoverMcpServer activity", () => {
448
488
  executionContextId: "ctx-abc",
449
489
  executionContextToken: "scoped-ec-token",
450
490
  },
451
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
491
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
452
492
  );
453
493
 
454
494
  expect(mockClient.getExecutionContextByExecutionId).toHaveBeenCalledWith(
@@ -494,7 +534,7 @@ describe("DiscoverMcpServer activity", () => {
494
534
 
495
535
  const result = await discoverMcpServer(
496
536
  { mcpServerId: "mcp-isc", executionContextId: "ctx-isc" },
497
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
537
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
498
538
  );
499
539
 
500
540
  expect(result.tools).toHaveLength(1);
@@ -522,7 +562,7 @@ describe("DiscoverMcpServer activity", () => {
522
562
 
523
563
  await discoverMcpServer(
524
564
  { mcpServerId: "mcp-no-env" },
525
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
565
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
526
566
  );
527
567
 
528
568
  expect(mockClient.getExecutionContextByExecutionId).not.toHaveBeenCalled();
@@ -548,7 +588,7 @@ describe("DiscoverMcpServer activity", () => {
548
588
 
549
589
  const result = await discoverMcpServer(
550
590
  { mcpServerId: "mcp-no-res" },
551
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
591
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
552
592
  );
553
593
 
554
594
  expect(result.tools).toHaveLength(1);
@@ -582,7 +622,7 @@ describe("DiscoverMcpServer activity", () => {
582
622
 
583
623
  const result = await discoverMcpServer(
584
624
  { mcpServerId: "mcp-existing" },
585
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
625
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
586
626
  );
587
627
 
588
628
  expect(result.previousToolsFingerprint).not.toBe("");
@@ -617,7 +657,7 @@ describe("DiscoverMcpServer activity", () => {
617
657
 
618
658
  const result = await discoverMcpServer(
619
659
  { mcpServerId: "mcp-fp" },
620
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
660
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
621
661
  );
622
662
 
623
663
  const expected = toolsFingerprint(result.tools);
@@ -640,7 +680,7 @@ describe("DiscoverMcpServer activity", () => {
640
680
 
641
681
  const result = await discoverMcpServer(
642
682
  { mcpServerId: "mcp-empty" },
643
- { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" },
683
+ { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS },
644
684
  );
645
685
 
646
686
  expect(result.newToolsFingerprint).toBe("");
@@ -665,7 +705,7 @@ describe("DiscoverMcpServer activity", () => {
665
705
  await expect(
666
706
  discoverMcpServer(
667
707
  { mcpServerId: "mcp-monday", executionContextId: "ctx-1" },
668
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
708
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
669
709
  ),
670
710
  ).rejects.toThrow(CredentialResolutionError);
671
711
  expect(mockInitializeConnections).not.toHaveBeenCalled();
@@ -689,7 +729,7 @@ describe("DiscoverMcpServer activity", () => {
689
729
 
690
730
  const result = await discoverMcpServer(
691
731
  { mcpServerId: "mcp-lenient", executionContextId: "ctx-2" },
692
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
732
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
693
733
  );
694
734
  expect(result.tools).toEqual([]);
695
735
  });
@@ -714,7 +754,7 @@ describe("DiscoverMcpServer activity", () => {
714
754
 
715
755
  const result = await discoverMcpServer(
716
756
  { mcpServerId: "mcp-optional", executionContextId: "ctx-3" },
717
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
757
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
718
758
  );
719
759
  expect(result.tools).toEqual([]);
720
760
  });
@@ -731,7 +771,7 @@ describe("DiscoverMcpServer activity", () => {
731
771
 
732
772
  const promise = discoverMcpServer(
733
773
  { mcpServerId: "mcp-monday", executionContextId: "ctx-4" },
734
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
774
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
735
775
  );
736
776
  await expect(promise).rejects.toThrow(CredentialResolutionError);
737
777
  await expect(promise).rejects.toThrow(/delivered no credentials/);
@@ -755,7 +795,7 @@ describe("DiscoverMcpServer activity", () => {
755
795
 
756
796
  const promise = discoverMcpServer(
757
797
  { mcpServerId: "mcp-monday", executionContextId: "ctx-5" },
758
- { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
798
+ { stigmerClient: mockClient as any, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
759
799
  );
760
800
  await expect(promise).rejects.toThrow(CredentialResolutionError);
761
801
  await expect(promise).rejects.toThrow(/MONDAY_ACCESS_TOKEN.*delivered redacted|delivered redacted.*MONDAY_ACCESS_TOKEN/s);
@@ -775,7 +815,7 @@ describe("DiscoverMcpServer activity", () => {
775
815
  mockInitializeConnections.mockRejectedValue(new Error("Connection refused"));
776
816
 
777
817
  await expect(
778
- discoverMcpServer({ mcpServerId: "mcp-fail" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed" }),
818
+ discoverMcpServer({ mcpServerId: "mcp-fail" }, { stigmerClient: mockClient as any, transportPosture: "stdio-allowed", platformEndpoints: PLATFORM_ENDPOINTS }),
779
819
  ).rejects.toThrow("Connection refused");
780
820
 
781
821
  expect(mockClose).toHaveBeenCalled();
@@ -903,6 +943,7 @@ function makeConfig() {
903
943
  temporalConnection: {},
904
944
  stigmerBackendEndpoint: "http://localhost:7234",
905
945
  mcpBridgeEndpoint: null,
946
+ mcpPublicEndpoint: null,
906
947
  stigmerTokenRef: { current: "test-token" },
907
948
  cursorApiKey: "",
908
949
  workspaceRootDir: "/tmp/test",
@@ -4,6 +4,7 @@ import { StigmerClient } from "../../client/stigmer-client.js";
4
4
  import { WorkflowEventType } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
5
5
  import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
6
6
  import { WorkflowTaskStatus } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/enum_pb";
7
+ import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
7
8
  import type { WorkflowEventDescriptor } from "../../workflow-engine/types.js";
8
9
 
9
10
  const mockGetEventLogHighWaterMark = vi.fn<(executionId: string) => Promise<bigint>>();
@@ -648,7 +649,7 @@ describe("toProtoEvent", () => {
648
649
  expect(actor!.avatar).toBe("https://example.com/ada.png");
649
650
  });
650
651
 
651
- it("does not map outcome or autoResolved (proto limitation)", () => {
652
+ it("maps the chosen outcome and a human decision's autoResolved=false", () => {
652
653
  const evt = toProtoEvent({
653
654
  type: "approval_resolved",
654
655
  taskName: "gate",
@@ -657,16 +658,36 @@ describe("toProtoEvent", () => {
657
658
  resolvedBy: "bob",
658
659
  comment: "",
659
660
  waitDurationMs: 1000,
661
+ autoResolved: false,
662
+ });
663
+
664
+ if (evt.payload.case !== "approvalResolved") throw new Error("unexpected");
665
+ const p = evt.payload.value;
666
+ expect(p.outcome).toBe("needs_revision");
667
+ expect(p.autoResolved).toBe(false);
668
+ // A human_input gate's outcome is a declared string, never mapped onto
669
+ // the agent ApprovalAction enum: action stays unset.
670
+ expect(p.action).toBe(ApprovalAction.UNSPECIFIED);
671
+ });
672
+
673
+ it("maps a timeout auto-resolution: the policy's outcome, autoResolved, no reviewer", () => {
674
+ const evt = toProtoEvent({
675
+ type: "approval_resolved",
676
+ taskName: "gate",
677
+ occurredAt: NOW,
678
+ outcome: "escalate",
679
+ resolvedBy: "",
680
+ comment: "",
681
+ waitDurationMs: 5000,
660
682
  autoResolved: true,
661
683
  });
662
684
 
663
685
  if (evt.payload.case !== "approvalResolved") throw new Error("unexpected");
664
- // action field stays at default (UNSPECIFIED) because toProtoEvent
665
- // does not map the string outcome to ApprovalAction enum.
666
- // autoResolved has no corresponding proto field.
667
- // This documents the current limitation per Finding #2.
668
- expect(evt.payload.value.resolvedBy).toBe("bob");
669
- expect(evt.payload.value.waitDurationMs).toBe(BigInt(1000));
686
+ const p = evt.payload.value;
687
+ expect(p.outcome).toBe("escalate");
688
+ expect(p.autoResolved).toBe(true);
689
+ expect(p.resolvedBy).toBe("");
690
+ expect(p.resolvedByActor).toBeUndefined();
670
691
  });
671
692
  });
672
693
 
@@ -228,7 +228,7 @@ export async function callAgentAction(
228
228
  // the author may set is forwarded to a field the runner enforces.
229
229
  // model_name replaces the agent's default outright; max_cost_usd feeds
230
230
  // the turn runtime's cost guard (shared/cost-guard.ts, both harnesses);
231
- // max_tool_rounds feeds resolveRecursionLimit (native
231
+ // max_tool_rounds feeds resolveToolRoundLimit (native
232
232
  // harness only); service_tier and thinking_mode feed the cursor
233
233
  // harness's explicit variant selection (issues #357/#772). Zero/unset
234
234
  // means "no override" and is omitted.
@@ -31,6 +31,12 @@ import {
31
31
  } from "../shared/mcp-transport-guard.js";
32
32
  import { detectOAuthChallenge } from "../shared/mcp-oauth-detect.js";
33
33
  import { injectAnonymousCallerIdentityForDiscovery } from "../shared/caller-identity.js";
34
+ import {
35
+ fillPlatformServerAddress,
36
+ platformServerAddress,
37
+ SERVER_ADDRESS_ENV_KEY,
38
+ type PlatformEndpoints,
39
+ } from "../shared/platform-server-address.js";
34
40
  import { startHeartbeat } from "../shared/heartbeat.js";
35
41
  import { withTimeout } from "../shared/with-timeout.js";
36
42
  import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
@@ -70,10 +76,6 @@ const STDIO_INIT_TIMEOUT_MS = 270_000;
70
76
  */
71
77
  const REDACTED_MARKER = "***REDACTED***";
72
78
 
73
- const PLATFORM_INJECTABLE_MAP: Record<string, string> = {
74
- STIGMER_SERVER_ADDRESS: "STIGMER_MCP_PUBLIC_ENDPOINT",
75
- };
76
-
77
79
  // ─────────────────────────────────────────────────────────────────────────────
78
80
  // Types
79
81
  // ─────────────────────────────────────────────────────────────────────────────
@@ -237,36 +239,6 @@ function structToPlainObject(struct: unknown): Record<string, unknown> | null {
237
239
  }
238
240
  }
239
241
 
240
- // ─────────────────────────────────────────────────────────────────────────────
241
- // Platform Env Injection
242
- // ─────────────────────────────────────────────────────────────────────────────
243
-
244
- export function injectPlatformEnv(
245
- declaredEnvKeys: Set<string>,
246
- envVars: Record<string, string>,
247
- ): Record<string, string> {
248
- if (declaredEnvKeys.size === 0) return envVars;
249
-
250
- let result: Record<string, string> | undefined;
251
-
252
- for (const [targetKey, sourceKey] of Object.entries(PLATFORM_INJECTABLE_MAP)) {
253
- if (!declaredEnvKeys.has(targetKey)) continue;
254
- const value = process.env[sourceKey];
255
- if (!value) continue;
256
-
257
- if (!result) result = { ...envVars };
258
- if (targetKey in result && result[targetKey] !== value) {
259
- console.info(
260
- `Platform env var '${targetKey}' overrides value from ExecutionContext ` +
261
- `(platform infra vars are authoritative)`,
262
- );
263
- }
264
- result[targetKey] = value;
265
- }
266
-
267
- return result ?? envVars;
268
- }
269
-
270
242
  // ─────────────────────────────────────────────────────────────────────────────
271
243
  // Core Discovery Logic (no Temporal coupling)
272
244
  // ─────────────────────────────────────────────────────────────────────────────
@@ -281,6 +253,12 @@ export interface DiscoverDeps {
281
253
  * tool enumeration.
282
254
  */
283
255
  transportPosture: McpTransportPosture;
256
+ /**
257
+ * The runner's endpoints a missing STIGMER_SERVER_ADDRESS is filled from
258
+ * (the runner Config satisfies it) — the same fill execution applies, so
259
+ * discovery dials what the run will dial.
260
+ */
261
+ platformEndpoints: PlatformEndpoints;
284
262
  }
285
263
 
286
264
  export async function discoverMcpServer(
@@ -305,16 +283,25 @@ export async function discoverMcpServer(
305
283
  const previousState = extractPreviousState(mcpServer);
306
284
 
307
285
  const declaredEnv = mcpServer.spec.env ?? {};
286
+ // A key the platform fills for this transport is not a credential the
287
+ // connect flow must deliver, so it never makes an empty EC a failure.
288
+ const platformFillsAddress =
289
+ platformServerAddress(mcpServer.spec.serverType.case, deps.platformEndpoints) !== null;
290
+ const credentialDeclarations = platformFillsAddress
291
+ ? Object.fromEntries(
292
+ Object.entries(declaredEnv).filter(([key]) => key !== SERVER_ADDRESS_ENV_KEY),
293
+ )
294
+ : declaredEnv;
308
295
  const envVars = await resolveEnvVarsForDiscovery(
309
296
  stigmerClient,
310
297
  executionContextId ?? null,
311
298
  executionContextToken ?? null,
312
299
  slug,
313
- declaredEnv,
300
+ credentialDeclarations,
314
301
  );
315
302
 
316
303
  const declaredEnvKeys = new Set(Object.keys(declaredEnv));
317
- const platformEnv = injectPlatformEnv(declaredEnvKeys, envVars);
304
+ const platformEnv = fillPlatformServerAddress(mcpServer, envVars, deps.platformEndpoints);
318
305
 
319
306
  // Discovery runs with no session, so every declared caller-identity key
320
307
  // resolves to the anonymous sentinel — without it, a server templating
@@ -385,7 +372,9 @@ export async function discoverMcpServer(
385
372
  * sentinel can only produce a misleading 401.
386
373
  * - No non-optional declarations → the old lenient path (warn and continue):
387
374
  * servers declaring nothing (or only optional/injected keys like the
388
- * caller-identity family) legitimately discover without an EC.
375
+ * caller-identity family) legitimately discover without an EC. A
376
+ * STIGMER_SERVER_ADDRESS the platform fills for the server's transport is
377
+ * left out of the count by the caller (platform-server-address.ts).
389
378
  */
390
379
  async function resolveEnvVarsForDiscovery(
391
380
  client: StigmerClient,
@@ -635,6 +624,7 @@ export function createDiscoverMcpServerActivities(config: Config) {
635
624
  return await discoverMcpServer(input, {
636
625
  stigmerClient,
637
626
  transportPosture: resolveMcpTransportPosture(config.mode),
627
+ platformEndpoints: config,
638
628
  });
639
629
  } finally {
640
630
  hb.stop();
@@ -21,7 +21,7 @@ import { create } from "@bufbuild/protobuf";
21
21
  import type { InteractionUpdate, SDKMessage } from "@cursor/sdk";
22
22
  import { AgentExecutionStatusSchema, type AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
23
23
  import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
24
- import { TranscriptBuilder } from "../../../harness/transcript/builder.js";
24
+ import { TranscriptBuilder, type TranscriptObserver } from "../../../harness/transcript/builder.js";
25
25
  import { CursorTranslator, type CursorTranslatorOptions } from "../translator.js";
26
26
 
27
27
  export interface CursorFoldOptions {
@@ -35,6 +35,8 @@ export interface CursorFoldOptions {
35
35
  readonly messages?: AgentMessage[];
36
36
  readonly policies?: CursorTranslatorOptions["policies"];
37
37
  readonly leases?: CursorTranslatorOptions["leases"];
38
+ /** The builder's observer, as the runtime passes its turn timeline; a test that moves a fake clock between folds passes one. */
39
+ readonly observer?: TranscriptObserver;
38
40
  }
39
41
 
40
42
  export class CursorFold {
@@ -50,12 +52,12 @@ export class CursorFold {
50
52
  leases: options.leases ?? { global: false, categories: new Set() },
51
53
  seeded: this.status.messages,
52
54
  });
53
- this.builder = new TranscriptBuilder("exec-fold", this.status);
55
+ this.builder = new TranscriptBuilder("exec-fold", this.status, options.observer);
54
56
  }
55
57
 
56
- /** One stream event, translated and folded, then the deltas queued so far — the loop's order. */
58
+ /** One stream event, translated and folded as one observation, then the deltas queued so far — the loop's order. */
57
59
  event(event: SDKMessage): this {
58
- for (const e of this.translator.translate(event)) this.builder.apply(e);
60
+ this.builder.applyObservation(this.translator.translate(event));
59
61
  this.drain();
60
62
  return this;
61
63
  }
@@ -148,6 +148,7 @@ export function hermeticCursorConfig(env: HermeticEnvironment): Config {
148
148
  temporalConnection: {},
149
149
  stigmerBackendEndpoint: "http://localhost:7234",
150
150
  mcpBridgeEndpoint: null,
151
+ mcpPublicEndpoint: null,
151
152
  stigmerTokenRef: { current: null },
152
153
  cursorApiKey: FIXTURE.cursorApiKey,
153
154
  workspaceRootDir: env.workspaceRootDir,
@@ -23,8 +23,6 @@ import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecutio
23
23
 
24
24
  import {
25
25
  approvalCategory,
26
- builtInRequiresApproval,
27
- getBuiltInApprovalMessage,
28
26
  getBuiltInGatedList,
29
27
  getBuiltInGatedCategories,
30
28
  extractArgKey,
@@ -81,22 +79,10 @@ describe("approvalCategory (cross-taxonomy drift-guard)", () => {
81
79
  });
82
80
  });
83
81
 
84
- describe("builtInRequiresApproval", () => {
85
- it("gates mutating/destructive tools in BOTH taxonomies", () => {
86
- for (const t of ["Write", "StrReplace", "EditNotebook", "Shell", "Delete", "edit", "shell", "delete", "execute", "write"]) {
87
- expect(builtInRequiresApproval(t)).toBe(true);
88
- }
89
- });
90
-
91
- it("allows read-only built-in tools", () => {
92
- for (const t of ["Read", "read", "Grep", "grep", "Glob", "glob", "ls", "think", "task"]) {
93
- expect(builtInRequiresApproval(t)).toBe(false);
94
- }
95
- });
96
-
97
- it("fails open for unknown tools (parity with native, avoids denying auto-approved MCP)", () => {
98
- expect(builtInRequiresApproval("SomeFutureTool")).toBe(false);
99
- expect(builtInRequiresApproval("search_services")).toBe(false);
82
+ describe("the hook's gated set", () => {
83
+ it("fails open for unknown tools: no category, so the hook allows them (parity with native, avoids denying auto-approved MCP)", () => {
84
+ expect(approvalCategory("SomeFutureTool")).toBeUndefined();
85
+ expect(approvalCategory("search_services")).toBeUndefined();
100
86
  });
101
87
 
102
88
  it("exposes the gated set in the HOOK taxonomy (what the hook matches)", () => {
@@ -114,17 +100,6 @@ describe("builtInRequiresApproval", () => {
114
100
  });
115
101
  });
116
102
 
117
- describe("getBuiltInApprovalMessage", () => {
118
- it("returns a category template for gated tools in EITHER taxonomy", () => {
119
- expect(getBuiltInApprovalMessage("Write")).toContain("{{args.path}}");
120
- expect(getBuiltInApprovalMessage("edit")).toContain("{{args.path}}");
121
- expect(getBuiltInApprovalMessage("Shell")).toContain("{{args.command}}");
122
- expect(getBuiltInApprovalMessage("shell")).toContain("{{args.command}}");
123
- expect(getBuiltInApprovalMessage("Read")).toBeUndefined();
124
- expect(getBuiltInApprovalMessage("read")).toBeUndefined();
125
- });
126
- });
127
-
128
103
  describe("extractArgKey (spans both taxonomies' field names)", () => {
129
104
  it("extracts the salient value regardless of field name (file_path or path)", () => {
130
105
  expect(extractArgKey({ file_path: "a.txt" })).toBe("a.txt"); // hook shape