@stigmer/runner 3.0.9-dev.20260616060535 → 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 +10 -0
- package/dist/activities/execute-deep-agent/hitl.js +5 -1
- 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 +370 -56
- 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 +11 -5
- package/dist/activities/execute-deep-agent/status-builder.js +6 -2
- 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 +6 -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 +2 -5
- 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 +12 -11
- 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 +15 -1
- package/src/activities/execute-deep-agent/index.ts +434 -64
- 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 +19 -7
- 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 +8 -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,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression: two SEQUENTIAL approval gates under the ephemeral memory
|
|
3
|
+
* checkpointer (the OSS local / desktop default — config.ts resolves
|
|
4
|
+
* `checkpointerType: "memory"`).
|
|
5
|
+
*
|
|
6
|
+
* Unlike the durable (http) checkpointer — which resumes via Command(resume) and
|
|
7
|
+
* re-emits only post-checkpoint events — the memory checkpointer is recreated
|
|
8
|
+
* empty every invocation (factory.ts), so the Temporal workflow's post-approval
|
|
9
|
+
* re-invocation REPLAYS the graph from scratch. The langgraph input is just the
|
|
10
|
+
* original user message (setup.ts), and the blind-FIFO mock LLM advances exactly
|
|
11
|
+
* one gate per invocation, so the replay reaches the NEXT gate (gate B) directly.
|
|
12
|
+
*
|
|
13
|
+
* The defect this pins: on that gate-B re-invocation the live checkpoint is
|
|
14
|
+
* empty, so a status rebuilt from an empty proto emits a transcript that holds
|
|
15
|
+
* ONLY gate B and silently drops gate A's already-committed tool-call id. The
|
|
16
|
+
* server's append-only-at-identity guard (update_status.go
|
|
17
|
+
* nonTerminalTranscriptRegression) then rejects the update and keeps the old
|
|
18
|
+
* messages, but projects pending_approvals from those kept messages — gate A is
|
|
19
|
+
* already decided, so pending=0, and WAITING_FOR_APPROVAL + pending=0 makes the
|
|
20
|
+
* workflow auto-resume, skipping gate B. The fix seeds the status from the
|
|
21
|
+
* persisted transcript whenever the execution already has history, so gate B's
|
|
22
|
+
* update is a superset of gate A rather than a replacement.
|
|
23
|
+
*
|
|
24
|
+
* This test exercises BOTH invocations through the real activity, asserting the
|
|
25
|
+
* gate-A tool call survives into the gate-B update — the single behavior that,
|
|
26
|
+
* if regressed, reintroduces the silent skip.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
30
|
+
import { makeInMemoryArtifactStorage } from "../../../__test-utils__/fake-artifact-storage.js";
|
|
31
|
+
import { create, clone } from "@bufbuild/protobuf";
|
|
32
|
+
import {
|
|
33
|
+
AgentExecutionStatusSchema,
|
|
34
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
35
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
36
|
+
import {
|
|
37
|
+
AgentMessageSchema,
|
|
38
|
+
ToolCallSchema,
|
|
39
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
40
|
+
import {
|
|
41
|
+
ApprovalAction,
|
|
42
|
+
ExecutionPhase,
|
|
43
|
+
MessageType,
|
|
44
|
+
ToolCallStatus,
|
|
45
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
46
|
+
import type { V3ProtocolEvent } from "../v3-event-recorder.js";
|
|
47
|
+
import type { Config } from "../../../config.js";
|
|
48
|
+
import type { SetupResult } from "../setup.js";
|
|
49
|
+
|
|
50
|
+
// The Temporal activity context is unavailable outside a worker.
|
|
51
|
+
vi.mock("@temporalio/activity", () => ({
|
|
52
|
+
Context: {
|
|
53
|
+
current: () => ({
|
|
54
|
+
cancellationSignal: { aborted: false },
|
|
55
|
+
heartbeat: vi.fn(),
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
CancelledFailure: class CancelledFailure extends Error {},
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
vi.mock("../../../idle-watchdog.js", () => ({
|
|
62
|
+
activityStarted: vi.fn(),
|
|
63
|
+
activityFinished: vi.fn(),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// Capture every persisted status (the gRPC chokepoint) for assertions.
|
|
67
|
+
const { persistedStatuses } = vi.hoisted(() => ({
|
|
68
|
+
persistedStatuses: [] as AgentExecutionStatus[],
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
vi.mock("../../../client/stigmer-client.js", () => ({
|
|
72
|
+
StigmerClient: vi.fn().mockImplementation(() => ({
|
|
73
|
+
updateStatus: vi.fn(async (_id: string, status: AgentExecutionStatus) => {
|
|
74
|
+
// Snapshot point-in-time: persistStatus mutates the same object in place.
|
|
75
|
+
persistedStatuses.push(clone(AgentExecutionStatusSchema, status));
|
|
76
|
+
return { signal: 0 };
|
|
77
|
+
}),
|
|
78
|
+
getExecution: vi.fn(),
|
|
79
|
+
})),
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
// performSetup is mocked per-invocation to return a fake memory-replay SetupResult.
|
|
83
|
+
vi.mock("../setup.js", () => ({ performSetup: vi.fn() }));
|
|
84
|
+
|
|
85
|
+
import { createDeepAgentActivities } from "../index.js";
|
|
86
|
+
import { performSetup } from "../setup.js";
|
|
87
|
+
|
|
88
|
+
const GATE_A_ID = "toolu_gate_a";
|
|
89
|
+
const GATE_B_ID = "toolu_gate_b";
|
|
90
|
+
|
|
91
|
+
function v3Event(
|
|
92
|
+
seq: number,
|
|
93
|
+
method: string,
|
|
94
|
+
data: unknown,
|
|
95
|
+
namespace: string[] = [],
|
|
96
|
+
): V3ProtocolEvent {
|
|
97
|
+
return {
|
|
98
|
+
type: "event",
|
|
99
|
+
seq,
|
|
100
|
+
method,
|
|
101
|
+
params: { namespace, timestamp: Date.now(), data },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Events for a replayed turn that proposes a gated tool but pauses BEFORE
|
|
107
|
+
* execution. The model turn commits no transcript message (no text/tool deltas);
|
|
108
|
+
* the gated tool call is authored downstream by the activity from the pending
|
|
109
|
+
* interrupt, matching the live e2e's single-tool gate update. Two events keep the
|
|
110
|
+
* v3 loop from erroring on an empty stream.
|
|
111
|
+
*/
|
|
112
|
+
function gateTurnEvents(): V3ProtocolEvent[] {
|
|
113
|
+
return [
|
|
114
|
+
v3Event(0, "messages", { event: "message-start", run_id: "g1" }),
|
|
115
|
+
v3Event(1, "messages", { event: "message-finish", run_id: "g1" }),
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function mockV3Run(events: V3ProtocolEvent[]) {
|
|
120
|
+
return {
|
|
121
|
+
[Symbol.asyncIterator]: async function* () {
|
|
122
|
+
for (const e of events) yield e;
|
|
123
|
+
},
|
|
124
|
+
output: Promise.resolve({ messages: [] }),
|
|
125
|
+
abort: vi.fn(),
|
|
126
|
+
signal: new AbortController().signal,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The empty live checkpoint a freshly-recreated MemorySaver reports pre-stream. */
|
|
131
|
+
const EMPTY_REPLAY_STATE = { values: { messages: [] as unknown[] }, tasks: [] };
|
|
132
|
+
|
|
133
|
+
/** The paused graph state after the replayed stream reaches the next gate. */
|
|
134
|
+
function gatedGraphState(toolCallId: string, toolName: string, filePath: string) {
|
|
135
|
+
return {
|
|
136
|
+
values: {
|
|
137
|
+
messages: [{
|
|
138
|
+
role: "assistant",
|
|
139
|
+
content: "",
|
|
140
|
+
tool_calls: [{ id: toolCallId, name: toolName, args: { file_path: filePath, content: "x\n" } }],
|
|
141
|
+
}],
|
|
142
|
+
},
|
|
143
|
+
tasks: [{
|
|
144
|
+
id: "task-gate",
|
|
145
|
+
interrupts: [{
|
|
146
|
+
value: {
|
|
147
|
+
tool_call_id: toolCallId,
|
|
148
|
+
tool_name: toolName,
|
|
149
|
+
message: `Execute ${toolName}`,
|
|
150
|
+
},
|
|
151
|
+
resumeValue: undefined,
|
|
152
|
+
}],
|
|
153
|
+
}],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A memory-checkpointer invocation that replays from scratch and re-pauses at a
|
|
159
|
+
* single gate: getState reports an empty live checkpoint before the stream and a
|
|
160
|
+
* pending interrupt after it.
|
|
161
|
+
*/
|
|
162
|
+
function fakeMemoryGateSetup(opts: {
|
|
163
|
+
gateId: string;
|
|
164
|
+
gateName: string;
|
|
165
|
+
filePath: string;
|
|
166
|
+
executionStatus: AgentExecutionStatus;
|
|
167
|
+
}): SetupResult {
|
|
168
|
+
let streamed = false;
|
|
169
|
+
const gated = gatedGraphState(opts.gateId, opts.gateName, opts.filePath);
|
|
170
|
+
|
|
171
|
+
const agentGraph = {
|
|
172
|
+
getState: vi.fn(async () => (streamed ? gated : EMPTY_REPLAY_STATE)),
|
|
173
|
+
streamEvents: vi.fn(async () => {
|
|
174
|
+
streamed = true;
|
|
175
|
+
return mockV3Run(gateTurnEvents());
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
agentGraph,
|
|
181
|
+
langgraphConfig: { configurable: { thread_id: "thread-seq" } },
|
|
182
|
+
langgraphInput: { messages: [{ role: "user", content: "Write A then B" }] },
|
|
183
|
+
execution: {
|
|
184
|
+
spec: { message: "Write file A, then write file B." },
|
|
185
|
+
status: opts.executionStatus,
|
|
186
|
+
},
|
|
187
|
+
agent: {},
|
|
188
|
+
session: { spec: { workspaceEntries: [] } },
|
|
189
|
+
workspaceBackend: {
|
|
190
|
+
rootDir: "/tmp/ws",
|
|
191
|
+
exists: vi.fn(async () => false),
|
|
192
|
+
readFile: vi.fn(async () => ""),
|
|
193
|
+
},
|
|
194
|
+
mcpConnection: null,
|
|
195
|
+
mergedEnvVars: {},
|
|
196
|
+
secretKeys: new Set<string>(),
|
|
197
|
+
modelName: "claude-sonnet",
|
|
198
|
+
gracefulStop: undefined,
|
|
199
|
+
artifactStorage: makeInMemoryArtifactStorage({ urlBase: "https://artifacts.local/" }).storage,
|
|
200
|
+
provisionResults: [],
|
|
201
|
+
approvalPolicies: new Map(),
|
|
202
|
+
toolServerMap: new Map(),
|
|
203
|
+
leasedCategories: new Set(),
|
|
204
|
+
globalBypass: false,
|
|
205
|
+
hasStructuredOutput: false,
|
|
206
|
+
streamVersion: "v3",
|
|
207
|
+
} as unknown as SetupResult;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Run-1 transcript persisted on the execution, with the user's APPROVE recorded. */
|
|
211
|
+
function approvedGateAStatus(): AgentExecutionStatus {
|
|
212
|
+
return create(AgentExecutionStatusSchema, {
|
|
213
|
+
phase: ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL,
|
|
214
|
+
startedAt: "2026-06-20T00:00:00.000Z",
|
|
215
|
+
messages: [
|
|
216
|
+
create(AgentMessageSchema, {
|
|
217
|
+
type: MessageType.MESSAGE_AI,
|
|
218
|
+
content: "",
|
|
219
|
+
timestamp: "2026-06-20T00:00:01.000Z",
|
|
220
|
+
toolCalls: [
|
|
221
|
+
create(ToolCallSchema, {
|
|
222
|
+
id: GATE_A_ID,
|
|
223
|
+
name: "write_file",
|
|
224
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
225
|
+
requiresApproval: true,
|
|
226
|
+
approvalMessage: "Execute write_file",
|
|
227
|
+
// The user approved gate A; SubmitApproval records the decision on
|
|
228
|
+
// the persisted transcript (status stays WAITING_APPROVAL until the
|
|
229
|
+
// resumed tool runs), which is what re-invokes the activity.
|
|
230
|
+
approvalAction: ApprovalAction.APPROVE,
|
|
231
|
+
}),
|
|
232
|
+
],
|
|
233
|
+
}),
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const memoryConfig: Config = {
|
|
239
|
+
taskQueue: "test-queue",
|
|
240
|
+
temporalAddress: "localhost:7233",
|
|
241
|
+
temporalNamespace: "default",
|
|
242
|
+
stigmerBackendEndpoint: "http://localhost:7234",
|
|
243
|
+
stigmerToken: null,
|
|
244
|
+
cursorApiKey: "",
|
|
245
|
+
workspaceRootDir: "/tmp/test",
|
|
246
|
+
mode: "local",
|
|
247
|
+
proxyEndpoint: null,
|
|
248
|
+
maxConcurrentActivities: 5,
|
|
249
|
+
idleTimeoutSeconds: null,
|
|
250
|
+
cloudModeEnabled: false,
|
|
251
|
+
checkpointerType: "memory",
|
|
252
|
+
checkpointerProxyEndpoint: null,
|
|
253
|
+
primaryModel: "claude-sonnet",
|
|
254
|
+
cursorStreamStallTimeoutMs: 180000,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
function toolCallIds(status: AgentExecutionStatus): string[] {
|
|
258
|
+
return status.messages.flatMap(m => m.toolCalls).map(tc => tc.id);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
describe("ExecuteDeepAgent — sequential gates under the memory checkpointer", () => {
|
|
262
|
+
beforeEach(() => {
|
|
263
|
+
persistedStatuses.length = 0;
|
|
264
|
+
vi.mocked(performSetup).mockReset();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("preserves gate A's committed tool call when resuming into gate B", async () => {
|
|
268
|
+
const activities = createDeepAgentActivities(memoryConfig);
|
|
269
|
+
|
|
270
|
+
// ── Invocation 1: first run reaches gate A (no prior history to seed). ──
|
|
271
|
+
vi.mocked(performSetup).mockResolvedValueOnce(
|
|
272
|
+
fakeMemoryGateSetup({
|
|
273
|
+
gateId: GATE_A_ID,
|
|
274
|
+
gateName: "write_file",
|
|
275
|
+
filePath: "a.txt",
|
|
276
|
+
executionStatus: create(AgentExecutionStatusSchema, {}),
|
|
277
|
+
}),
|
|
278
|
+
);
|
|
279
|
+
await activities.ExecuteDeepAgent("exec-seq", "thread-seq");
|
|
280
|
+
|
|
281
|
+
const afterGateA = persistedStatuses.at(-1)!;
|
|
282
|
+
expect(afterGateA.phase).toBe(ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL);
|
|
283
|
+
expect(toolCallIds(afterGateA)).toContain(GATE_A_ID);
|
|
284
|
+
|
|
285
|
+
// ── User approves gate A; the server records the decision on the persisted
|
|
286
|
+
// transcript. The next invocation re-invocation reads this status. ──
|
|
287
|
+
const approved = clone(AgentExecutionStatusSchema, afterGateA);
|
|
288
|
+
for (const m of approved.messages) {
|
|
289
|
+
for (const tc of m.toolCalls) {
|
|
290
|
+
if (tc.id === GATE_A_ID) tc.approvalAction = ApprovalAction.APPROVE;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ── Invocation 2: re-invoked after approval. The memory checkpointer replays
|
|
295
|
+
// from scratch (empty live checkpoint) and the mock serves gate B. ──
|
|
296
|
+
persistedStatuses.length = 0;
|
|
297
|
+
vi.mocked(performSetup).mockResolvedValueOnce(
|
|
298
|
+
fakeMemoryGateSetup({
|
|
299
|
+
gateId: GATE_B_ID,
|
|
300
|
+
gateName: "write_file",
|
|
301
|
+
filePath: "b.txt",
|
|
302
|
+
executionStatus: approved,
|
|
303
|
+
}),
|
|
304
|
+
);
|
|
305
|
+
await activities.ExecuteDeepAgent("exec-seq", "thread-seq");
|
|
306
|
+
|
|
307
|
+
const afterGateB = persistedStatuses.at(-1)!;
|
|
308
|
+
const ids = toolCallIds(afterGateB);
|
|
309
|
+
|
|
310
|
+
// Gate B is the new pending approval.
|
|
311
|
+
expect(afterGateB.phase).toBe(ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL);
|
|
312
|
+
expect(ids.filter(id => id === GATE_B_ID)).toHaveLength(1);
|
|
313
|
+
|
|
314
|
+
// The regression guard: gate A's committed tool call survives the resume.
|
|
315
|
+
// Dropping it lets the server guard reject the update and project pending=0,
|
|
316
|
+
// which auto-resumes the workflow and silently skips gate B.
|
|
317
|
+
expect(ids).toContain(GATE_A_ID);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
/** A status that is a strict superset of the persisted transcript is what the
|
|
321
|
+
* server's append-only-at-identity guard accepts (no committed id dropped). */
|
|
322
|
+
it("emits a gate-B transcript that is a superset of the persisted gate-A transcript", async () => {
|
|
323
|
+
const activities = createDeepAgentActivities(memoryConfig);
|
|
324
|
+
|
|
325
|
+
const approved = approvedGateAStatus();
|
|
326
|
+
const priorIds = toolCallIds(approved);
|
|
327
|
+
|
|
328
|
+
vi.mocked(performSetup).mockResolvedValueOnce(
|
|
329
|
+
fakeMemoryGateSetup({
|
|
330
|
+
gateId: GATE_B_ID,
|
|
331
|
+
gateName: "write_file",
|
|
332
|
+
filePath: "b.txt",
|
|
333
|
+
executionStatus: approved,
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
await activities.ExecuteDeepAgent("exec-seq", "thread-seq");
|
|
337
|
+
|
|
338
|
+
const afterGateB = persistedStatuses.at(-1)!;
|
|
339
|
+
const ids = new Set(toolCallIds(afterGateB));
|
|
340
|
+
|
|
341
|
+
// Every previously-committed tool-call id is still present (no drop), and the
|
|
342
|
+
// transcript did not shrink — exactly the two regressions the server guard
|
|
343
|
+
// rejects (update_status.go nonTerminalTranscriptRegression).
|
|
344
|
+
for (const id of priorIds) {
|
|
345
|
+
expect(ids.has(id)).toBe(true);
|
|
346
|
+
}
|
|
347
|
+
expect(afterGateB.messages.length).toBeGreaterThanOrEqual(approved.messages.length);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the deep-agent turn-boundary flowed-row stamping
|
|
3
|
+
* (`stamp-flowed-rows.ts`), with emphasis on the sub-agent path added by the
|
|
4
|
+
* DD-24 follow-up: sub-agent edit rows fold into the parent turn's change set,
|
|
5
|
+
* so they carry the parent change set id, scoped to this turn by tool-call-id
|
|
6
|
+
* novelty (they lack the already-stamped/hidden shields the top-level pass has).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect } from "vitest";
|
|
10
|
+
import { create } from "@bufbuild/protobuf";
|
|
11
|
+
import {
|
|
12
|
+
AgentMessageSchema,
|
|
13
|
+
ToolCallSchema,
|
|
14
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
15
|
+
import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
16
|
+
import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
17
|
+
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
18
|
+
import { collectSubAgentToolCallIds } from "../../../shared/tool-row.js";
|
|
19
|
+
import {
|
|
20
|
+
stampFlowedFileEditRows,
|
|
21
|
+
stampFlowedSubAgentFileEditRows,
|
|
22
|
+
} from "../stamp-flowed-rows.js";
|
|
23
|
+
|
|
24
|
+
const CHANGE_SET_ID = "exec-1:0";
|
|
25
|
+
|
|
26
|
+
/** A streamed (COMPLETED) file-edit tool call, as the deep-agent would record. */
|
|
27
|
+
function editMessage(id: string, path: string, name = "write_file"): AgentMessage {
|
|
28
|
+
return create(AgentMessageSchema, {
|
|
29
|
+
type: 1, // MESSAGE_AI
|
|
30
|
+
toolCalls: [
|
|
31
|
+
create(ToolCallSchema, {
|
|
32
|
+
id,
|
|
33
|
+
name,
|
|
34
|
+
status: ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
35
|
+
args: { file_path: path, content: "x" },
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function subAgent(subId: string, ...messages: AgentMessage[]) {
|
|
42
|
+
return create(SubAgentExecutionSchema, { id: subId, name: "code_editor", messages });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("stampFlowedFileEditRows (deep-agent)", () => {
|
|
46
|
+
it("stamps a flowed write/delete row and leaves a non-file row untouched", () => {
|
|
47
|
+
const write = editMessage("tc-1", "src/a.ts", "write_file");
|
|
48
|
+
const shell = create(AgentMessageSchema, {
|
|
49
|
+
type: 1,
|
|
50
|
+
toolCalls: [create(ToolCallSchema, { id: "tc-2", name: "shell", status: ToolCallStatus.TOOL_CALL_COMPLETED })],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
stampFlowedFileEditRows([write, shell], CHANGE_SET_ID);
|
|
54
|
+
|
|
55
|
+
expect(write.toolCalls[0].fileChangeSetId).toBe(CHANGE_SET_ID);
|
|
56
|
+
expect(shell.toolCalls[0].fileChangeSetId).toBe("");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("skips a tool-call id in skipToolCallIds (current-turn scoping)", () => {
|
|
60
|
+
const prior = editMessage("tc-prior", "src/old.ts");
|
|
61
|
+
const current = editMessage("tc-current", "src/new.ts");
|
|
62
|
+
|
|
63
|
+
stampFlowedFileEditRows([prior, current], CHANGE_SET_ID, new Set(["tc-prior"]));
|
|
64
|
+
|
|
65
|
+
expect(prior.toolCalls[0].fileChangeSetId).toBe(""); // skipped
|
|
66
|
+
expect(current.toolCalls[0].fileChangeSetId).toBe(CHANGE_SET_ID);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("never overwrites an already-stamped row", () => {
|
|
70
|
+
const row = editMessage("tc-1", "src/a.ts");
|
|
71
|
+
row.toolCalls[0].fileChangeSetId = "exec-1:prior";
|
|
72
|
+
|
|
73
|
+
stampFlowedFileEditRows([row], CHANGE_SET_ID);
|
|
74
|
+
|
|
75
|
+
expect(row.toolCalls[0].fileChangeSetId).toBe("exec-1:prior");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("stampFlowedSubAgentFileEditRows (deep-agent)", () => {
|
|
80
|
+
it("stamps a current-turn sub-agent's edit row with the parent change set id", () => {
|
|
81
|
+
const sub = subAgent("sa-1", editMessage("sa-tc-1", "src/sub.ts"));
|
|
82
|
+
|
|
83
|
+
stampFlowedSubAgentFileEditRows([sub], CHANGE_SET_ID, new Set());
|
|
84
|
+
|
|
85
|
+
expect(sub.messages[0].toolCalls[0].fileChangeSetId).toBe(CHANGE_SET_ID);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("stamps a fresh continuation row of a spanning sub-agent, not its prior row", () => {
|
|
89
|
+
// A sub-agent that paused on an internal tool gate spans invocations: its
|
|
90
|
+
// sub-agent id and its earlier row pre-exist on resume, but the continuation
|
|
91
|
+
// edit row carries a fresh tool-call id and belongs to the completing turn.
|
|
92
|
+
const priorRow = editMessage("sa-tc-old", "src/before.ts");
|
|
93
|
+
const spanning = subAgent("sa-span", priorRow);
|
|
94
|
+
const priorToolCallIds = collectSubAgentToolCallIds([spanning]); // {sa-tc-old}
|
|
95
|
+
|
|
96
|
+
// On resume the same sub-agent appends a new edit row.
|
|
97
|
+
spanning.messages.push(editMessage("sa-tc-new", "src/after.ts"));
|
|
98
|
+
|
|
99
|
+
stampFlowedSubAgentFileEditRows([spanning], "exec-1:1", priorToolCallIds);
|
|
100
|
+
|
|
101
|
+
expect(spanning.messages[0].toolCalls[0].fileChangeSetId).toBe(""); // prior row untouched
|
|
102
|
+
expect(spanning.messages[1].toolCalls[0].fileChangeSetId).toBe("exec-1:1"); // continuation stamped
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("withholds content for a sub-agent write to a tracked secret-like path (DD-12 D4 inherited)", () => {
|
|
106
|
+
const sub = subAgent("sa-1", editMessage("sa-tc-secret", "config/credentials.json"));
|
|
107
|
+
|
|
108
|
+
stampFlowedSubAgentFileEditRows([sub], CHANGE_SET_ID, new Set());
|
|
109
|
+
|
|
110
|
+
const row = sub.messages[0].toolCalls[0];
|
|
111
|
+
expect(row.fileChangeSetId).toBe(CHANGE_SET_ID); // stamped (path visible)
|
|
112
|
+
// stampFileEditRow normalizes the withheld row to `{ path }` (see tool-row.ts).
|
|
113
|
+
expect(row.args).toEqual({ path: "config/credentials.json" }); // body withheld
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("does nothing for no sub-agents", () => {
|
|
117
|
+
expect(() => stampFlowedSubAgentFileEditRows([], CHANGE_SET_ID, new Set())).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -384,7 +384,6 @@ describe("StatusBuilder", () => {
|
|
|
384
384
|
kind: 0,
|
|
385
385
|
sizeBytes: 100n,
|
|
386
386
|
storageKey: "artifacts/exec-1/report.txt",
|
|
387
|
-
downloadUrl: "http://localhost/dl",
|
|
388
387
|
createdAt: "2026-05-19T12:00:00Z",
|
|
389
388
|
expiresAt: "",
|
|
390
389
|
entries: [],
|
|
@@ -405,7 +404,6 @@ describe("StatusBuilder", () => {
|
|
|
405
404
|
kind: 0,
|
|
406
405
|
sizeBytes: 10n,
|
|
407
406
|
storageKey: "k",
|
|
408
|
-
downloadUrl: "u",
|
|
409
407
|
createdAt: "",
|
|
410
408
|
expiresAt: "",
|
|
411
409
|
entries: [],
|
|
@@ -429,7 +427,6 @@ describe("StatusBuilder", () => {
|
|
|
429
427
|
kind: 0,
|
|
430
428
|
sizeBytes: 10n,
|
|
431
429
|
storageKey: "k",
|
|
432
|
-
downloadUrl: "u",
|
|
433
430
|
createdAt: "",
|
|
434
431
|
expiresAt: "",
|
|
435
432
|
entries: [],
|
|
@@ -547,7 +544,7 @@ describe("StatusBuilder", () => {
|
|
|
547
544
|
return {
|
|
548
545
|
policies: new Map(),
|
|
549
546
|
toolServerMap: new Map(),
|
|
550
|
-
|
|
547
|
+
globalBypass: false,
|
|
551
548
|
...overrides,
|
|
552
549
|
};
|
|
553
550
|
}
|
|
@@ -564,6 +561,7 @@ describe("StatusBuilder", () => {
|
|
|
564
561
|
mcpServerSlug: serverSlug,
|
|
565
562
|
requiresApproval: true,
|
|
566
563
|
approvalMessage: message,
|
|
564
|
+
source: "classifier_default",
|
|
567
565
|
},
|
|
568
566
|
];
|
|
569
567
|
}
|
|
@@ -600,13 +598,13 @@ describe("StatusBuilder", () => {
|
|
|
600
598
|
expect(sb.currentStatus.phase).toBe(ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL);
|
|
601
599
|
});
|
|
602
600
|
|
|
603
|
-
it("does not require approval
|
|
601
|
+
it("does not require approval under the global bypass (spec.auto_approve_all)", () => {
|
|
604
602
|
const sb = makeBuilder();
|
|
605
603
|
const [key, policy] = policyFor("github", "delete_repo");
|
|
606
604
|
sb.setApprovalProvider(makeApprovalProvider({
|
|
607
605
|
policies: new Map([[key, policy]]),
|
|
608
606
|
toolServerMap: new Map([["delete_repo", "github"]]),
|
|
609
|
-
|
|
607
|
+
globalBypass: true,
|
|
610
608
|
}));
|
|
611
609
|
|
|
612
610
|
sb.processEvent(chatStreamEvent("run-1", "deleting"));
|
|
@@ -617,11 +615,11 @@ describe("StatusBuilder", () => {
|
|
|
617
615
|
expect(tc.requiresApproval).toBe(false);
|
|
618
616
|
});
|
|
619
617
|
|
|
620
|
-
it("sets mcpServerSlug even
|
|
618
|
+
it("sets mcpServerSlug even under the global bypass (spec.auto_approve_all)", () => {
|
|
621
619
|
const sb = makeBuilder();
|
|
622
620
|
sb.setApprovalProvider(makeApprovalProvider({
|
|
623
621
|
toolServerMap: new Map([["echo", "test-mcp-server"]]),
|
|
624
|
-
|
|
622
|
+
globalBypass: true,
|
|
625
623
|
}));
|
|
626
624
|
|
|
627
625
|
sb.processEvent(chatStreamEvent("run-1", "echoing"));
|
|
@@ -718,10 +716,11 @@ describe("StatusBuilder", () => {
|
|
|
718
716
|
mcpServerSlug: serverSlug,
|
|
719
717
|
requiresApproval: true,
|
|
720
718
|
approvalMessage: "Approve?",
|
|
719
|
+
source: "classifier_default",
|
|
721
720
|
}],
|
|
722
721
|
]),
|
|
723
722
|
toolServerMap: new Map([[toolName, serverSlug]]),
|
|
724
|
-
|
|
723
|
+
globalBypass: false,
|
|
725
724
|
});
|
|
726
725
|
return sb;
|
|
727
726
|
}
|
|
@@ -1571,10 +1570,11 @@ describe("StatusBuilder", () => {
|
|
|
1571
1570
|
mcpServerSlug: "github",
|
|
1572
1571
|
requiresApproval: true,
|
|
1573
1572
|
approvalMessage: "Create PR '{{args.title}}' in {{args.repo}}?",
|
|
1573
|
+
source: "classifier_default",
|
|
1574
1574
|
}],
|
|
1575
1575
|
]),
|
|
1576
1576
|
toolServerMap: new Map([["create_pull_request", "github"]]),
|
|
1577
|
-
|
|
1577
|
+
globalBypass: false,
|
|
1578
1578
|
});
|
|
1579
1579
|
|
|
1580
1580
|
// Model streams text then calls the approval-gated tool
|
|
@@ -1815,10 +1815,11 @@ describe("StatusBuilder", () => {
|
|
|
1815
1815
|
mcpServerSlug: "my-server",
|
|
1816
1816
|
requiresApproval: true,
|
|
1817
1817
|
approvalMessage: "Execute dangerous_tool",
|
|
1818
|
+
source: "classifier_default",
|
|
1818
1819
|
}],
|
|
1819
1820
|
]),
|
|
1820
1821
|
toolServerMap: new Map([["dangerous_tool", "my-server"]]),
|
|
1821
|
-
|
|
1822
|
+
globalBypass: false,
|
|
1822
1823
|
});
|
|
1823
1824
|
return sb;
|
|
1824
1825
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { makeInMemoryArtifactStorage } from "../../../__test-utils__/fake-artifact-storage.js";
|
|
2
3
|
import { create } from "@bufbuild/protobuf";
|
|
3
4
|
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
4
5
|
import { streamExecutionV3 } from "../streaming-v3.js";
|
|
@@ -531,14 +532,13 @@ describe("streamExecutionV3", () => {
|
|
|
531
532
|
// faithfully (no truncation), and the persist chokepoint offloads the image
|
|
532
533
|
// to artifact storage so the persisted ToolCall carries outputRef.isImage.
|
|
533
534
|
const uploads: { key: string; size: number; contentType?: string }[] = [];
|
|
534
|
-
const artifactStorage = {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
} as any;
|
|
535
|
+
const { storage: artifactStorage, blobs } = makeInMemoryArtifactStorage({ urlBase: "https://artifacts.local/" });
|
|
536
|
+
artifactStorage.exists.mockResolvedValue(true);
|
|
537
|
+
artifactStorage.upload.mockImplementation(async (key: string, content: Buffer, contentType?: string) => {
|
|
538
|
+
uploads.push({ key, size: content.length, contentType });
|
|
539
|
+
blobs.set(key, Buffer.from(content));
|
|
540
|
+
return key;
|
|
541
|
+
});
|
|
542
542
|
|
|
543
543
|
const base64 = Buffer.from("PNGBYTES".repeat(64)).toString("base64");
|
|
544
544
|
const imageEnvelope = {
|
|
@@ -587,7 +587,7 @@ describe("streamExecutionV3", () => {
|
|
|
587
587
|
expect(refTc).toBeDefined();
|
|
588
588
|
expect(refTc.outputRef.isImage).toBe(true);
|
|
589
589
|
expect(refTc.outputRef.mimeType).toBe("image/png");
|
|
590
|
-
expect(refTc.outputRef.
|
|
590
|
+
expect(refTc.outputRef.storageKey).toBe("artifacts/exec-v3-test/toolcalls/toolu_1.png");
|
|
591
591
|
// The base64 must not survive inline on the persisted result.
|
|
592
592
|
expect(refTc.result).not.toContain(base64);
|
|
593
593
|
expect(uploads).toHaveLength(1);
|