@stigmer/runner 3.0.9-dev.20260615153829 → 3.1.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.
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/approval-contract/types.d.ts +174 -0
- package/dist/__test-utils__/approval-contract/types.js +24 -0
- package/dist/__test-utils__/approval-contract/types.js.map +1 -0
- package/dist/activities/call-agent-status.d.ts +19 -1
- package/dist/activities/call-agent-status.js +64 -5
- package/dist/activities/call-agent-status.js.map +1 -1
- package/dist/activities/call-llm.js +19 -53
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +24 -0
- package/dist/activities/classify-tool-approvals.js +69 -17
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -0
- package/dist/activities/discover-mcp-server.js +11 -1
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +140 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +130 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.d.ts +18 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +123 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-cursor/approval-policy.d.ts +17 -61
- package/dist/activities/execute-cursor/approval-policy.js +8 -120
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +219 -9
- package/dist/activities/execute-cursor/approval-state.js +233 -17
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +127 -0
- package/dist/activities/execute-cursor/capture-flow.js +234 -0
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -0
- package/dist/activities/execute-cursor/cas-observations.d.ts +96 -0
- package/dist/activities/execute-cursor/cas-observations.js +184 -0
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -0
- package/dist/activities/execute-cursor/command-provenance.d.ts +62 -0
- package/dist/activities/execute-cursor/command-provenance.js +128 -0
- package/dist/activities/execute-cursor/command-provenance.js.map +1 -0
- package/dist/activities/execute-cursor/exact-apply.d.ts +110 -0
- package/dist/activities/execute-cursor/exact-apply.js +204 -0
- package/dist/activities/execute-cursor/exact-apply.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +53 -24
- package/dist/activities/execute-cursor/hook-script.js +310 -47
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +8 -1
- package/dist/activities/execute-cursor/index.js +500 -64
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +258 -11
- package/dist/activities/execute-cursor/message-translator.js +836 -90
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +20 -23
- package/dist/activities/execute-cursor/prompt-builder.js +74 -15
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/workspace-setup.d.ts +17 -0
- package/dist/activities/execute-cursor/workspace-setup.js +212 -33
- package/dist/activities/execute-cursor/workspace-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.d.ts +19 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js +143 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +88 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +81 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -0
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +47 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +68 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -0
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +8 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +7 -7
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +42 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +47 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +79 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +112 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -0
- package/dist/activities/execute-deep-agent/hitl.d.ts +16 -3
- package/dist/activities/execute-deep-agent/hitl.js +11 -3
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.d.ts +2 -1
- package/dist/activities/execute-deep-agent/index.js +393 -33
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +7 -1
- package/dist/activities/execute-deep-agent/inline-publisher.js +23 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +53 -2
- package/dist/activities/execute-deep-agent/setup.js +149 -92
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +36 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +56 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +1 -0
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +34 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.js +26 -25
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +17 -5
- package/dist/activities/execute-deep-agent/status-builder.js +44 -3
- package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +2 -19
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming.js +3 -15
- package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js +23 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +30 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js +29 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js +32 -2
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +9 -5
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +2 -0
- package/dist/client/stigmer-client.js +2 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +85 -4
- package/dist/middleware/approval-gate.js +165 -38
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/types.d.ts +2 -5
- package/dist/shared/activity-input.d.ts +43 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.js.map +1 -0
- package/dist/shared/approval-canonicalize.d.ts +19 -0
- package/dist/shared/approval-canonicalize.js +119 -0
- package/dist/shared/approval-canonicalize.js.map +1 -0
- package/dist/shared/approval-fingerprint.d.ts +106 -0
- package/dist/shared/approval-fingerprint.js +113 -0
- package/dist/shared/approval-fingerprint.js.map +1 -0
- package/dist/shared/approval-policy.d.ts +182 -12
- package/dist/shared/approval-policy.js +213 -27
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +52 -0
- package/dist/shared/args-preview.js +93 -0
- package/dist/shared/args-preview.js.map +1 -0
- package/dist/shared/artifact-storage.d.ts +19 -1
- package/dist/shared/artifact-storage.js +48 -11
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/file-change.d.ts +44 -0
- package/dist/shared/file-change.js +57 -0
- package/dist/shared/file-change.js.map +1 -0
- package/dist/shared/file-tools.d.ts +107 -0
- package/dist/shared/file-tools.js +168 -0
- package/dist/shared/file-tools.js.map +1 -0
- package/dist/shared/filereview/capture.d.ts +202 -0
- package/dist/shared/filereview/capture.js +498 -0
- package/dist/shared/filereview/capture.js.map +1 -0
- package/dist/shared/filereview/cas-substrate.d.ts +190 -0
- package/dist/shared/filereview/cas-substrate.js +284 -0
- package/dist/shared/filereview/cas-substrate.js.map +1 -0
- package/dist/shared/filereview/digest.d.ts +40 -0
- package/dist/shared/filereview/digest.js +66 -0
- package/dist/shared/filereview/digest.js.map +1 -0
- package/dist/shared/filereview/events.d.ts +170 -0
- package/dist/shared/filereview/events.js +298 -0
- package/dist/shared/filereview/events.js.map +1 -0
- package/dist/shared/filereview/git-substrate.d.ts +175 -0
- package/dist/shared/filereview/git-substrate.js +439 -0
- package/dist/shared/filereview/git-substrate.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +11 -0
- package/dist/shared/filereview/index.js +12 -0
- package/dist/shared/filereview/index.js.map +1 -0
- package/dist/shared/filereview/secret-paths.d.ts +63 -0
- package/dist/shared/filereview/secret-paths.js +105 -0
- package/dist/shared/filereview/secret-paths.js.map +1 -0
- package/dist/shared/fingerprint-secret.d.ts +26 -0
- package/dist/shared/fingerprint-secret.js +47 -0
- package/dist/shared/fingerprint-secret.js.map +1 -0
- package/dist/shared/model-client.d.ts +51 -0
- package/dist/shared/model-client.js +77 -0
- package/dist/shared/model-client.js.map +1 -0
- package/dist/shared/plan-artifact.js +0 -2
- package/dist/shared/plan-artifact.js.map +1 -1
- package/dist/shared/status-offload.d.ts +83 -9
- package/dist/shared/status-offload.js +399 -79
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/status.js +14 -1
- package/dist/shared/status.js.map +1 -1
- package/dist/shared/tool-kind.d.ts +19 -0
- package/dist/shared/tool-kind.js +13 -0
- package/dist/shared/tool-kind.js.map +1 -1
- package/dist/shared/tool-row.d.ts +88 -0
- package/dist/shared/tool-row.js +127 -0
- package/dist/shared/tool-row.js.map +1 -0
- package/dist/shared/workspace/platform-dir.d.ts +25 -0
- package/dist/shared/workspace/platform-dir.js +38 -2
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.js +56 -7
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.d.ts +50 -0
- package/dist/workflows/connect-mcp-server.js +136 -15
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/types.d.ts +8 -0
- package/package.json +2 -2
- package/src/__test-utils__/approval-contract/contract.ts +224 -0
- package/src/__test-utils__/approval-contract/types.ts +179 -0
- package/src/__test-utils__/fake-artifact-storage.ts +72 -0
- package/src/__tests__/approval-gateway-contract.test.ts +29 -0
- package/src/__tests__/claimcheck-codec.test.ts +16 -53
- package/src/__tests__/golden-e2e.test.ts +2 -0
- package/src/__tests__/runner-token-coordinator.test.ts +3 -3
- package/src/activities/__tests__/call-agent-status.test.ts +135 -0
- package/src/activities/__tests__/call-llm.test.ts +1 -1
- package/src/activities/__tests__/classify-tool-approvals.test.ts +208 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +30 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +2 -1
- package/src/activities/call-agent-status.ts +74 -4
- package/src/activities/call-llm.ts +18 -63
- package/src/activities/classify-tool-approvals.ts +101 -19
- package/src/activities/discover-mcp-server.ts +29 -1
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +216 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +148 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +41 -9
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +292 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +68 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +1005 -0
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +187 -0
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +97 -0
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +240 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +203 -0
- package/src/activities/execute-cursor/__tests__/exact-apply.test.ts +375 -0
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +1294 -24
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +446 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +384 -110
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +171 -25
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +189 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +44 -23
- package/src/activities/execute-cursor/__tests__/workspace-setup.test.ts +190 -10
- package/src/activities/execute-cursor/approval-policy.ts +28 -159
- package/src/activities/execute-cursor/approval-state.ts +366 -18
- package/src/activities/execute-cursor/capture-flow.ts +323 -0
- package/src/activities/execute-cursor/cas-observations.ts +204 -0
- package/src/activities/execute-cursor/command-provenance.ts +168 -0
- package/src/activities/execute-cursor/exact-apply.ts +253 -0
- package/src/activities/execute-cursor/hook-script.ts +317 -51
- package/src/activities/execute-cursor/index.ts +575 -67
- package/src/activities/execute-cursor/message-translator.ts +963 -89
- package/src/activities/execute-cursor/prompt-builder.ts +80 -14
- package/src/activities/execute-cursor/workspace-setup.ts +257 -42
- package/src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts +180 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +134 -0
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +11 -24
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +64 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-observer.test.ts +163 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +8 -5
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +342 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +284 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +14 -36
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +31 -13
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +349 -0
- package/src/activities/execute-deep-agent/__tests__/stamp-flowed-rows.test.ts +119 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +91 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +9 -9
- package/src/activities/execute-deep-agent/__tests__/subagent-approval-propagation.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +213 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +3 -6
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +84 -1
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +4 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +80 -0
- package/src/activities/execute-deep-agent/attachment-injector.ts +20 -11
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +66 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +125 -0
- package/src/activities/execute-deep-agent/hitl.ts +23 -7
- package/src/activities/execute-deep-agent/index.ts +463 -40
- package/src/activities/execute-deep-agent/inline-publisher.ts +27 -4
- package/src/activities/execute-deep-agent/setup.ts +223 -125
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +64 -0
- package/src/activities/execute-deep-agent/status-builder-shared.ts +62 -23
- package/src/activities/execute-deep-agent/status-builder.ts +63 -8
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +2 -16
- package/src/activities/execute-deep-agent/streaming.ts +3 -13
- package/src/activities/execute-deep-agent/subagent-transformer.ts +53 -13
- package/src/activities/execute-deep-agent/subagent-wiring.ts +50 -3
- package/src/activities/execute-deep-agent/v3-status-builder.ts +36 -2
- package/src/claimcheck/payload-codec.ts +8 -8
- package/src/client/stigmer-client.ts +9 -1
- package/src/middleware/__tests__/approval-gate.test.ts +488 -4
- package/src/middleware/approval-gate.ts +247 -38
- package/src/middleware/types.ts +5 -5
- package/src/shared/__tests__/activity-input.test.ts +78 -0
- package/src/shared/__tests__/approval-canonicalize.test.ts +106 -0
- package/src/shared/__tests__/approval-fingerprint.test.ts +115 -0
- package/src/shared/__tests__/approval-policy.test.ts +274 -40
- package/src/shared/__tests__/args-preview.test.ts +78 -0
- package/src/shared/__tests__/artifact-storage-extended.test.ts +62 -10
- package/src/shared/__tests__/artifact-storage.test.ts +123 -11
- package/src/shared/__tests__/file-change.test.ts +85 -0
- package/src/shared/__tests__/file-tools.test.ts +90 -0
- package/src/shared/__tests__/fingerprint-secret.test.ts +51 -0
- package/src/shared/__tests__/lease-scope-corpus.test.ts +56 -0
- package/src/shared/__tests__/model-client.test.ts +162 -0
- package/src/shared/__tests__/plan-artifact.test.ts +11 -26
- package/src/shared/__tests__/policy-source-corpus.test.ts +58 -0
- package/src/shared/__tests__/status-offload.test.ts +573 -16
- package/src/shared/__tests__/status.test.ts +4 -5
- package/src/shared/__tests__/tool-kind.test.ts +24 -1
- package/src/shared/__tests__/tool-row.test.ts +221 -0
- package/src/shared/activity-input.ts +57 -0
- package/src/shared/approval-canonicalize.ts +159 -0
- package/src/shared/approval-fingerprint.ts +148 -0
- package/src/shared/approval-policy.ts +303 -27
- package/src/shared/args-preview.ts +98 -0
- package/src/shared/artifact-storage.ts +62 -11
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +1 -2
- package/src/shared/file-change.ts +64 -0
- package/src/shared/file-tools.ts +169 -0
- package/src/shared/filereview/__tests__/capture.test.ts +856 -0
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +404 -0
- package/src/shared/filereview/__tests__/digest.test.ts +100 -0
- package/src/shared/filereview/__tests__/events.test.ts +245 -0
- package/src/shared/filereview/__tests__/git-substrate.test.ts +362 -0
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +286 -0
- package/src/shared/filereview/__tests__/secret-paths.test.ts +121 -0
- package/src/shared/filereview/capture.ts +727 -0
- package/src/shared/filereview/cas-substrate.ts +401 -0
- package/src/shared/filereview/digest.ts +83 -0
- package/src/shared/filereview/events.ts +449 -0
- package/src/shared/filereview/git-substrate.ts +555 -0
- package/src/shared/filereview/index.ts +60 -0
- package/src/shared/filereview/secret-paths.ts +121 -0
- package/src/shared/fingerprint-secret.ts +53 -0
- package/src/shared/model-client.ts +122 -0
- package/src/shared/plan-artifact.ts +0 -2
- package/src/shared/status-offload.ts +433 -77
- package/src/shared/status.ts +13 -0
- package/src/shared/tool-kind.ts +33 -0
- package/src/shared/tool-row.ts +135 -0
- package/src/shared/workspace/platform-dir.ts +41 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +35 -18
- package/src/workflow-engine/__tests__/tasks/try.test.ts +1 -1
- package/src/workflows/__tests__/connect-mcp-server.test.ts +304 -29
- package/src/workflows/call-agent-orchestrator.ts +53 -6
- package/src/workflows/connect-mcp-server.ts +179 -24
- package/src/workflows/types.ts +8 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test: resuming an execution from a durable LangGraph checkpoint
|
|
3
|
+
* must NOT wipe the message transcript.
|
|
4
|
+
*
|
|
5
|
+
* This models the cloud path deterministically. On the post-approval
|
|
6
|
+
* re-invocation of ExecuteDeepAgent:
|
|
7
|
+
* - the persisted execution already holds the run-1 transcript (an AI message
|
|
8
|
+
* plus the gated tool call in WAITING_APPROVAL, approved by the user);
|
|
9
|
+
* - the durable checkpoint exists (getState reports prior values + a pending
|
|
10
|
+
* interrupt), so streamEvents re-emits ONLY the post-interrupt events
|
|
11
|
+
* (the resumed tool execution + the following assistant turn).
|
|
12
|
+
*
|
|
13
|
+
* The activity must seed its status from the persisted execution so the
|
|
14
|
+
* streamed delta appends onto the existing history, and the resumed tool
|
|
15
|
+
* events must reconcile to the existing gated tool call rather than
|
|
16
|
+
* duplicating it. The persisted status after resume must therefore contain
|
|
17
|
+
* the full transcript with exactly one copy of the gated tool call.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
21
|
+
import { makeInMemoryArtifactStorage } from "../../../__test-utils__/fake-artifact-storage.js";
|
|
22
|
+
import { create, clone } from "@bufbuild/protobuf";
|
|
23
|
+
import {
|
|
24
|
+
AgentExecutionStatusSchema,
|
|
25
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
26
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
27
|
+
import {
|
|
28
|
+
AgentMessageSchema,
|
|
29
|
+
ToolCallSchema,
|
|
30
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
31
|
+
import {
|
|
32
|
+
ApprovalAction,
|
|
33
|
+
ExecutionPhase,
|
|
34
|
+
MessageType,
|
|
35
|
+
ToolCallStatus,
|
|
36
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
37
|
+
import type { V3ProtocolEvent } from "../v3-event-recorder.js";
|
|
38
|
+
import type { Config } from "../../../config.js";
|
|
39
|
+
import type { SetupResult } from "../setup.js";
|
|
40
|
+
|
|
41
|
+
// The Temporal activity context is unavailable outside a worker.
|
|
42
|
+
vi.mock("@temporalio/activity", () => ({
|
|
43
|
+
Context: {
|
|
44
|
+
current: () => ({
|
|
45
|
+
cancellationSignal: { aborted: false },
|
|
46
|
+
heartbeat: vi.fn(),
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
CancelledFailure: class CancelledFailure extends Error {},
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
vi.mock("../../../idle-watchdog.js", () => ({
|
|
53
|
+
activityStarted: vi.fn(),
|
|
54
|
+
activityFinished: vi.fn(),
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
// Capture every persisted status (the gRPC chokepoint) for assertions.
|
|
58
|
+
const { persistedStatuses } = vi.hoisted(() => ({
|
|
59
|
+
persistedStatuses: [] as AgentExecutionStatus[],
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
vi.mock("../../../client/stigmer-client.js", () => ({
|
|
63
|
+
StigmerClient: vi.fn().mockImplementation(() => ({
|
|
64
|
+
updateStatus: vi.fn(async (_id: string, status: AgentExecutionStatus) => {
|
|
65
|
+
// Snapshot point-in-time: persistStatus mutates the same object in place.
|
|
66
|
+
persistedStatuses.push(clone(AgentExecutionStatusSchema, status));
|
|
67
|
+
return { signal: 0 };
|
|
68
|
+
}),
|
|
69
|
+
getExecution: vi.fn(),
|
|
70
|
+
})),
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
// performSetup is mocked per-test to return a fake durable-resume SetupResult.
|
|
74
|
+
vi.mock("../setup.js", () => ({ performSetup: vi.fn() }));
|
|
75
|
+
|
|
76
|
+
import { createDeepAgentActivities } from "../index.js";
|
|
77
|
+
import { performSetup } from "../setup.js";
|
|
78
|
+
|
|
79
|
+
const GATED_TOOL_CALL_ID = "toolu_hitl_01";
|
|
80
|
+
const PRE_RESUME_AI_TEXT = "I'll call the echo tool.";
|
|
81
|
+
const POST_RESUME_AI_TEXT = "The echo tool returned the result.";
|
|
82
|
+
|
|
83
|
+
function v3Event(
|
|
84
|
+
seq: number,
|
|
85
|
+
method: string,
|
|
86
|
+
data: unknown,
|
|
87
|
+
namespace: string[] = [],
|
|
88
|
+
): V3ProtocolEvent {
|
|
89
|
+
return {
|
|
90
|
+
type: "event",
|
|
91
|
+
seq,
|
|
92
|
+
method,
|
|
93
|
+
params: { namespace, timestamp: Date.now(), data },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The events a durable resume re-emits: tool execution + assistant turn. */
|
|
98
|
+
function resumeEvents(): V3ProtocolEvent[] {
|
|
99
|
+
return [
|
|
100
|
+
v3Event(0, "tools", {
|
|
101
|
+
event: "tool-started",
|
|
102
|
+
tool_call_id: GATED_TOOL_CALL_ID,
|
|
103
|
+
tool_name: "echo",
|
|
104
|
+
input: { input: "hello-hitl" },
|
|
105
|
+
}, [`tools:${GATED_TOOL_CALL_ID}`]),
|
|
106
|
+
v3Event(1, "tools", {
|
|
107
|
+
event: "tool-finished",
|
|
108
|
+
tool_call_id: GATED_TOOL_CALL_ID,
|
|
109
|
+
output: {
|
|
110
|
+
lc: 1,
|
|
111
|
+
type: "constructor",
|
|
112
|
+
id: ["langchain_core", "messages", "ToolMessage"],
|
|
113
|
+
kwargs: { content: "hello-hitl", tool_call_id: GATED_TOOL_CALL_ID },
|
|
114
|
+
},
|
|
115
|
+
}, [`tools:${GATED_TOOL_CALL_ID}`]),
|
|
116
|
+
v3Event(2, "messages", { event: "message-start", run_id: "r2" }),
|
|
117
|
+
v3Event(3, "messages", {
|
|
118
|
+
event: "content-block-delta",
|
|
119
|
+
index: 0,
|
|
120
|
+
delta: { type: "text-delta", text: POST_RESUME_AI_TEXT },
|
|
121
|
+
run_id: "r2",
|
|
122
|
+
}),
|
|
123
|
+
v3Event(4, "messages", { event: "message-finish", run_id: "r2" }),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function mockV3Run(events: V3ProtocolEvent[]) {
|
|
128
|
+
return {
|
|
129
|
+
[Symbol.asyncIterator]: async function* () {
|
|
130
|
+
for (const e of events) yield e;
|
|
131
|
+
},
|
|
132
|
+
output: Promise.resolve({ messages: [] }),
|
|
133
|
+
abort: vi.fn(),
|
|
134
|
+
signal: new AbortController().signal,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The run-1 transcript already persisted on the execution. */
|
|
139
|
+
function persistedRunOneStatus(): AgentExecutionStatus {
|
|
140
|
+
return create(AgentExecutionStatusSchema, {
|
|
141
|
+
phase: ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL,
|
|
142
|
+
startedAt: "2026-06-15T00:00:00.000Z",
|
|
143
|
+
messages: [
|
|
144
|
+
create(AgentMessageSchema, {
|
|
145
|
+
type: MessageType.MESSAGE_AI,
|
|
146
|
+
content: PRE_RESUME_AI_TEXT,
|
|
147
|
+
timestamp: "2026-06-15T00:00:01.000Z",
|
|
148
|
+
toolCalls: [
|
|
149
|
+
create(ToolCallSchema, {
|
|
150
|
+
id: GATED_TOOL_CALL_ID,
|
|
151
|
+
name: "echo",
|
|
152
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
153
|
+
requiresApproval: true,
|
|
154
|
+
approvalMessage: "Execute echo tool",
|
|
155
|
+
approvalAction: ApprovalAction.APPROVE,
|
|
156
|
+
mcpServerSlug: "test-mcp",
|
|
157
|
+
}),
|
|
158
|
+
],
|
|
159
|
+
}),
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function fakeDurableResumeSetup(): SetupResult {
|
|
165
|
+
let streamed = false;
|
|
166
|
+
|
|
167
|
+
const pendingInterruptState = {
|
|
168
|
+
values: { messages: [{ role: "assistant", content: PRE_RESUME_AI_TEXT }] },
|
|
169
|
+
tasks: [{
|
|
170
|
+
id: "task-1",
|
|
171
|
+
interrupts: [{
|
|
172
|
+
value: {
|
|
173
|
+
tool_call_id: GATED_TOOL_CALL_ID,
|
|
174
|
+
tool_name: "echo",
|
|
175
|
+
mcp_server_slug: "test-mcp",
|
|
176
|
+
message: "Execute echo tool",
|
|
177
|
+
},
|
|
178
|
+
resumeValue: undefined,
|
|
179
|
+
}],
|
|
180
|
+
}],
|
|
181
|
+
};
|
|
182
|
+
const resolvedState = {
|
|
183
|
+
values: { messages: [{ role: "assistant", content: PRE_RESUME_AI_TEXT }] },
|
|
184
|
+
tasks: [],
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const agentGraph = {
|
|
188
|
+
// Before the resumed stream: a pending interrupt awaits the decision.
|
|
189
|
+
// After the stream: the interrupt is resolved (no pending approvals).
|
|
190
|
+
getState: vi.fn(async () => (streamed ? resolvedState : pendingInterruptState)),
|
|
191
|
+
streamEvents: vi.fn(async () => {
|
|
192
|
+
streamed = true;
|
|
193
|
+
return mockV3Run(resumeEvents());
|
|
194
|
+
}),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const approvalPolicies = new Map([
|
|
198
|
+
["test-mcp/echo", {
|
|
199
|
+
toolName: "echo",
|
|
200
|
+
mcpServerSlug: "test-mcp",
|
|
201
|
+
requiresApproval: true,
|
|
202
|
+
approvalMessage: "Execute echo tool",
|
|
203
|
+
}],
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
agentGraph,
|
|
208
|
+
langgraphConfig: { configurable: { thread_id: "thread-1" } },
|
|
209
|
+
langgraphInput: { messages: [{ role: "user", content: "Call echo" }] },
|
|
210
|
+
execution: {
|
|
211
|
+
spec: { message: "Call echo with 'hello-hitl'." },
|
|
212
|
+
status: persistedRunOneStatus(),
|
|
213
|
+
},
|
|
214
|
+
agent: {},
|
|
215
|
+
session: { spec: { workspaceEntries: [] } },
|
|
216
|
+
workspaceBackend: { rootDir: "/tmp/ws" },
|
|
217
|
+
mcpConnection: null,
|
|
218
|
+
mergedEnvVars: {},
|
|
219
|
+
secretKeys: new Set<string>(),
|
|
220
|
+
modelName: "claude-sonnet",
|
|
221
|
+
gracefulStop: undefined,
|
|
222
|
+
artifactStorage: makeInMemoryArtifactStorage({ urlBase: "https://artifacts.local/" }).storage,
|
|
223
|
+
provisionResults: [],
|
|
224
|
+
approvalPolicies,
|
|
225
|
+
toolServerMap: new Map([["echo", "test-mcp"]]),
|
|
226
|
+
autoApproveAll: false,
|
|
227
|
+
hasStructuredOutput: false,
|
|
228
|
+
streamVersion: "v3",
|
|
229
|
+
} as unknown as SetupResult;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const baseConfig: Config = {
|
|
233
|
+
taskQueue: "test-queue",
|
|
234
|
+
temporalAddress: "localhost:7233",
|
|
235
|
+
temporalNamespace: "default",
|
|
236
|
+
stigmerBackendEndpoint: "http://localhost:7234",
|
|
237
|
+
stigmerToken: null,
|
|
238
|
+
cursorApiKey: "",
|
|
239
|
+
workspaceRootDir: "/tmp/test",
|
|
240
|
+
mode: "cloud",
|
|
241
|
+
proxyEndpoint: "http://localhost:7234",
|
|
242
|
+
maxConcurrentActivities: 5,
|
|
243
|
+
idleTimeoutSeconds: null,
|
|
244
|
+
cloudModeEnabled: true,
|
|
245
|
+
checkpointerType: "http",
|
|
246
|
+
checkpointerProxyEndpoint: "http://localhost:7234",
|
|
247
|
+
primaryModel: "claude-sonnet",
|
|
248
|
+
cursorStreamStallTimeoutMs: 180000,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
describe("ExecuteDeepAgent — durable-checkpoint resume preserves history", () => {
|
|
252
|
+
beforeEach(() => {
|
|
253
|
+
persistedStatuses.length = 0;
|
|
254
|
+
vi.mocked(performSetup).mockResolvedValue(fakeDurableResumeSetup());
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("retains the full transcript and resolves the gated tool call in place", async () => {
|
|
258
|
+
const activities = createDeepAgentActivities(baseConfig);
|
|
259
|
+
await activities.ExecuteDeepAgent("exec-1", "thread-1");
|
|
260
|
+
|
|
261
|
+
expect(persistedStatuses.length).toBeGreaterThan(0);
|
|
262
|
+
const finalStatus = persistedStatuses.at(-1)!;
|
|
263
|
+
|
|
264
|
+
// 1. The pre-resume transcript survives the resume.
|
|
265
|
+
const allText = finalStatus.messages.map(m => m.content).join("\n");
|
|
266
|
+
expect(allText).toContain(PRE_RESUME_AI_TEXT);
|
|
267
|
+
|
|
268
|
+
// 2. The post-resume assistant turn is appended.
|
|
269
|
+
expect(allText).toContain(POST_RESUME_AI_TEXT);
|
|
270
|
+
|
|
271
|
+
// 3. Exactly one copy of the gated tool call — no duplicate on resume.
|
|
272
|
+
const gatedToolCalls = finalStatus.messages
|
|
273
|
+
.flatMap(m => m.toolCalls)
|
|
274
|
+
.filter(tc => tc.id === GATED_TOOL_CALL_ID);
|
|
275
|
+
expect(gatedToolCalls).toHaveLength(1);
|
|
276
|
+
|
|
277
|
+
// 4. The gated tool call is resolved with its result, not stuck waiting.
|
|
278
|
+
expect(gatedToolCalls[0].status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
279
|
+
expect(gatedToolCalls[0].result).toContain("hello-hitl");
|
|
280
|
+
|
|
281
|
+
// 5. The execution reaches a terminal COMPLETED phase.
|
|
282
|
+
expect(finalStatus.phase).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { Command } from "@langchain/langgraph";
|
|
3
3
|
import { resolveResumeInput, reconcileToolCallStatuses } from "../hitl.js";
|
|
4
4
|
import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
@@ -70,19 +70,12 @@ function makeGraphState(
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function makeAgentGraph(state: GraphStateSnapshot) {
|
|
74
|
-
return {
|
|
75
|
-
getState: vi.fn().mockResolvedValue(state),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
73
|
describe("resolveResumeInput", () => {
|
|
80
|
-
it("returns fresh input when no interrupts exist",
|
|
74
|
+
it("returns fresh input when no interrupts exist", () => {
|
|
81
75
|
const execution = makeExecution([]);
|
|
82
|
-
const
|
|
83
|
-
const config = { configurable: { thread_id: "t1" } };
|
|
76
|
+
const state: GraphStateSnapshot = { values: {}, tasks: [] };
|
|
84
77
|
|
|
85
|
-
const result =
|
|
78
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
86
79
|
|
|
87
80
|
expect(result.isResumeFromApproval).toBe(false);
|
|
88
81
|
expect(result.hasRejection).toBe(false);
|
|
@@ -91,19 +84,16 @@ describe("resolveResumeInput", () => {
|
|
|
91
84
|
});
|
|
92
85
|
});
|
|
93
86
|
|
|
94
|
-
it("returns fresh input when interrupts exist but no decisions",
|
|
87
|
+
it("returns fresh input when interrupts exist but no decisions", () => {
|
|
95
88
|
const execution = makeExecution([]);
|
|
96
89
|
const state = makeGraphState([{ taskId: "task-1", toolCallId: "call-1" }]);
|
|
97
|
-
const graph = makeAgentGraph(state);
|
|
98
90
|
|
|
99
|
-
const result =
|
|
100
|
-
execution, graph, { configurable: { thread_id: "t1" } }, "hello",
|
|
101
|
-
);
|
|
91
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
102
92
|
|
|
103
93
|
expect(result.isResumeFromApproval).toBe(false);
|
|
104
94
|
});
|
|
105
95
|
|
|
106
|
-
it("builds Command(resume) when interrupts match decisions",
|
|
96
|
+
it("builds Command(resume) when interrupts match decisions", () => {
|
|
107
97
|
const execution = makeExecution([{
|
|
108
98
|
id: "call-1",
|
|
109
99
|
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
@@ -111,18 +101,15 @@ describe("resolveResumeInput", () => {
|
|
|
111
101
|
}]);
|
|
112
102
|
|
|
113
103
|
const state = makeGraphState([{ taskId: "task-1", toolCallId: "call-1" }]);
|
|
114
|
-
const graph = makeAgentGraph(state);
|
|
115
104
|
|
|
116
|
-
const result =
|
|
117
|
-
execution, graph, { configurable: { thread_id: "t1" } }, "hello",
|
|
118
|
-
);
|
|
105
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
119
106
|
|
|
120
107
|
expect(result.isResumeFromApproval).toBe(true);
|
|
121
108
|
expect(result.hasRejection).toBe(false);
|
|
122
109
|
expect(result.graphInput).toBeInstanceOf(Command);
|
|
123
110
|
});
|
|
124
111
|
|
|
125
|
-
it("detects rejection when a decision is REJECT",
|
|
112
|
+
it("detects rejection when a decision is REJECT", () => {
|
|
126
113
|
const execution = makeExecution([{
|
|
127
114
|
id: "call-1",
|
|
128
115
|
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
@@ -130,18 +117,15 @@ describe("resolveResumeInput", () => {
|
|
|
130
117
|
}]);
|
|
131
118
|
|
|
132
119
|
const state = makeGraphState([{ taskId: "task-1", toolCallId: "call-1" }]);
|
|
133
|
-
const graph = makeAgentGraph(state);
|
|
134
120
|
|
|
135
|
-
const result =
|
|
136
|
-
execution, graph, { configurable: { thread_id: "t1" } }, "hello",
|
|
137
|
-
);
|
|
121
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
138
122
|
|
|
139
123
|
expect(result.isResumeFromApproval).toBe(true);
|
|
140
124
|
expect(result.hasRejection).toBe(true);
|
|
141
125
|
expect(result.rejectionReason).toContain("Rejected by user");
|
|
142
126
|
});
|
|
143
127
|
|
|
144
|
-
it("handles multiple interrupts with mixed decisions",
|
|
128
|
+
it("handles multiple interrupts with mixed decisions", () => {
|
|
145
129
|
const execution = makeExecution([
|
|
146
130
|
{
|
|
147
131
|
id: "call-1",
|
|
@@ -159,17 +143,14 @@ describe("resolveResumeInput", () => {
|
|
|
159
143
|
{ taskId: "task-1", toolCallId: "call-1" },
|
|
160
144
|
{ taskId: "task-2", toolCallId: "call-2" },
|
|
161
145
|
]);
|
|
162
|
-
const graph = makeAgentGraph(state);
|
|
163
146
|
|
|
164
|
-
const result =
|
|
165
|
-
execution, graph, { configurable: { thread_id: "t1" } }, "hello",
|
|
166
|
-
);
|
|
147
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
167
148
|
|
|
168
149
|
expect(result.isResumeFromApproval).toBe(true);
|
|
169
150
|
expect(result.hasRejection).toBe(false);
|
|
170
151
|
});
|
|
171
152
|
|
|
172
|
-
it("skips already-resumed interrupts",
|
|
153
|
+
it("skips already-resumed interrupts", () => {
|
|
173
154
|
const execution = makeExecution([{
|
|
174
155
|
id: "call-1",
|
|
175
156
|
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
@@ -179,11 +160,8 @@ describe("resolveResumeInput", () => {
|
|
|
179
160
|
const state = makeGraphState([
|
|
180
161
|
{ taskId: "task-1", toolCallId: "call-1", hasResume: true },
|
|
181
162
|
]);
|
|
182
|
-
const graph = makeAgentGraph(state);
|
|
183
163
|
|
|
184
|
-
const result =
|
|
185
|
-
execution, graph, { configurable: { thread_id: "t1" } }, "hello",
|
|
186
|
-
);
|
|
164
|
+
const result = resolveResumeInput(execution, state, "hello");
|
|
187
165
|
|
|
188
166
|
expect(result.isResumeFromApproval).toBe(false);
|
|
189
167
|
});
|
|
@@ -10,6 +10,7 @@ import { InlinePublisher } from "../inline-publisher.js";
|
|
|
10
10
|
import { StatusBuilder } from "../status-builder.js";
|
|
11
11
|
import { LocalWorkspaceBackend } from "../../../shared/workspace/local-backend.js";
|
|
12
12
|
import type { ArtifactStorage } from "../../../shared/artifact-storage.js";
|
|
13
|
+
import { makeInMemoryArtifactStorage } from "../../../__test-utils__/fake-artifact-storage.js";
|
|
13
14
|
import type { WorkspaceBackend } from "../../../shared/workspace/types.js";
|
|
14
15
|
|
|
15
16
|
function makeStatusBuilder(): StatusBuilder {
|
|
@@ -35,19 +36,16 @@ function mockArtifactStorage(): ArtifactStorage & {
|
|
|
35
36
|
uploadedKeys: string[];
|
|
36
37
|
uploadedContent: Map<string, Buffer>;
|
|
37
38
|
} {
|
|
39
|
+
// Canonical double; `uploadedKeys`/`uploadedContent` mirror the backing store
|
|
40
|
+
// so the existing assertions keep working and `download` reads back uploads.
|
|
41
|
+
const { storage, blobs } = makeInMemoryArtifactStorage({ urlBase: "http://localhost:7235/" });
|
|
38
42
|
const uploadedKeys: string[] = [];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
uploadedContent.set(key, content);
|
|
46
|
-
return key;
|
|
47
|
-
}),
|
|
48
|
-
getDownloadUrl: vi.fn(async (key: string) => `http://localhost:7235/${key}`),
|
|
49
|
-
exists: vi.fn(async () => false),
|
|
50
|
-
};
|
|
43
|
+
storage.upload.mockImplementation(async (key: string, content: Buffer) => {
|
|
44
|
+
uploadedKeys.push(key);
|
|
45
|
+
blobs.set(key, Buffer.from(content));
|
|
46
|
+
return key;
|
|
47
|
+
});
|
|
48
|
+
return Object.assign(storage, { uploadedKeys, uploadedContent: blobs });
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
function sha256(content: string): string {
|
|
@@ -83,7 +81,6 @@ describe("InlinePublisher", () => {
|
|
|
83
81
|
expect(artifact.sandboxPath).toBe("src/main.ts");
|
|
84
82
|
expect(artifact.kind).toBe(ExecutionArtifactKind.FILE);
|
|
85
83
|
expect(artifact.storageKey).toBe("artifacts/exec-123/main.ts");
|
|
86
|
-
expect(artifact.downloadUrl).toBe("http://localhost:7235/artifacts/exec-123/main.ts");
|
|
87
84
|
expect(artifact.contentHash).toBe(sha256("console.log('hello');"));
|
|
88
85
|
expect(Number(artifact.sizeBytes)).toBeGreaterThan(0);
|
|
89
86
|
});
|
|
@@ -164,6 +161,27 @@ describe("InlinePublisher", () => {
|
|
|
164
161
|
expect(sb.currentStatus.artifacts[0].contentHash).toBe(expected);
|
|
165
162
|
});
|
|
166
163
|
|
|
164
|
+
it("never publishes a secret-like file to artifact storage (design doc 12, D4)", async () => {
|
|
165
|
+
// Under the global bypass a secret write is not blocked up front, so it would
|
|
166
|
+
// otherwise be uploaded here. The publisher must withhold it: no read, no
|
|
167
|
+
// upload, no registered artifact — the secret's bytes never reach storage.
|
|
168
|
+
const secretBackend = mockWorkspaceBackend({ ".env": "API_KEY=super-secret-value" });
|
|
169
|
+
const readSpy = secretBackend.readFile as ReturnType<typeof vi.fn>;
|
|
170
|
+
const pub = new InlinePublisher({
|
|
171
|
+
workspaceBackend: secretBackend,
|
|
172
|
+
artifactStorage: storage,
|
|
173
|
+
statusWriter: sb,
|
|
174
|
+
executionId: "exec-secret",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await pub.publish(".env");
|
|
178
|
+
|
|
179
|
+
expect(storage.uploadedKeys).toHaveLength(0);
|
|
180
|
+
expect(sb.currentStatus.artifacts).toHaveLength(0);
|
|
181
|
+
expect(readSpy).not.toHaveBeenCalled(); // withheld before the file is even read
|
|
182
|
+
expect(pub.publishedPaths.size).toBe(0);
|
|
183
|
+
});
|
|
184
|
+
|
|
167
185
|
it("guesses content type for common extensions", async () => {
|
|
168
186
|
const jsonBackend = mockWorkspaceBackend({ "data.json": '{"key":"val"}' });
|
|
169
187
|
const pub = new InlinePublisher({
|