@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
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
6
6
  import { create } from "@bufbuild/protobuf";
7
7
  import { type AgentExecutionStatus, AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
8
8
  import { ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
9
+ import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
9
10
  import { InlinePublisher } from "../inline-publisher.js";
10
11
  import { TranscriptBuilder } from "../../../harness/transcript/builder.js";
11
12
  import { LocalWorkspaceBackend } from "../../../shared/workspace/local-backend.js";
@@ -71,6 +72,7 @@ describe("InlinePublisher", () => {
71
72
  workspaceBackend: backend,
72
73
  artifactStorage: storage,
73
74
  artifacts: sb,
75
+ record: status,
74
76
  executionId: "exec-123",
75
77
  });
76
78
  });
@@ -97,6 +99,25 @@ describe("InlinePublisher", () => {
97
99
  expect(status.artifacts[0].sandboxPath).toBe("src/main.ts");
98
100
  });
99
101
 
102
+ it("stamps createdAt when the publish is asked for, never when the upload returns", async () => {
103
+ // The stream asks for the publish as it folds the write's finish, and the
104
+ // upload runs on beside the stream; a slow store must not move the stamp.
105
+ vi.useFakeTimers({ toFake: ["Date"], now: Date.UTC(2026, 0, 1) });
106
+ try {
107
+ const askedAt = new Date().toISOString();
108
+ vi.mocked(storage.upload).mockImplementationOnce(async (key: string) => {
109
+ vi.setSystemTime(Date.now() + 5_000);
110
+ return key;
111
+ });
112
+
113
+ await publisher.publish("src/main.ts");
114
+
115
+ expect(status.artifacts[0].createdAt).toBe(askedAt);
116
+ } finally {
117
+ vi.useRealTimers();
118
+ }
119
+ });
120
+
100
121
  it("deduplicates by path + content hash", async () => {
101
122
  await publisher.publish("src/main.ts");
102
123
  await publisher.publish("src/main.ts");
@@ -124,6 +145,64 @@ describe("InlinePublisher", () => {
124
145
  expect(publisher.publishedPaths.has("src/main.ts")).toBe(true);
125
146
  });
126
147
 
148
+ // A reinvocation seeds the earlier turns' rows onto the status before the
149
+ // turn's publisher exists; the safety net then asks for those paths again.
150
+ describe("against rows the status already lists (stigmer/stigmer#1448)", () => {
151
+ function seedRow(sandboxPath: string, content: string): void {
152
+ status.artifacts.push(create(ExecutionArtifactSchema, {
153
+ name: sandboxPath.split("/").at(-1) ?? sandboxPath,
154
+ sandboxPath,
155
+ kind: ExecutionArtifactKind.FILE,
156
+ storageKey: `artifacts/exec-123/${sandboxPath.split("/").at(-1)}`,
157
+ contentHash: sha256(content),
158
+ }));
159
+ }
160
+
161
+ it("uploads nothing for a file whose bytes the status already lists, and counts it as brought up to date", async () => {
162
+ seedRow("src/main.ts", "console.log('hello');");
163
+ const seeded = status.artifacts[0];
164
+
165
+ await publisher.publish("src/main.ts");
166
+
167
+ expect(storage.uploadedKeys, "the unchanged file is not sent again").toEqual([]);
168
+ expect(status.artifacts, "the seeded row is kept as it was").toEqual([seeded]);
169
+ expect(publisher.publishedPaths.has("src/main.ts")).toBe(true);
170
+ });
171
+
172
+ it("uploads and replaces the row when the file changed since the status listed it", async () => {
173
+ seedRow("src/main.ts", "an earlier turn's bytes");
174
+
175
+ await publisher.publish("src/main.ts");
176
+
177
+ expect(storage.uploadedKeys).toEqual(["artifacts/exec-123/main.ts"]);
178
+ expect(status.artifacts).toHaveLength(1);
179
+ expect(status.artifacts[0].contentHash).toBe(sha256("console.log('hello');"));
180
+ });
181
+
182
+ it("still reads the file, so a rewrite made outside a write tool is caught", async () => {
183
+ seedRow("src/main.ts", "console.log('hello');");
184
+
185
+ await publisher.publish("src/main.ts");
186
+
187
+ expect(backend.readFile).toHaveBeenCalledWith("src/main.ts");
188
+ });
189
+
190
+ it("leaves an earlier turn's path out of publishedPaths until this turn checks it", () => {
191
+ seedRow("src/main.ts", "console.log('hello');");
192
+
193
+ expect(publisher.publishedPaths.size).toBe(0);
194
+ });
195
+
196
+ it("uploads a file the status lists under another path", async () => {
197
+ seedRow("docs/main.ts", "console.log('hello');");
198
+
199
+ await publisher.publish("src/main.ts");
200
+
201
+ expect(storage.uploadedKeys).toEqual(["artifacts/exec-123/main.ts"]);
202
+ expect(status.artifacts.map((a) => a.sandboxPath)).toEqual(["docs/main.ts", "src/main.ts"]);
203
+ });
204
+ });
205
+
127
206
  it("swallows errors without throwing (fire-and-forget)", async () => {
128
207
  const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
129
208
  const failBackend = mockWorkspaceBackend({});
@@ -132,6 +211,7 @@ describe("InlinePublisher", () => {
132
211
  workspaceBackend: failBackend,
133
212
  artifactStorage: storage,
134
213
  artifacts: sb,
214
+ record: status,
135
215
  executionId: "exec-err",
136
216
  });
137
217
 
@@ -176,6 +256,7 @@ describe("InlinePublisher", () => {
176
256
  workspaceBackend: secretBackend,
177
257
  artifactStorage: storage,
178
258
  artifacts: sb,
259
+ record: status,
179
260
  executionId: "exec-secret",
180
261
  });
181
262
 
@@ -193,6 +274,7 @@ describe("InlinePublisher", () => {
193
274
  workspaceBackend: jsonBackend,
194
275
  artifactStorage: storage,
195
276
  artifacts: sb,
277
+ record: status,
196
278
  executionId: "exec-ct",
197
279
  });
198
280
 
@@ -221,6 +303,7 @@ describe("InlinePublisher with LocalWorkspaceBackend (disk-backed)", () => {
221
303
  workspaceBackend: backend,
222
304
  artifactStorage: storage,
223
305
  artifacts: sb,
306
+ record: status,
224
307
  executionId: "exec-disk",
225
308
  });
226
309
 
@@ -250,6 +333,7 @@ describe("InlinePublisher with LocalWorkspaceBackend (disk-backed)", () => {
250
333
  workspaceBackend: backend,
251
334
  artifactStorage: storage,
252
335
  artifacts: sb,
336
+ record: status,
253
337
  executionId: "exec-miss",
254
338
  });
255
339
 
@@ -3,16 +3,20 @@
3
3
  // builds (`createCasCaptureBackend` + `mountPlatformRoute`, parent and
4
4
  // sub-agents alike). The stock virtual-rooted backend refuses to follow the
5
5
  // workspace link out of the root; the route mounts the platform dir instead,
6
- // read-only, on the turns the link exists. What is pinned:
6
+ // read-only, on the turns the link exists, and an empty route on every other
7
+ // turn. What is pinned:
7
8
  // - the stock backend's refusal (why the route exists);
8
9
  // - reads, listings and search through the route, for the shell-capable and
9
10
  // the plan-mode backend, with `execute` kept exactly where it was;
10
11
  // - every mutation through the route refused, and a root delete refused
11
12
  // before it touches the workspace or the platform dir;
12
- // - no route on a turn without the link.
13
+ // - on a turn without the link, `.stigmer/` still the platform's: nothing
14
+ // readable, every write refused and nothing created in the workspace (the
15
+ // write that used to make a real directory there, #1123), and the root
16
+ // listing without it.
13
17
 
14
18
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
15
- import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
19
+ import { lstat, mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
16
20
  import { tmpdir } from "node:os";
17
21
  import { join } from "node:path";
18
22
  import { FilesystemBackend, isSandboxBackend } from "deepagents";
@@ -120,23 +124,56 @@ describe("platform route", () => {
120
124
  });
121
125
  });
122
126
 
123
- it("mounts nothing on a turn without the link", async () => {
124
- const workspace = await createCasCaptureBackend({ rootDir: workspaceDir, observer: observer(), shellEnv: {} });
125
- expect(await mountPlatformRoute(workspace, { workspaceDir, platformDir })).toBe(workspace);
126
- expect(await mountPlatformRoute(workspace, { workspaceDir, platformDir: undefined })).toBe(workspace);
127
+ describe.each(["shell", "plan"] as const)("the %s backend on a turn without the link", (mode) => {
128
+ it("refuses every write under .stigmer/ and creates nothing in the workspace", async () => {
129
+ const backend = v2(await productionBackend(mode));
130
+ expect(backend).toBeInstanceOf(PlatformRoutedBackend);
131
+ expect((await backend.write(`${PLATFORM_ROUTE_PREFIX}notes/draft.md`, "x")).error).toMatch(/read-only/);
132
+ expect((await backend.edit(`/${SKILL}`, "My Skill", "Your Skill")).error).toMatch(/read-only/);
133
+ const uploads = (await backend.uploadFiles!([[`${PLATFORM_ROUTE_PREFIX}up.bin`, new Uint8Array([1])]])) ?? [];
134
+ expect(uploads.map((u) => u.error)).toEqual(["permission_denied"]);
135
+ await expect(lstat(join(workspaceDir, ".stigmer")), "no real directory in the user's tree").rejects.toMatchObject({
136
+ code: "ENOENT",
137
+ });
138
+ });
139
+
140
+ it("exposes nothing of the platform dir, which still holds earlier turns' content", async () => {
141
+ const backend = v2(await productionBackend(mode));
142
+ expect((await backend.read(`/${SKILL}`)).error).toMatch(/no platform content/);
143
+ expect((await backend.ls(PLATFORM_ROUTE_PREFIX)).error).toMatch(/no platform content/);
144
+ const found = (await backend.glob("**/SKILL.md", "/")).files ?? [];
145
+ expect(found).toEqual([]);
146
+ });
147
+
148
+ it("lists the workspace at the root without .stigmer/", async () => {
149
+ const backend = v2(await productionBackend(mode));
150
+ const root = ((await backend.ls("/")).files ?? []).map((f) => f.path);
151
+ expect(root).toContain("/main.go");
152
+ expect(root).not.toContain(PLATFORM_ROUTE_PREFIX);
153
+ });
154
+
155
+ it("keeps `execute` exactly where the workspace backend had it", async () => {
156
+ const backend = await productionBackend(mode);
157
+ expect(isSandboxBackend(backend)).toBe(mode === "shell");
158
+ });
127
159
  });
128
160
 
129
- it("mounts nothing when the link points somewhere else", async () => {
161
+ it("exposes nothing when the link points somewhere else", async () => {
130
162
  const other = await mkdtemp(join(tmpdir(), "platform-route-other-"));
131
163
  try {
132
164
  await ensureStigmerSymlink(workspaceDir, other);
133
- const workspace = await createCasCaptureBackend({ rootDir: workspaceDir, observer: observer() });
134
- expect(await mountPlatformRoute(workspace, { workspaceDir, platformDir })).toBe(workspace);
165
+ const backend = v2(await productionBackend("plan"));
166
+ expect((await backend.read(`/${SKILL}`)).error).toMatch(/no platform content/);
135
167
  } finally {
136
168
  await rm(other, { recursive: true, force: true });
137
169
  }
138
170
  });
139
171
 
172
+ it("mounts nothing for a session without a platform dir", async () => {
173
+ const workspace = await createCasCaptureBackend({ rootDir: workspaceDir, observer: observer(), shellEnv: {} });
174
+ expect(await mountPlatformRoute(workspace, { workspaceDir, platformDir: undefined })).toBe(workspace);
175
+ });
176
+
140
177
  it("a workspace entry of its own is untouched by the route", async () => {
141
178
  await mkdir(join(workspaceDir, "src"));
142
179
  await writeFile(join(workspaceDir, "src/a.ts"), "export {};\n");