@stigmer/runner 3.40.0 → 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 (278) 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/prompt-builder.d.ts +70 -35
  34. package/dist/activities/execute-deep-agent/prompt-builder.js +70 -55
  35. package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
  36. package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +20 -3
  37. package/dist/activities/execute-deep-agent/streaming-side-effects.js +26 -4
  38. package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
  39. package/dist/activities/execute-deep-agent/translator.d.ts +5 -4
  40. package/dist/activities/execute-deep-agent/translator.js +5 -4
  41. package/dist/activities/execute-deep-agent/translator.js.map +1 -1
  42. package/dist/activities/execute-deep-agent/turn-settle.js +6 -6
  43. package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
  44. package/dist/activities/execute-deep-agent/turn-setup.d.ts +18 -13
  45. package/dist/activities/execute-deep-agent/turn-setup.js +42 -36
  46. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
  47. package/dist/activities/execute-deep-agent/turn-stream.d.ts +10 -6
  48. package/dist/activities/execute-deep-agent/turn-stream.js +6 -16
  49. package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
  50. package/dist/activities/execute-deep-agent/turn.d.ts +4 -1
  51. package/dist/activities/execute-deep-agent/turn.js +15 -6
  52. package/dist/activities/execute-deep-agent/turn.js.map +1 -1
  53. package/dist/activities/workflow-event-activities.js +2 -0
  54. package/dist/activities/workflow-event-activities.js.map +1 -1
  55. package/dist/config.d.ts +9 -0
  56. package/dist/config.js +2 -0
  57. package/dist/config.js.map +1 -1
  58. package/dist/exit-backstop.d.ts +64 -0
  59. package/dist/exit-backstop.js +79 -0
  60. package/dist/exit-backstop.js.map +1 -0
  61. package/dist/harness/terminal-table.d.ts +12 -5
  62. package/dist/harness/terminal-table.js +13 -13
  63. package/dist/harness/terminal-table.js.map +1 -1
  64. package/dist/harness/transcript/builder.d.ts +21 -0
  65. package/dist/harness/transcript/builder.js +53 -31
  66. package/dist/harness/transcript/builder.js.map +1 -1
  67. package/dist/harness/transcript/events.d.ts +22 -26
  68. package/dist/harness/transcript/events.js +6 -5
  69. package/dist/harness/transcript/events.js.map +1 -1
  70. package/dist/harness/turn-context.d.ts +3 -1
  71. package/dist/harness/turn-context.js +5 -3
  72. package/dist/harness/turn-context.js.map +1 -1
  73. package/dist/main.js +10 -4
  74. package/dist/main.js.map +1 -1
  75. package/dist/middleware/cost-advisory.d.ts +6 -3
  76. package/dist/middleware/cost-advisory.js +6 -3
  77. package/dist/middleware/cost-advisory.js.map +1 -1
  78. package/dist/middleware/execution-budget.d.ts +22 -7
  79. package/dist/middleware/execution-budget.js +57 -35
  80. package/dist/middleware/execution-budget.js.map +1 -1
  81. package/dist/middleware/index.d.ts +10 -7
  82. package/dist/middleware/index.js +10 -7
  83. package/dist/middleware/index.js.map +1 -1
  84. package/dist/middleware/types.d.ts +6 -1
  85. package/dist/runner-manager.js +3 -2
  86. package/dist/runner-manager.js.map +1 -1
  87. package/dist/runner.d.ts +25 -0
  88. package/dist/runner.js +41 -14
  89. package/dist/runner.js.map +1 -1
  90. package/dist/shared/approval-policy.d.ts +23 -13
  91. package/dist/shared/approval-policy.js +47 -14
  92. package/dist/shared/approval-policy.js.map +1 -1
  93. package/dist/shared/caller-identity.d.ts +4 -5
  94. package/dist/shared/caller-identity.js +4 -5
  95. package/dist/shared/caller-identity.js.map +1 -1
  96. package/dist/shared/channel-attachment.js +3 -2
  97. package/dist/shared/channel-attachment.js.map +1 -1
  98. package/dist/shared/connect-backfill.d.ts +2 -1
  99. package/dist/shared/connect-backfill.js +2 -2
  100. package/dist/shared/connect-backfill.js.map +1 -1
  101. package/dist/shared/mcp-resolver.d.ts +10 -1
  102. package/dist/shared/mcp-resolver.js +11 -2
  103. package/dist/shared/mcp-resolver.js.map +1 -1
  104. package/dist/shared/memory-attachment.js +3 -2
  105. package/dist/shared/memory-attachment.js.map +1 -1
  106. package/dist/shared/model-error.d.ts +25 -3
  107. package/dist/shared/model-error.js +78 -10
  108. package/dist/shared/model-error.js.map +1 -1
  109. package/dist/shared/platform-server-address.d.ts +71 -0
  110. package/dist/shared/platform-server-address.js +103 -0
  111. package/dist/shared/platform-server-address.js.map +1 -0
  112. package/dist/shared/prompt-sections.d.ts +6 -28
  113. package/dist/shared/prompt-sections.js +6 -35
  114. package/dist/shared/prompt-sections.js.map +1 -1
  115. package/dist/shared/skill-resolver.d.ts +2 -3
  116. package/dist/shared/skill-resolver.js +2 -3
  117. package/dist/shared/skill-resolver.js.map +1 -1
  118. package/dist/shared/synthesized-attachment.d.ts +0 -5
  119. package/dist/shared/synthesized-attachment.js +0 -12
  120. package/dist/shared/synthesized-attachment.js.map +1 -1
  121. package/dist/shared/tool-rounds.d.ts +51 -23
  122. package/dist/shared/tool-rounds.js +67 -24
  123. package/dist/shared/tool-rounds.js.map +1 -1
  124. package/dist/shared/workspace/platform-dir.d.ts +15 -0
  125. package/dist/shared/workspace/platform-dir.js +18 -1
  126. package/dist/shared/workspace/platform-dir.js.map +1 -1
  127. package/dist/shared/workspace/sources/git.js +13 -6
  128. package/dist/shared/workspace/sources/git.js.map +1 -1
  129. package/dist/shared/workspace/stigmer-link.d.ts +21 -14
  130. package/dist/shared/workspace/stigmer-link.js +91 -29
  131. package/dist/shared/workspace/stigmer-link.js.map +1 -1
  132. package/dist/tools/url-guard.d.ts +5 -1
  133. package/dist/tools/url-guard.js +9 -2
  134. package/dist/tools/url-guard.js.map +1 -1
  135. package/dist/worker.d.ts +11 -2
  136. package/dist/worker.js +1 -1
  137. package/dist/worker.js.map +1 -1
  138. package/dist/workflow-engine/tasks/human-input.d.ts +16 -0
  139. package/dist/workflow-engine/tasks/human-input.js +79 -37
  140. package/dist/workflow-engine/tasks/human-input.js.map +1 -1
  141. package/dist/workflow-engine/types.d.ts +14 -4
  142. package/dist/workflow-engine/types.js.map +1 -1
  143. package/dist/workflows/call-agent-orchestrator.d.ts +13 -0
  144. package/dist/workflows/call-agent-orchestrator.js.map +1 -1
  145. package/dist/workflows/engine-core.d.ts +25 -0
  146. package/dist/workflows/engine-core.js.map +1 -1
  147. package/dist/workflows/execute-from-execution.d.ts +3 -0
  148. package/dist/workflows/execute-from-execution.js.map +1 -1
  149. package/dist/workflows/human-input-orchestrator.d.ts +9 -12
  150. package/dist/workflows/human-input-orchestrator.js +12 -44
  151. package/dist/workflows/human-input-orchestrator.js.map +1 -1
  152. package/package.json +5 -5
  153. package/src/__test-utils__/config-fixture.ts +1 -0
  154. package/src/__test-utils__/execution-record-fixture.ts +2 -1
  155. package/src/__test-utils__/harness-contract/runtime-contract.ts +5 -3
  156. package/src/__test-utils__/test-gate.ts +8 -0
  157. package/src/__tests__/exit-backstop.test.ts +91 -0
  158. package/src/__tests__/golden-e2e.test.ts +80 -62
  159. package/src/__tests__/history-encryption-e2e.test.ts +51 -27
  160. package/src/__tests__/runner.test.ts +60 -2
  161. package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
  162. package/src/activities/__tests__/discover-mcp-server.hang.test.ts +5 -1
  163. package/src/activities/__tests__/discover-mcp-server.test.ts +90 -49
  164. package/src/activities/__tests__/workflow-event-activities.test.ts +28 -7
  165. package/src/activities/call-agent.ts +1 -1
  166. package/src/activities/discover-mcp-server.ts +27 -37
  167. package/src/activities/execute-cursor/__test-utils__/fold.ts +6 -4
  168. package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +1 -0
  169. package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +4 -29
  170. package/src/activities/execute-cursor/__tests__/hermetic/bypassed-tool-call.test.ts +120 -0
  171. package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -5
  172. package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +0 -2
  173. package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
  174. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +0 -3
  175. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +0 -2
  176. package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +0 -2
  177. package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +0 -2
  178. package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +0 -3
  179. package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +9 -7
  180. package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +6 -1
  181. package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +8 -11
  182. package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +1 -3
  183. package/src/activities/execute-cursor/__tests__/translator.test.ts +68 -15
  184. package/src/activities/execute-cursor/approval-policy.ts +11 -36
  185. package/src/activities/execute-cursor/boundary-rows.ts +3 -3
  186. package/src/activities/execute-cursor/translator.ts +15 -32
  187. package/src/activities/execute-cursor/turn-stream.ts +4 -1
  188. package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +33 -2
  189. package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +1 -0
  190. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +1 -27
  191. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +26 -30
  192. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +1 -26
  193. package/src/activities/execute-deep-agent/__tests__/goldens/user-message.everything.md +36 -0
  194. package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +4 -0
  195. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +1 -1
  196. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +1 -1
  197. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +1 -1
  198. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +8 -73
  199. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
  200. package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +17 -0
  201. package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +86 -26
  202. package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +9 -5
  203. package/src/activities/execute-deep-agent/__tests__/hermetic/thrown-turn-drains-publishes.test.ts +159 -0
  204. package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +84 -0
  205. package/src/activities/execute-deep-agent/__tests__/platform-route.test.ts +47 -10
  206. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +127 -199
  207. package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +31 -26
  208. package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +34 -0
  209. package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +13 -5
  210. package/src/activities/execute-deep-agent/inline-publisher.ts +47 -7
  211. package/src/activities/execute-deep-agent/platform-route.ts +109 -20
  212. package/src/activities/execute-deep-agent/prompt-builder.ts +104 -92
  213. package/src/activities/execute-deep-agent/streaming-side-effects.ts +27 -4
  214. package/src/activities/execute-deep-agent/translator.ts +5 -4
  215. package/src/activities/execute-deep-agent/turn-settle.ts +6 -6
  216. package/src/activities/execute-deep-agent/turn-setup.ts +43 -38
  217. package/src/activities/execute-deep-agent/turn-stream.ts +15 -22
  218. package/src/activities/execute-deep-agent/turn.ts +16 -7
  219. package/src/activities/workflow-event-activities.ts +2 -0
  220. package/src/config.ts +11 -0
  221. package/src/exit-backstop.ts +97 -0
  222. package/src/harness/README.md +5 -5
  223. package/src/harness/terminal-table.ts +13 -13
  224. package/src/harness/transcript/__tests__/builder.test.ts +93 -21
  225. package/src/harness/transcript/builder.ts +53 -30
  226. package/src/harness/transcript/events.ts +22 -27
  227. package/src/harness/turn-context.ts +5 -3
  228. package/src/main.ts +9 -5
  229. package/src/middleware/__tests__/execution-budget.test.ts +181 -103
  230. package/src/middleware/cost-advisory.ts +6 -3
  231. package/src/middleware/execution-budget.ts +60 -34
  232. package/src/middleware/index.ts +10 -7
  233. package/src/middleware/types.ts +6 -1
  234. package/src/runner-manager.ts +3 -2
  235. package/src/runner.ts +57 -15
  236. package/src/shared/__tests__/approval-policy.test.ts +41 -0
  237. package/src/shared/__tests__/artifact-storage.test.ts +1 -0
  238. package/src/shared/__tests__/connect-backfill.test.ts +15 -11
  239. package/src/shared/__tests__/mcp-resolver.test.ts +72 -10
  240. package/src/shared/__tests__/model-error.test.ts +71 -3
  241. package/src/shared/__tests__/platform-server-address.test.ts +122 -0
  242. package/src/shared/__tests__/prompt-sections.test.ts +2 -54
  243. package/src/shared/__tests__/tool-rounds.test.ts +64 -14
  244. package/src/shared/approval-policy.ts +57 -14
  245. package/src/shared/caller-identity.ts +4 -5
  246. package/src/shared/channel-attachment.ts +2 -2
  247. package/src/shared/connect-backfill.ts +5 -1
  248. package/src/shared/mcp-resolver.ts +11 -1
  249. package/src/shared/memory-attachment.ts +2 -2
  250. package/src/shared/model-error.ts +87 -9
  251. package/src/shared/platform-server-address.ts +123 -0
  252. package/src/shared/prompt-sections.ts +6 -55
  253. package/src/shared/skill-resolver.ts +2 -3
  254. package/src/shared/synthesized-attachment.ts +0 -12
  255. package/src/shared/tool-rounds.ts +70 -24
  256. package/src/shared/workspace/__tests__/git-source.test.ts +11 -5
  257. package/src/shared/workspace/__tests__/stigmer-link.test.ts +107 -10
  258. package/src/shared/workspace/platform-dir.ts +19 -1
  259. package/src/shared/workspace/sources/git.ts +14 -8
  260. package/src/shared/workspace/stigmer-link.ts +99 -29
  261. package/src/tools/__tests__/url-guard.test.ts +19 -1
  262. package/src/tools/url-guard.ts +9 -1
  263. package/src/worker.ts +12 -2
  264. package/src/workflow-engine/__tests__/golden-execution.test.ts +2 -1
  265. package/src/workflow-engine/__tests__/loader.test.ts +1 -1
  266. package/src/workflow-engine/__tests__/tasks/human-input.test.ts +133 -57
  267. package/src/workflow-engine/tasks/human-input.ts +92 -42
  268. package/src/workflow-engine/types.ts +16 -4
  269. package/src/workflows/call-agent-orchestrator.ts +7 -0
  270. package/src/workflows/engine-core.ts +18 -0
  271. package/src/workflows/execute-from-execution.ts +2 -1
  272. package/src/workflows/human-input-orchestrator.ts +15 -56
  273. package/dist/shared/skill-relevance.d.ts +0 -65
  274. package/dist/shared/skill-relevance.js +0 -175
  275. package/dist/shared/skill-relevance.js.map +0 -1
  276. package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +0 -146
  277. package/src/shared/__tests__/skill-relevance.test.ts +0 -292
  278. package/src/shared/skill-relevance.ts +0 -222
@@ -15,8 +15,8 @@
15
15
  * converter has no Void special-case — a data-bearing encrypted result
16
16
  * would fail its converter lookup.
17
17
  *
18
- * Follows the golden-e2e pattern: tests skip gracefully when the
19
- * Temporal test server cannot start.
18
+ * Follows the golden-e2e pattern: the test skips by name only when the
19
+ * Temporal dev server cannot boot; any later failure fails it.
20
20
  */
21
21
 
22
22
  import { describe, it, expect, beforeAll, afterAll } from "vitest";
@@ -24,9 +24,11 @@ import { randomBytes } from "node:crypto";
24
24
  import { join, dirname } from "node:path";
25
25
  import { fileURLToPath } from "node:url";
26
26
  import { loadWorkflowFromYaml } from "../workflow-engine/loader.js";
27
+ import { IN_TEST_GATE } from "../__test-utils__/test-gate.js";
27
28
  import { evaluateExpressionBatch } from "../workflow-engine/expression.js";
28
29
  import { EncryptionPayloadCodec } from "@stigmer/temporal-codecs";
29
- import type { ExecuteServerlessWorkflowInput } from "../workflows/engine-core.js";
30
+ import type { EngineActivities, ExecuteServerlessWorkflowInput } from "../workflows/engine-core.js";
31
+ import type { HydrateActivities } from "../workflows/execute-from-execution.js";
30
32
 
31
33
  const __dirname = dirname(fileURLToPath(import.meta.url));
32
34
  const WORKFLOWS_PATH = join(__dirname, "../workflows/index.ts");
@@ -68,7 +70,8 @@ type Worker = import("@temporalio/worker").Worker;
68
70
  let env: TestWorkflowEnvironment | null = null;
69
71
  let worker: Worker | null = null;
70
72
  let workerRunPromise: Promise<void> | null = null;
71
- let envReady = false;
73
+ /** Why the Temporal dev server could not boot; `null` once it has. */
74
+ let bootError: string | null = "not attempted";
72
75
 
73
76
  function createMockActivities() {
74
77
  return {
@@ -90,6 +93,23 @@ function createMockActivities() {
90
93
  EmitWorkflowEvents: async (): Promise<void> => {},
91
94
  LoadRecoveryContext: async (): Promise<unknown[]> => [],
92
95
  PromoteTaskOutput: async (): Promise<void> => {},
96
+ CallGrpc: unscheduled("CallGrpc"),
97
+ CallFunction: unscheduled("CallFunction"),
98
+ RunScript: unscheduled("RunScript"),
99
+ RunShell: unscheduled("RunShell"),
100
+ CallAgent: unscheduled("CallAgent"),
101
+ UpdateWorkflowTaskApprovalStatus: unscheduled("UpdateWorkflowTaskApprovalStatus"),
102
+ ClearWorkflowApprovalStatus: unscheduled("ClearWorkflowApprovalStatus"),
103
+ UpdateWorkflowFileReviewStatus: unscheduled("UpdateWorkflowFileReviewStatus"),
104
+ GetAwaitingFileReviewChangeSetIds: unscheduled("GetAwaitingFileReviewChangeSetIds"),
105
+ GetAgentExecutionProgress: unscheduled("GetAgentExecutionProgress"),
106
+ } satisfies Record<keyof (EngineActivities & HydrateActivities), unknown>;
107
+ }
108
+
109
+ /** An activity the secret-bearing workflow never schedules; reaching it is a test bug. */
110
+ function unscheduled(name: string): () => Promise<never> {
111
+ return async () => {
112
+ throw new Error(`${name} is not scheduled by the secret-bearing workflow`);
93
113
  };
94
114
  }
95
115
 
@@ -110,31 +130,35 @@ function collectByteFields(value: unknown, out: Uint8Array[]): void {
110
130
 
111
131
  describe("History encryption tripwire — Temporal TestWorkflowEnvironment", () => {
112
132
  beforeAll(async () => {
113
- try {
114
- const { TestWorkflowEnvironment: TWE } = await import("@temporalio/testing");
115
- const { Worker: W } = await import("@temporalio/worker");
133
+ const { TestWorkflowEnvironment: TWE } = await import("@temporalio/testing");
134
+ const { Worker: W } = await import("@temporalio/worker");
116
135
 
136
+ try {
117
137
  env = await TWE.createLocal();
118
- worker = await W.create({
119
- connection: env.nativeConnection,
120
- taskQueue: TASK_QUEUE,
121
- workflowsPath: WORKFLOWS_PATH,
122
- activities: createMockActivities(),
123
- dataConverter: {
124
- payloadCodecs: [
125
- new EncryptionPayloadCodec({
126
- primary: { keyId: "tripwire-key", key: randomBytes(32) },
127
- }),
128
- ],
129
- },
130
- });
131
- workerRunPromise = worker.run();
132
- envReady = true;
133
138
  } catch (err: unknown) {
134
- const msg = err instanceof Error ? err.message : String(err);
135
- console.warn(`Temporal test server unavailable (tests will be skipped): ${msg}`);
136
- envReady = false;
139
+ // Inside a gate the test server is a provided dependency: not booting is a failure.
140
+ if (IN_TEST_GATE) throw err;
141
+ bootError = err instanceof Error ? err.message : String(err);
142
+ console.warn(`Temporal dev server cannot boot; the tripwire skips: ${bootError}`);
143
+ return;
137
144
  }
145
+ bootError = null;
146
+
147
+ // Past the boot, a failure is the suite's to report, not to skip.
148
+ worker = await W.create({
149
+ connection: env.nativeConnection,
150
+ taskQueue: TASK_QUEUE,
151
+ workflowsPath: WORKFLOWS_PATH,
152
+ activities: createMockActivities(),
153
+ dataConverter: {
154
+ payloadCodecs: [
155
+ new EncryptionPayloadCodec({
156
+ primary: { keyId: "tripwire-key", key: randomBytes(32) },
157
+ }),
158
+ ],
159
+ },
160
+ });
161
+ workerRunPromise = worker.run();
138
162
  }, 60_000);
139
163
 
140
164
  afterAll(async () => {
@@ -145,8 +169,8 @@ describe("History encryption tripwire — Temporal TestWorkflowEnvironment", ()
145
169
  if (env) await env.teardown();
146
170
  }, 30_000);
147
171
 
148
- it("records no plaintext secret bytes anywhere in workflow history", async () => {
149
- if (!envReady || !env) return;
172
+ it("records no plaintext secret bytes anywhere in workflow history", async (testCtx) => {
173
+ if (bootError !== null || !env) return testCtx.skip(`Temporal dev server cannot boot: ${bootError}`);
150
174
 
151
175
  const workflowId = `tripwire-${Date.now()}`;
152
176
  // The starting client has NO codec, mirroring the Java/Go
@@ -1,5 +1,5 @@
1
- import { describe, it, expect } from "vitest";
2
- import { mapOptionsToConfig } from "../runner.js";
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mapOptionsToConfig, releaseAfterDrain } from "../runner.js";
3
3
  import type { StigmerRunnerOptions, StigmerRunner } from "../runner.js";
4
4
  import type { TokenRef } from "../config.js";
5
5
 
@@ -224,3 +224,61 @@ describe("public API exports", () => {
224
224
  expect(typeof mod.createStigmerRunner).toBe("function");
225
225
  });
226
226
  });
227
+
228
+ describe("releaseAfterDrain", () => {
229
+ function resources(opts: { harnessFails?: boolean; closeFails?: boolean } = {}) {
230
+ const order: string[] = [];
231
+ return {
232
+ order,
233
+ value: {
234
+ shutdownHarnesses: vi.fn(async () => {
235
+ order.push("harnesses");
236
+ if (opts.harnessFails) {
237
+ throw new Error("harness boom");
238
+ }
239
+ }),
240
+ connection: {
241
+ close: vi.fn(async () => {
242
+ order.push("connection");
243
+ if (opts.closeFails) {
244
+ throw new Error("close boom");
245
+ }
246
+ }),
247
+ },
248
+ },
249
+ };
250
+ }
251
+
252
+ it("releases the harnesses, then closes the Temporal connection", async () => {
253
+ const r = resources();
254
+ const logError = vi.fn<(line: string) => void>();
255
+
256
+ await releaseAfterDrain(r.value, logError);
257
+
258
+ expect(r.order).toEqual(["harnesses", "connection"]);
259
+ expect(logError).not.toHaveBeenCalled();
260
+ });
261
+
262
+ it("still closes the connection when a harness teardown fails, and logs it", async () => {
263
+ const r = resources({ harnessFails: true });
264
+ const logError = vi.fn<(line: string) => void>();
265
+
266
+ await releaseAfterDrain(r.value, logError);
267
+
268
+ expect(r.order).toEqual(["harnesses", "connection"]);
269
+ expect(logError).toHaveBeenCalledTimes(1);
270
+ expect(logError.mock.calls[0]?.[0]).toContain("Harness shutdown failed after the worker drained");
271
+ expect(logError.mock.calls[0]?.[0]).toContain("harness boom");
272
+ });
273
+
274
+ it("logs a failed close instead of throwing, so a clean stop stays clean", async () => {
275
+ const r = resources({ closeFails: true });
276
+ const logError = vi.fn<(line: string) => void>();
277
+
278
+ await expect(releaseAfterDrain(r.value, logError)).resolves.toBeUndefined();
279
+
280
+ expect(logError).toHaveBeenCalledTimes(1);
281
+ expect(logError.mock.calls[0]?.[0]).toContain("Temporal connection close failed after the worker drained");
282
+ expect(logError.mock.calls[0]?.[0]).toContain("close boom");
283
+ });
284
+ });
@@ -727,6 +727,7 @@ function makeConfig() {
727
727
  temporalConnection: {},
728
728
  stigmerBackendEndpoint: "http://localhost:7234",
729
729
  mcpBridgeEndpoint: null,
730
+ mcpPublicEndpoint: null,
730
731
  stigmerTokenRef: { current: "test-token" },
731
732
  cursorApiKey: "",
732
733
  workspaceRootDir: "/tmp/test",
@@ -14,6 +14,10 @@
14
14
  */
15
15
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
16
16
  import { createServer, type Server } from "node:http";
17
+ import { testConfig } from "../../__test-utils__/config-fixture.js";
18
+
19
+ /** The runner endpoints discovery fills STIGMER_SERVER_ADDRESS from. */
20
+ const PLATFORM_ENDPOINTS = testConfig();
17
21
 
18
22
  vi.mock("../../idle-watchdog.js", () => ({
19
23
  activityStarted: vi.fn(),
@@ -85,7 +89,7 @@ describe("discovery against a 4xx-then-silent-SSE endpoint (issue #239)", () =>
85
89
 
86
90
  const promise = discoverMcpServer(
87
91
  { mcpServerId: "mcp-monday" },
88
- { stigmerClient: stigmerClient as never, transportPosture: "stdio-forbidden" },
92
+ { stigmerClient: stigmerClient as never, transportPosture: "stdio-forbidden", platformEndpoints: PLATFORM_ENDPOINTS },
89
93
  );
90
94
  // Attach the rejection expectation BEFORE advancing so the rejection is
91
95
  // never momentarily unhandled.
@@ -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.