@stigmer/runner 3.15.1 → 3.15.2-dev.20260913223433
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/README.md +2 -4
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
- package/dist/__test-utils__/execution-record-fixture.js +4 -0
- package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
- package/dist/__test-utils__/file-review-projection.d.ts +40 -0
- package/dist/__test-utils__/file-review-projection.js +136 -0
- package/dist/__test-utils__/file-review-projection.js.map +1 -0
- package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
- package/dist/__test-utils__/harness-contract/types.js +15 -0
- package/dist/__test-utils__/harness-contract/types.js.map +1 -1
- package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
- package/dist/__test-utils__/hermetic-activity.js +52 -14
- package/dist/__test-utils__/hermetic-activity.js.map +1 -1
- package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
- package/dist/__test-utils__/model-registry-fixture.js +31 -7
- package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
- package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
- package/dist/__test-utils__/turn-input-fixture.js +1 -1
- package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
- package/dist/activities/call-agent.js +2 -2
- package/dist/activities/call-agent.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.d.ts +7 -5
- package/dist/activities/execute-cursor/adapter.js +7 -5
- package/dist/activities/execute-cursor/adapter.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
- package/dist/activities/execute-cursor/approval-policy.js +5 -2
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
- package/dist/activities/execute-cursor/approval-state.js +6 -3
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
- package/dist/activities/execute-cursor/cas-observations.js +19 -2
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
- package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
- package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
- package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
- package/dist/activities/execute-cursor/message-translator.js +19 -32
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
- package/dist/activities/execute-cursor/prompt-builder.js +76 -95
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
- package/dist/activities/execute-cursor/turn-boundary.js +30 -90
- package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
- package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
- package/dist/activities/execute-cursor/turn-settle.js +37 -49
- package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
- package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
- package/dist/activities/execute-cursor/turn-setup.js +29 -59
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
- package/dist/activities/execute-cursor/turn-stream.js +9 -22
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-cursor/turn.js +5 -1
- package/dist/activities/execute-cursor/turn.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
- package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
- package/dist/activities/execute-deep-agent/adapter.js +78 -0
- package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
- package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
- package/dist/activities/execute-deep-agent/execution-state.js +4 -12
- package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
- package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
- package/dist/activities/execute-deep-agent/hitl.js +77 -103
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
- package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
- package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
- package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
- package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
- package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
- package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
- package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
- package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
- package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
- package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
- package/dist/activities/execute-deep-agent/turn.js +116 -0
- package/dist/activities/execute-deep-agent/turn.js.map +1 -0
- package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
- package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
- package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
- package/dist/activities/execute-deep-agent/v3-events.js +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
- package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/harness/approval-decisions.d.ts +61 -0
- package/dist/harness/approval-decisions.js +102 -0
- package/dist/harness/approval-decisions.js.map +1 -0
- package/dist/harness/capabilities.d.ts +16 -5
- package/dist/harness/capabilities.js +9 -0
- package/dist/harness/capabilities.js.map +1 -1
- package/dist/harness/capture.d.ts +144 -0
- package/dist/harness/capture.js +219 -0
- package/dist/harness/capture.js.map +1 -0
- package/dist/harness/persist-chokepoint.d.ts +19 -4
- package/dist/harness/persist-chokepoint.js +17 -4
- package/dist/harness/persist-chokepoint.js.map +1 -1
- package/dist/harness/run-turn.js +115 -29
- package/dist/harness/run-turn.js.map +1 -1
- package/dist/harness/terminal-table.d.ts +40 -11
- package/dist/harness/terminal-table.js +58 -19
- package/dist/harness/terminal-table.js.map +1 -1
- package/dist/harness/turn-context.d.ts +141 -76
- package/dist/harness/turn-context.js +194 -99
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/harness/types.d.ts +65 -12
- package/dist/harness/types.js +8 -2
- package/dist/harness/types.js.map +1 -1
- package/dist/harness-adapters.d.ts +12 -8
- package/dist/harness-adapters.js +17 -9
- package/dist/harness-adapters.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +1 -1
- package/dist/middleware/approval-gate.js +1 -1
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +39 -0
- package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
- package/dist/middleware/cost-advisory.js.map +1 -0
- package/dist/middleware/index.d.ts +23 -12
- package/dist/middleware/index.js +26 -18
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/types.d.ts +3 -2
- package/dist/runner-manager.js +1 -5
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +1 -5
- package/dist/runner.js.map +1 -1
- package/dist/shared/attachment-resolver.d.ts +41 -18
- package/dist/shared/attachment-resolver.js +178 -46
- package/dist/shared/attachment-resolver.js.map +1 -1
- package/dist/shared/attachment-zip.d.ts +85 -0
- package/dist/shared/attachment-zip.js +185 -0
- package/dist/shared/attachment-zip.js.map +1 -0
- package/dist/shared/connect-backfill.js +5 -4
- package/dist/shared/connect-backfill.js.map +1 -1
- package/dist/shared/cost-guard.d.ts +3 -3
- package/dist/shared/cost-guard.js +3 -3
- package/dist/shared/execution-status-writer.d.ts +5 -5
- package/dist/shared/execution-status-writer.js +5 -5
- package/dist/shared/extract-structured-output.d.ts +8 -7
- package/dist/shared/extract-structured-output.js +8 -7
- package/dist/shared/extract-structured-output.js.map +1 -1
- package/dist/shared/filereview/cas-progress.d.ts +1 -18
- package/dist/shared/filereview/cas-progress.js +4 -0
- package/dist/shared/filereview/cas-progress.js.map +1 -1
- package/dist/shared/filereview/cas-touched.d.ts +56 -0
- package/dist/shared/filereview/cas-touched.js +63 -0
- package/dist/shared/filereview/cas-touched.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +2 -1
- package/dist/shared/filereview/index.js +2 -1
- package/dist/shared/filereview/index.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +4 -5
- package/dist/shared/mcp-resolver.js +4 -5
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/persist-decision.d.ts +3 -4
- package/dist/shared/persist-decision.js +3 -4
- package/dist/shared/persist-decision.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +2 -2
- package/dist/shared/plan-mode-permissions.js +2 -2
- package/dist/shared/plan-mode-prompt.d.ts +1 -1
- package/dist/shared/plan-mode-prompt.js +1 -1
- package/dist/shared/prompt-sections.d.ts +163 -0
- package/dist/shared/prompt-sections.js +164 -0
- package/dist/shared/prompt-sections.js.map +1 -0
- package/dist/shared/skill-resolver.d.ts +4 -2
- package/dist/shared/skill-resolver.js +4 -2
- package/dist/shared/skill-resolver.js.map +1 -1
- package/dist/shared/tool-rounds.d.ts +32 -0
- package/dist/shared/tool-rounds.js +43 -0
- package/dist/shared/tool-rounds.js.map +1 -1
- package/dist/shared/tool-row.d.ts +34 -0
- package/dist/shared/tool-row.js +52 -0
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/workspace/session-provision.d.ts +3 -3
- package/dist/shared/workspace/session-provision.js +3 -3
- package/dist/shared/workspace/stigmer-link.d.ts +3 -3
- package/dist/shared/workspace/stigmer-link.js +3 -3
- package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
- package/dist/shared/workspace/writeback-coordinator.js +16 -53
- package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +1 -1
- package/package.json +4 -4
- package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
- package/src/__test-utils__/execution-record-fixture.ts +18 -2
- package/src/__test-utils__/file-review-projection.ts +156 -0
- package/src/__test-utils__/git-workspace-fixture.ts +58 -0
- package/src/__test-utils__/harness-boot-order-child.ts +7 -2
- package/src/__test-utils__/harness-contract/contract.ts +33 -20
- package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
- package/src/__test-utils__/harness-contract/types.ts +27 -3
- package/src/__test-utils__/hermetic-activity.ts +52 -14
- package/src/__test-utils__/model-registry-fixture.ts +32 -7
- package/src/__test-utils__/module-specifiers.ts +24 -0
- package/src/__test-utils__/turn-input-fixture.ts +2 -2
- package/src/__tests__/harness-boot-order.test.ts +9 -2
- package/src/activities/call-agent.ts +2 -2
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
- package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
- package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
- package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
- package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
- package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
- package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
- package/src/activities/execute-cursor/adapter.ts +7 -5
- package/src/activities/execute-cursor/approval-policy.ts +5 -2
- package/src/activities/execute-cursor/approval-state.ts +16 -8
- package/src/activities/execute-cursor/cas-observations.ts +20 -2
- package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
- package/src/activities/execute-cursor/message-translator.ts +29 -41
- package/src/activities/execute-cursor/prompt-builder.ts +99 -157
- package/src/activities/execute-cursor/turn-boundary.ts +34 -140
- package/src/activities/execute-cursor/turn-settle.ts +37 -51
- package/src/activities/execute-cursor/turn-setup.ts +30 -70
- package/src/activities/execute-cursor/turn-stream.ts +8 -31
- package/src/activities/execute-cursor/turn.ts +5 -1
- package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
- package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
- package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
- package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
- package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
- package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
- package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
- package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
- package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
- package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
- package/src/activities/execute-deep-agent/adapter.ts +94 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
- package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
- package/src/activities/execute-deep-agent/execution-state.ts +4 -13
- package/src/activities/execute-deep-agent/hitl.ts +100 -123
- package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
- package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
- package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
- package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
- package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
- package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
- package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
- package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
- package/src/activities/execute-deep-agent/turn.ts +131 -0
- package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
- package/src/activities/execute-deep-agent/v3-events.ts +1 -1
- package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
- package/src/harness/__tests__/approval-decisions.test.ts +116 -0
- package/src/harness/__tests__/capture.test.ts +268 -0
- package/src/harness/__tests__/mount-skills.test.ts +169 -0
- package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
- package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
- package/src/harness/__tests__/run-turn.test.ts +39 -23
- package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
- package/src/harness/__tests__/turn-context.test.ts +73 -26
- package/src/harness/approval-decisions.ts +104 -0
- package/src/harness/capabilities.ts +16 -5
- package/src/harness/capture.ts +301 -0
- package/src/harness/persist-chokepoint.ts +24 -4
- package/src/harness/run-turn.ts +120 -29
- package/src/harness/terminal-table.ts +61 -20
- package/src/harness/turn-context.ts +208 -111
- package/src/harness/types.ts +66 -12
- package/src/harness-adapters.ts +17 -9
- package/src/middleware/__tests__/approval-gate.test.ts +1 -1
- package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
- package/src/middleware/approval-gate.ts +2 -2
- package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
- package/src/middleware/index.ts +29 -29
- package/src/middleware/types.ts +3 -2
- package/src/runner-manager.ts +0 -8
- package/src/runner.ts +0 -8
- package/src/shared/__tests__/attachment-naming.test.ts +6 -19
- package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
- package/src/shared/__tests__/attachment-zip.test.ts +236 -0
- package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
- package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
- package/src/shared/__tests__/cost-guard.test.ts +3 -3
- package/src/shared/__tests__/prompt-sections.test.ts +167 -0
- package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
- package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
- package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
- package/src/shared/attachment-resolver.ts +218 -55
- package/src/shared/attachment-zip.ts +281 -0
- package/src/shared/connect-backfill.ts +9 -11
- package/src/shared/cost-guard.ts +3 -3
- package/src/shared/execution-status-writer.ts +5 -5
- package/src/shared/extract-structured-output.ts +8 -7
- package/src/shared/filereview/__tests__/capture.test.ts +86 -4
- package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
- package/src/shared/filereview/cas-progress.ts +5 -19
- package/src/shared/filereview/cas-touched.ts +89 -0
- package/src/shared/filereview/index.ts +4 -2
- package/src/shared/mcp-resolver.ts +4 -5
- package/src/shared/persist-decision.ts +3 -4
- package/src/shared/plan-mode-permissions.ts +2 -2
- package/src/shared/plan-mode-prompt.ts +1 -1
- package/src/shared/prompt-sections.ts +256 -0
- package/src/shared/skill-resolver.ts +4 -2
- package/src/shared/tool-rounds.ts +48 -0
- package/src/shared/tool-row.ts +61 -0
- package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
- package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
- package/src/shared/workspace/session-provision.ts +3 -3
- package/src/shared/workspace/stigmer-link.ts +3 -3
- package/src/shared/workspace/writeback-coordinator.ts +16 -64
- package/src/tools/index.ts +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
- package/dist/activities/execute-cursor/capture-flow.js +0 -285
- package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
- package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
- package/dist/activities/execute-cursor/command-provenance.js +0 -38
- package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
- package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
- package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
- package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
- package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
- package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
- package/dist/activities/execute-deep-agent/environment.js +0 -58
- package/dist/activities/execute-deep-agent/environment.js.map +0 -1
- package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
- package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
- package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
- package/dist/activities/execute-deep-agent/index.d.ts +0 -15
- package/dist/activities/execute-deep-agent/index.js +0 -918
- package/dist/activities/execute-deep-agent/index.js.map +0 -1
- package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
- package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
- package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
- package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
- package/dist/activities/execute-deep-agent/post-stream.js +0 -71
- package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
- package/dist/activities/execute-deep-agent/setup.js +0 -767
- package/dist/activities/execute-deep-agent/setup.js.map +0 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
- package/dist/activities/execute-deep-agent/status-builder.js +0 -365
- package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
- package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
- package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
- package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
- package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
- package/dist/activities/execute-deep-agent/streaming.js +0 -160
- package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
- package/dist/middleware/cost-cap.d.ts +0 -22
- package/dist/middleware/cost-cap.js.map +0 -1
- package/dist/middleware/graceful-stop.d.ts +0 -17
- package/dist/middleware/graceful-stop.js +0 -63
- package/dist/middleware/graceful-stop.js.map +0 -1
- package/dist/shared/skill-writer.d.ts +0 -75
- package/dist/shared/skill-writer.js +0 -207
- package/dist/shared/skill-writer.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
- package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
- package/src/activities/execute-cursor/capture-flow.ts +0 -379
- package/src/activities/execute-cursor/command-provenance.ts +0 -73
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
- package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
- package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
- package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
- package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
- package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
- package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
- package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
- package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
- package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
- package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
- package/src/activities/execute-deep-agent/environment.ts +0 -76
- package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
- package/src/activities/execute-deep-agent/index.ts +0 -1072
- package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
- package/src/activities/execute-deep-agent/post-stream.ts +0 -109
- package/src/activities/execute-deep-agent/setup.ts +0 -1081
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
- package/src/activities/execute-deep-agent/status-builder.ts +0 -481
- package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
- package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
- package/src/activities/execute-deep-agent/streaming.ts +0 -313
- package/src/middleware/__tests__/cost-cap.test.ts +0 -192
- package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
- package/src/middleware/graceful-stop.ts +0 -86
- package/src/shared/__tests__/skill-writer.test.ts +0 -372
- package/src/shared/skill-writer.ts +0 -266
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: two SEQUENTIAL approval gates — three invocations on the
|
|
3
|
+
* durable (sqlite) checkpointer.
|
|
4
|
+
*
|
|
5
|
+
* Turn 1 opens exactly as every gated arm does (the shared
|
|
6
|
+
* `gate.turn1.status.json`): gated `execute` A, WAITING_FOR_APPROVAL.
|
|
7
|
+
* APPROVE. Turn 2 resumes inside gate A, runs A, and the model proposes
|
|
8
|
+
* gated `execute` B: the graph pauses again and the activity persists
|
|
9
|
+
* WAITING_FOR_APPROVAL with BOTH rows — A COMPLETED (its committed identity
|
|
10
|
+
* and result intact), B WAITING. APPROVE. Turn 3 resumes inside gate B, runs
|
|
11
|
+
* B, the model closes, COMPLETED with both rows COMPLETED and each decision
|
|
12
|
+
* recorded on its own row.
|
|
13
|
+
*
|
|
14
|
+
* Invariant carried from the retired `sequential-gate-resume.test.ts`: the
|
|
15
|
+
* status persisted at gate B must hold gate A's already-committed tool-call
|
|
16
|
+
* row — a status rebuilt from scratch that dropped it would trip the server's
|
|
17
|
+
* append-only-at-identity guard and strand the execution in
|
|
18
|
+
* WAITING_FOR_APPROVAL with zero pending approvals. That test modelled the
|
|
19
|
+
* memory checkpointer's replay through a mocked graph. On the real graph the
|
|
20
|
+
* memory posture (a test-only opt-in; `config.ts` resolves `sqlite` locally
|
|
21
|
+
* and `http` in cloud) re-interrupts the same call on replay and duplicates
|
|
22
|
+
* the row (S3 M0 finding F-M0-3), so the invariant is recorded on the durable
|
|
23
|
+
* posture, where the reinvocation resumes instead of replaying.
|
|
24
|
+
*
|
|
25
|
+
* What every reinvocation golden here shows since S3 M2a (the runtime over
|
|
26
|
+
* the native adapter; Q-S3-16 and Q-M2a-2): `startedAt` is THIS turn's own,
|
|
27
|
+
* not turn 1's carried by the orchestrator's whole-status clone; the
|
|
28
|
+
* `fileReviewEventStream` carries only the events THIS turn authored (the
|
|
29
|
+
* runtime seeds messages, sub-agents, artifacts, write-backs, todos, usage,
|
|
30
|
+
* the memory report and the structured output — never the server-owned
|
|
31
|
+
* ledgers, which the server appends to by event id in `update-status.ts`
|
|
32
|
+
* `appendRunnerEvents`; the hermetic record replaces wholesale, so a
|
|
33
|
+
* golden reads as what the runner SENT); and `streamingUsage` carries the
|
|
34
|
+
* runtime accountant's fields (`model`, `estimatedCostUsd`, the requested
|
|
35
|
+
* tier and thinking mode).
|
|
36
|
+
*
|
|
37
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
38
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
42
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
43
|
+
import {
|
|
44
|
+
AgentExecutionStatusSchema,
|
|
45
|
+
type AgentExecutionStatus,
|
|
46
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
47
|
+
import {
|
|
48
|
+
ApprovalAction,
|
|
49
|
+
ExecutionPhase,
|
|
50
|
+
ToolCallStatus,
|
|
51
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
52
|
+
|
|
53
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
54
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
55
|
+
);
|
|
56
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
57
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
import {
|
|
61
|
+
ScriptedClock,
|
|
62
|
+
createHermeticEnvironment,
|
|
63
|
+
type HermeticEnvironment,
|
|
64
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
65
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
66
|
+
import {
|
|
67
|
+
beginDeepAgentScenario,
|
|
68
|
+
deepAgentExecutionRecord,
|
|
69
|
+
runDeepAgentTurn,
|
|
70
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
71
|
+
import {
|
|
72
|
+
CLOSING_TURN,
|
|
73
|
+
EXECUTE_CALL_A,
|
|
74
|
+
EXECUTE_CALL_B,
|
|
75
|
+
GATED_OPENING_TURN,
|
|
76
|
+
GATE_TURN1_GOLDEN,
|
|
77
|
+
executeApprovalMessage,
|
|
78
|
+
} from "../../__test-utils__/hitl-script.js";
|
|
79
|
+
|
|
80
|
+
const USER_MESSAGE = "Run the command for me.";
|
|
81
|
+
const DECIDED_A_AT = "2026-01-01T00:00:30.000Z";
|
|
82
|
+
const DECIDED_B_AT = "2026-01-01T00:01:00.000Z";
|
|
83
|
+
|
|
84
|
+
function statusJson(status: AgentExecutionStatus): string {
|
|
85
|
+
return JSON.stringify(toJson(AgentExecutionStatusSchema, status), null, 2) + "\n";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe("ExecuteDeepAgent hermetic — two sequential gates (sqlite)", () => {
|
|
89
|
+
let env: HermeticEnvironment;
|
|
90
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
91
|
+
const clock = new ScriptedClock();
|
|
92
|
+
|
|
93
|
+
beforeAll(() => {
|
|
94
|
+
env = createHermeticEnvironment();
|
|
95
|
+
registry = stubRegistryFetch();
|
|
96
|
+
clock.install();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
afterAll(() => {
|
|
100
|
+
clock.uninstall();
|
|
101
|
+
registry.restore();
|
|
102
|
+
env.dispose();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("keeps gate A's committed row through gate B's pause and completes after both approvals", async () => {
|
|
106
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
107
|
+
const record = deepAgentExecutionRecord({ message: USER_MESSAGE });
|
|
108
|
+
const scenario = beginDeepAgentScenario({
|
|
109
|
+
env,
|
|
110
|
+
clock,
|
|
111
|
+
record,
|
|
112
|
+
checkpointer: "sqlite",
|
|
113
|
+
script: () => ({
|
|
114
|
+
turns: [
|
|
115
|
+
GATED_OPENING_TURN,
|
|
116
|
+
{ text: "Now the second command.", toolCalls: [EXECUTE_CALL_B], usage: { inputTokens: 1_600, outputTokens: 40 } },
|
|
117
|
+
CLOSING_TURN,
|
|
118
|
+
],
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ── Turn 1: gate A ───────────────────────────────────────────────────────
|
|
123
|
+
const turn1 = await runDeepAgentTurn(scenario, { turnSeq: 0 });
|
|
124
|
+
expect(turn1.outcome.kind).toBe("returned");
|
|
125
|
+
expect(record.waitingToolCalls().map((tc) => tc.id)).toEqual([EXECUTE_CALL_A.id]);
|
|
126
|
+
const atGateA = record.lastFullStatus!;
|
|
127
|
+
await expect(statusJson(atGateA), "the same opening as every gated arm").toMatchFileSnapshot(GATE_TURN1_GOLDEN);
|
|
128
|
+
expect(record.decideWaitingToolCalls(ApprovalAction.APPROVE, DECIDED_A_AT)).toBe(1);
|
|
129
|
+
|
|
130
|
+
// ── Turn 2: A runs, gate B pauses ────────────────────────────────────────
|
|
131
|
+
const turn2 = await runDeepAgentTurn(scenario, { turnSeq: 1 });
|
|
132
|
+
expect(turn2.outcome.kind).toBe("returned");
|
|
133
|
+
expect((turn2.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_WAITING_FOR_APPROVAL");
|
|
134
|
+
const atGateB = record.lastFullStatus!;
|
|
135
|
+
const rowsAtB = atGateB.messages.flatMap((m) => m.toolCalls);
|
|
136
|
+
expect(rowsAtB.map((tc) => [tc.id, tc.status]), "A committed and kept, B waiting").toEqual([
|
|
137
|
+
[EXECUTE_CALL_A.id, ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
138
|
+
[EXECUTE_CALL_B.id, ToolCallStatus.TOOL_CALL_WAITING_APPROVAL],
|
|
139
|
+
]);
|
|
140
|
+
expect(rowsAtB[0].result).toContain("hermetic-a");
|
|
141
|
+
expect(rowsAtB[0].approvalAction).toBe(ApprovalAction.APPROVE);
|
|
142
|
+
expect(rowsAtB[1].approvalMessage).toBe(executeApprovalMessage(EXECUTE_CALL_B));
|
|
143
|
+
expect(record.waitingToolCalls().map((tc) => tc.id), "exactly one pending approval").toEqual([EXECUTE_CALL_B.id]);
|
|
144
|
+
// The gate-B transcript is a superset of the gate-A one: every id, every message, in order.
|
|
145
|
+
const gateAIds = atGateA.messages.flatMap((m) => m.toolCalls).map((tc) => tc.id);
|
|
146
|
+
expect(rowsAtB.map((tc) => tc.id).slice(0, gateAIds.length)).toEqual(gateAIds);
|
|
147
|
+
expect(atGateB.messages.length).toBeGreaterThanOrEqual(atGateA.messages.length);
|
|
148
|
+
await expect(statusJson(atGateB)).toMatchFileSnapshot("./goldens/sequential-gates.turn2.status.json");
|
|
149
|
+
expect(record.decideWaitingToolCalls(ApprovalAction.APPROVE, DECIDED_B_AT)).toBe(1);
|
|
150
|
+
|
|
151
|
+
// ── Turn 3: B runs, the run completes ────────────────────────────────────
|
|
152
|
+
const turn3 = await runDeepAgentTurn(scenario, { turnSeq: 2 });
|
|
153
|
+
expect(turn3.outcome.kind).toBe("returned");
|
|
154
|
+
expect((turn3.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
155
|
+
expect(record.persistedPhases.filter((p) => p === ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL)).toHaveLength(2);
|
|
156
|
+
const final = record.lastFullStatus!;
|
|
157
|
+
const rows = final.messages.flatMap((m) => m.toolCalls);
|
|
158
|
+
expect(rows.map((tc) => [tc.id, tc.status, tc.approvalAction, tc.approvalDecidedAt])).toEqual([
|
|
159
|
+
[EXECUTE_CALL_A.id, ToolCallStatus.TOOL_CALL_COMPLETED, ApprovalAction.APPROVE, DECIDED_A_AT],
|
|
160
|
+
[EXECUTE_CALL_B.id, ToolCallStatus.TOOL_CALL_COMPLETED, ApprovalAction.APPROVE, DECIDED_B_AT],
|
|
161
|
+
]);
|
|
162
|
+
expect(rows[1].result).toContain("hermetic-b");
|
|
163
|
+
|
|
164
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
165
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
166
|
+
|
|
167
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
168
|
+
await expect(statusJson(final)).toMatchFileSnapshot("./goldens/sequential-gates.turn3.status.json");
|
|
169
|
+
});
|
|
170
|
+
});
|
package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: an INLINE-PUBLISHED ARTIFACT survives a HITL gate — the
|
|
3
|
+
* arbiter for Q-S3-16 (the seed's breadth), two invocations on the durable
|
|
4
|
+
* (sqlite) checkpointer.
|
|
5
|
+
*
|
|
6
|
+
* Turn 1: the model writes a report (`write_file`; under capture mode in a
|
|
7
|
+
* non-git session workspace the write FLOWS into the CAS ledger), and
|
|
8
|
+
* `streaming-side-effects.ts` hands the path to `InlinePublisher`, which
|
|
9
|
+
* uploads the bytes to artifact storage and appends an `ExecutionArtifact`
|
|
10
|
+
* (`storageKey = artifacts/<executionId>/<name>`, a content hash, the
|
|
11
|
+
* scripted clock) to the status — a mid-turn publish the Cursor harness never
|
|
12
|
+
* performs. The model then proposes a gated `execute`; WAITING_FOR_APPROVAL.
|
|
13
|
+
*
|
|
14
|
+
* Turn 2: after APPROVE, the reinvocation seeds its status from the persisted
|
|
15
|
+
* one. On this harness `seedStatusFromExecution` clones the WHOLE status, so
|
|
16
|
+
* the artifact list arrives intact and the run's final status still lists the
|
|
17
|
+
* report. The server replaces `artifacts` wholesale whenever the request's
|
|
18
|
+
* list is non-empty (`update-status.ts`), so a runtime that seeded messages
|
|
19
|
+
* only and then published nothing new would carry an empty list — and the
|
|
20
|
+
* server would keep the old one, but a runtime that published one NEW
|
|
21
|
+
* artifact from an empty list would ERASE the prior one. Q-S3-16's
|
|
22
|
+
* `seedFromPersistedStatus` carries `artifacts` for exactly this reason;
|
|
23
|
+
* this pair of goldens (`inline-artifact.turn1`, `inline-artifact.turn2`) is
|
|
24
|
+
* what proves it did.
|
|
25
|
+
*
|
|
26
|
+
* Also recorded, F-M0-8 (a shape fact for the owner): the write's tool row
|
|
27
|
+
* shows `requiresApproval: true` with `approvalPolicySource` BUILTIN_CATEGORY
|
|
28
|
+
* — the category policy's verdict — while the row is COMPLETED because
|
|
29
|
+
* capture mode let it flow; the artifact carries no URL (the local store's
|
|
30
|
+
* serve URL never enters the record).
|
|
31
|
+
*
|
|
32
|
+
* What every reinvocation golden here shows since S3 M2a (the runtime over
|
|
33
|
+
* the native adapter; Q-S3-16 and Q-M2a-2): `startedAt` is THIS turn's own,
|
|
34
|
+
* not turn 1's carried by the orchestrator's whole-status clone; the
|
|
35
|
+
* `fileReviewEventStream` carries only the events THIS turn authored (the
|
|
36
|
+
* runtime seeds messages, sub-agents, artifacts, write-backs, todos, usage,
|
|
37
|
+
* the memory report and the structured output — never the server-owned
|
|
38
|
+
* ledgers, which the server appends to by event id in `update-status.ts`
|
|
39
|
+
* `appendRunnerEvents`; the hermetic record replaces wholesale, so a
|
|
40
|
+
* golden reads as what the runner SENT); and `streamingUsage` carries the
|
|
41
|
+
* runtime accountant's fields (`model`, `estimatedCostUsd`, the requested
|
|
42
|
+
* tier and thinking mode).
|
|
43
|
+
*
|
|
44
|
+
* Golden hunk since S3 M4 (Q-M4-7, ruled Q-M4-12 A): turn 1's
|
|
45
|
+
* `fileChangeProgress` is the TRUE snapshot at the WAITING write
|
|
46
|
+
* (`filesChanged: 1`, `report.md` ADD +3, `capturedAt` t=2) where it was a
|
|
47
|
+
* stale t=0 snapshot taken by the stream loop before the engine wrote the
|
|
48
|
+
* file — no later mid-stream persist crossed the floor, so the pause used
|
|
49
|
+
* to persist "0 files" beside a one-file change set. The runtime now
|
|
50
|
+
* refreshes the strip on every write, the terminal one included.
|
|
51
|
+
*
|
|
52
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
53
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
import { existsSync } from "node:fs";
|
|
57
|
+
import { join } from "node:path";
|
|
58
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
59
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
60
|
+
import {
|
|
61
|
+
AgentExecutionStatusSchema,
|
|
62
|
+
type AgentExecutionStatus,
|
|
63
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
64
|
+
import {
|
|
65
|
+
ApprovalAction,
|
|
66
|
+
ExecutionPhase,
|
|
67
|
+
ToolCallStatus,
|
|
68
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
69
|
+
|
|
70
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
71
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
72
|
+
);
|
|
73
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
74
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
import {
|
|
78
|
+
ScriptedClock,
|
|
79
|
+
createHermeticEnvironment,
|
|
80
|
+
type HermeticEnvironment,
|
|
81
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
82
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
83
|
+
import {
|
|
84
|
+
FIXTURE,
|
|
85
|
+
beginDeepAgentScenario,
|
|
86
|
+
deepAgentExecutionRecord,
|
|
87
|
+
runDeepAgentTurn,
|
|
88
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
89
|
+
import { CLOSING_TURN, EXECUTE_CALL_A } from "../../__test-utils__/hitl-script.js";
|
|
90
|
+
|
|
91
|
+
const REPORT = "report.md";
|
|
92
|
+
const REPORT_BODY = "# Report\n\nAll fixtures nominal.\n";
|
|
93
|
+
const WRITE_CALL_ID = "call-hermetic-write-0001";
|
|
94
|
+
const DECIDED_AT = "2026-01-01T00:00:30.000Z";
|
|
95
|
+
|
|
96
|
+
function statusJson(status: AgentExecutionStatus): string {
|
|
97
|
+
return JSON.stringify(toJson(AgentExecutionStatusSchema, status), null, 2) + "\n";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe("ExecuteDeepAgent hermetic — inline artifact across a gate (sqlite)", () => {
|
|
101
|
+
let env: HermeticEnvironment;
|
|
102
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
103
|
+
const clock = new ScriptedClock();
|
|
104
|
+
|
|
105
|
+
beforeAll(() => {
|
|
106
|
+
env = createHermeticEnvironment();
|
|
107
|
+
registry = stubRegistryFetch();
|
|
108
|
+
clock.install();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
afterAll(() => {
|
|
112
|
+
clock.uninstall();
|
|
113
|
+
registry.restore();
|
|
114
|
+
env.dispose();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("publishes the report inline in turn 1 and still lists it after the approved turn 2", async () => {
|
|
118
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
119
|
+
const record = deepAgentExecutionRecord({ message: "Write the report, then run the command." });
|
|
120
|
+
const scenario = beginDeepAgentScenario({
|
|
121
|
+
env,
|
|
122
|
+
clock,
|
|
123
|
+
record,
|
|
124
|
+
checkpointer: "sqlite",
|
|
125
|
+
script: () => ({
|
|
126
|
+
turns: [
|
|
127
|
+
{
|
|
128
|
+
text: "Writing the report.",
|
|
129
|
+
toolCalls: [{ id: WRITE_CALL_ID, name: "write_file", args: { file_path: `/${REPORT}`, content: REPORT_BODY } }],
|
|
130
|
+
usage: { inputTokens: 1_300, outputTokens: 60 },
|
|
131
|
+
},
|
|
132
|
+
{ text: "Now the command.", toolCalls: [EXECUTE_CALL_A], usage: { inputTokens: 1_500, outputTokens: 40 } },
|
|
133
|
+
CLOSING_TURN,
|
|
134
|
+
],
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ── Turn 1 ───────────────────────────────────────────────────────────────
|
|
139
|
+
const turn1 = await runDeepAgentTurn(scenario, { turnSeq: 0 });
|
|
140
|
+
expect(turn1.outcome.kind).toBe("returned");
|
|
141
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL);
|
|
142
|
+
const run1 = record.lastFullStatus!;
|
|
143
|
+
expect(run1.artifacts.map((a) => [a.name, a.storageKey])).toEqual([
|
|
144
|
+
[REPORT, `artifacts/${FIXTURE.executionId}/${REPORT}`],
|
|
145
|
+
]);
|
|
146
|
+
expect(run1.artifacts[0].contentHash, "sha256 of the bytes").toMatch(/^[0-9a-f]{64}$/);
|
|
147
|
+
expect(existsSync(join(env.artifactPath, run1.artifacts[0].storageKey)), "the bytes reached the store").toBe(true);
|
|
148
|
+
const writeRow = record.toolCalls().find((tc) => tc.id === WRITE_CALL_ID)!;
|
|
149
|
+
expect(writeRow.status, "capture mode let the write flow").toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
150
|
+
expect(record.waitingToolCalls().map((tc) => tc.id)).toEqual([EXECUTE_CALL_A.id]);
|
|
151
|
+
await expect(statusJson(run1)).toMatchFileSnapshot("./goldens/inline-artifact.turn1.status.json");
|
|
152
|
+
|
|
153
|
+
// ── Between turns ────────────────────────────────────────────────────────
|
|
154
|
+
expect(record.decideWaitingToolCalls(ApprovalAction.APPROVE, DECIDED_AT)).toBe(1);
|
|
155
|
+
|
|
156
|
+
// ── Turn 2 ───────────────────────────────────────────────────────────────
|
|
157
|
+
const turn2 = await runDeepAgentTurn(scenario, { turnSeq: 1 });
|
|
158
|
+
expect(turn2.outcome.kind).toBe("returned");
|
|
159
|
+
expect((turn2.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
160
|
+
const final = record.lastFullStatus!;
|
|
161
|
+
expect(final.artifacts.map((a) => [a.name, a.storageKey, a.contentHash]), "the seed carried the artifact").toEqual(
|
|
162
|
+
run1.artifacts.map((a) => [a.name, a.storageKey, a.contentHash]),
|
|
163
|
+
);
|
|
164
|
+
expect(final.messages.flatMap((m) => m.toolCalls).map((tc) => [tc.id, tc.status])).toEqual([
|
|
165
|
+
[WRITE_CALL_ID, ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
166
|
+
[EXECUTE_CALL_A.id, ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
167
|
+
]);
|
|
168
|
+
|
|
169
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
170
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
171
|
+
const finalJson = statusJson(final);
|
|
172
|
+
expect(finalJson, "no temp path may reach the golden").not.toContain(env.artifactPath);
|
|
173
|
+
await expect(finalJson).toMatchFileSnapshot("./goldens/inline-artifact.turn2.status.json");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: the two ways a turn is INTERRUPTED from outside, and how
|
|
3
|
+
* the activity tells them apart — the throw-vs-return table the control
|
|
4
|
+
* plane's workflow keys on. Since S3 M2a the activity is the turn runtime
|
|
5
|
+
* over the native adapter, and the shapes below are the runtime's terminal
|
|
6
|
+
* table (`harness/terminal-table.ts`), ruled at Q-S3-7 (P-1: the pause copy
|
|
7
|
+
* is Cursor's) and stigmer#1071 ("persist a thrown terminal once").
|
|
8
|
+
*
|
|
9
|
+
* 1. USER PAUSE: the activity's `cancellationSignal` aborts. Whether the
|
|
10
|
+
* abort lands inside a model call (staged from the scripted model's
|
|
11
|
+
* `onTurn`) or while the loop is persisting (staged from the control
|
|
12
|
+
* plane's persist channel, the record's `controlSignal`), the runtime's
|
|
13
|
+
* stop controller aborts the adapter's signal, the adapter cancels the
|
|
14
|
+
* graph run and settles `interrupted`, and the runtime persists PAUSED
|
|
15
|
+
* ONCE — WITH the transcript and the row "Execution paused by user. Use
|
|
16
|
+
* resume to continue." (P-1: the runtime's pause copy is Cursor's; the
|
|
17
|
+
* orchestrator's said "… from this checkpoint.") — then throws
|
|
18
|
+
* `CancelledFailure("Activity paused by orchestrator")`. Until M2a the
|
|
19
|
+
* orchestrator persisted PAUSED TWICE, the second write bare and erasing
|
|
20
|
+
* the transcript (S3 M0 finding F-M0-6, stigmer#1054's double persist);
|
|
21
|
+
* `pause.bare.status.json` recorded that write and is gone with it. The
|
|
22
|
+
* one write differs by where the cancel landed — after the tool finished
|
|
23
|
+
* (`pause.loop.after-tool`) or while it was still RUNNING
|
|
24
|
+
* (`pause.loop.mid-tool`, the cancel arriving on the persist that carried
|
|
25
|
+
* the row).
|
|
26
|
+
* 2. WORKER SHUTDOWN: the queue's shutdown signal is aborted before the
|
|
27
|
+
* cancel, so the interruption is EXECUTION_FAILED with the interrupted
|
|
28
|
+
* copy and one row, thrown as
|
|
29
|
+
* `CancelledFailure("Activity cancelled (worker shutdown, not user pause)")`
|
|
30
|
+
* (#776) — the workflow re-invokes instead of waiting for a resume. The
|
|
31
|
+
* copy is unchanged; what changed at M2a (Q-S3-7, the runtime's arm
|
|
32
|
+
* wins) is that the runtime's arm KEEPS the transcript the turn produced
|
|
33
|
+
* and appends the row, where the orchestrator's `buildWorkerShutdownStatus`
|
|
34
|
+
* replaced the whole status with the row alone.
|
|
35
|
+
*
|
|
36
|
+
* Every interruption is staged deterministically — from a model turn or from
|
|
37
|
+
* a persist, never from a timer.
|
|
38
|
+
*
|
|
39
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
40
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
44
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
45
|
+
import {
|
|
46
|
+
AgentExecutionStatusSchema,
|
|
47
|
+
type AgentExecutionStatus,
|
|
48
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
49
|
+
import {
|
|
50
|
+
ExecutionControlSignal,
|
|
51
|
+
ExecutionPhase,
|
|
52
|
+
MessageType,
|
|
53
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
54
|
+
|
|
55
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
56
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
57
|
+
);
|
|
58
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
59
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
import {
|
|
63
|
+
ScriptedClock,
|
|
64
|
+
createHermeticEnvironment,
|
|
65
|
+
threwCancelledFailure,
|
|
66
|
+
type HermeticEnvironment,
|
|
67
|
+
type InvocationControls,
|
|
68
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
69
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
70
|
+
import {
|
|
71
|
+
beginDeepAgentScenario,
|
|
72
|
+
deepAgentExecutionRecord,
|
|
73
|
+
runDeepAgentTurn,
|
|
74
|
+
type DeepAgentScenario,
|
|
75
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
76
|
+
import type { RoleScript } from "../../__test-utils__/scripted-model.js";
|
|
77
|
+
|
|
78
|
+
const READ_CALL = { id: "call-hermetic-read-0001", name: "read_file", args: { file_path: "/notes.md" } };
|
|
79
|
+
|
|
80
|
+
/** A turn that reads, then would speak again — the interruption lands before it can. */
|
|
81
|
+
const SCRIPT: RoleScript = {
|
|
82
|
+
turns: [
|
|
83
|
+
{ text: "Let me look.", toolCalls: [READ_CALL], usage: { inputTokens: 1_200, outputTokens: 30 } },
|
|
84
|
+
{ text: "Never reaches the record.", usage: { inputTokens: 1_400, outputTokens: 10 } },
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
describe("ExecuteDeepAgent hermetic — pause vs worker shutdown", () => {
|
|
89
|
+
let env: HermeticEnvironment;
|
|
90
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
91
|
+
const clock = new ScriptedClock();
|
|
92
|
+
/** The scenario whose invocation is running — the persist-channel staging cancels it. */
|
|
93
|
+
let inFlight: DeepAgentScenario | undefined;
|
|
94
|
+
|
|
95
|
+
beforeAll(() => {
|
|
96
|
+
env = createHermeticEnvironment();
|
|
97
|
+
registry = stubRegistryFetch();
|
|
98
|
+
clock.install();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
afterAll(() => {
|
|
102
|
+
clock.uninstall();
|
|
103
|
+
registry.restore();
|
|
104
|
+
env.dispose();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe.each([
|
|
108
|
+
{
|
|
109
|
+
staging: "inside a model call",
|
|
110
|
+
begin: (record: ReturnType<typeof deepAgentExecutionRecord>) =>
|
|
111
|
+
beginDeepAgentScenario({
|
|
112
|
+
env,
|
|
113
|
+
clock,
|
|
114
|
+
record,
|
|
115
|
+
script: () => SCRIPT,
|
|
116
|
+
onTurn: (info, controls) => {
|
|
117
|
+
if (info.round === 1) controls.cancel();
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
controlSignal: undefined as ((status: AgentExecutionStatus) => ExecutionControlSignal) | undefined,
|
|
121
|
+
loopGolden: "./goldens/pause.loop.after-tool.status.json",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
staging: "between events",
|
|
125
|
+
begin: (record: ReturnType<typeof deepAgentExecutionRecord>) =>
|
|
126
|
+
beginDeepAgentScenario({ env, clock, record, script: () => SCRIPT }),
|
|
127
|
+
// The platform answers the persist that carries the first tool row by
|
|
128
|
+
// cancelling the activity (the workflow's pause).
|
|
129
|
+
controlSignal: (status: AgentExecutionStatus) => {
|
|
130
|
+
if (status.messages.some((m) => m.toolCalls.length > 0)) inFlight?.controls?.cancel();
|
|
131
|
+
return ExecutionControlSignal.UNSPECIFIED;
|
|
132
|
+
},
|
|
133
|
+
loopGolden: "./goldens/pause.loop.mid-tool.status.json",
|
|
134
|
+
},
|
|
135
|
+
])("user pause $staging", ({ begin, controlSignal, loopGolden }) => {
|
|
136
|
+
it("persists PAUSED once, with the transcript and the row, and throws as a pause", async () => {
|
|
137
|
+
clock.reset();
|
|
138
|
+
const record = deepAgentExecutionRecord({ message: "Look at my notes.", controlSignal });
|
|
139
|
+
inFlight = begin(record);
|
|
140
|
+
|
|
141
|
+
const invocation = await runDeepAgentTurn(inFlight);
|
|
142
|
+
|
|
143
|
+
expect(threwCancelledFailure(invocation.outcome), "a pause THROWS CancelledFailure").toBe(true);
|
|
144
|
+
expect((invocation.outcome as { error: Error }).error.message).toBe("Activity paused by orchestrator");
|
|
145
|
+
expect(record.persistedPhases).toEqual([ExecutionPhase.EXECUTION_IN_PROGRESS, ExecutionPhase.EXECUTION_PAUSED]);
|
|
146
|
+
|
|
147
|
+
const pausedWrites = record.persisted.filter((s) => s.phase === ExecutionPhase.EXECUTION_PAUSED);
|
|
148
|
+
expect(pausedWrites, "PAUSED is persisted exactly once (stigmer#1071; F-M0-6 closed at M2a)").toHaveLength(1);
|
|
149
|
+
const [paused] = pausedWrites;
|
|
150
|
+
expect(paused.messages.filter((m) => m.type === MessageType.MESSAGE_SYSTEM).map((m) => m.content)).toEqual([
|
|
151
|
+
"Execution paused by user. Use resume to continue.",
|
|
152
|
+
]);
|
|
153
|
+
expect(
|
|
154
|
+
paused.messages.flatMap((m) => m.toolCalls).map((tc) => tc.name),
|
|
155
|
+
"the read that landed before the pause is in the one write",
|
|
156
|
+
).toEqual(["read_file"]);
|
|
157
|
+
expect(paused.completedAt).toBe("");
|
|
158
|
+
expect(record.lastFullStatus, "the wire ends on the write that carries the transcript").toEqual(paused);
|
|
159
|
+
|
|
160
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, paused), null, 2) + "\n";
|
|
161
|
+
await expect(json).toMatchFileSnapshot(loopGolden);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("worker shutdown: FAILED with the interrupted copy, thrown as a shutdown", async () => {
|
|
166
|
+
clock.reset();
|
|
167
|
+
const record = deepAgentExecutionRecord({ message: "Look at my notes." });
|
|
168
|
+
const scenario = beginDeepAgentScenario({
|
|
169
|
+
env,
|
|
170
|
+
clock,
|
|
171
|
+
record,
|
|
172
|
+
script: () => SCRIPT,
|
|
173
|
+
onTurn: (info, controls: InvocationControls) => {
|
|
174
|
+
if (info.round === 1) {
|
|
175
|
+
controls.signalWorkerShutdown();
|
|
176
|
+
controls.cancel();
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
182
|
+
|
|
183
|
+
expect(threwCancelledFailure(invocation.outcome), "a drain THROWS so the workflow re-invokes").toBe(true);
|
|
184
|
+
expect((invocation.outcome as { error: Error }).error.message).toBe(
|
|
185
|
+
"Activity cancelled (worker shutdown, not user pause)",
|
|
186
|
+
);
|
|
187
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_FAILED);
|
|
188
|
+
const final = record.lastFullStatus!;
|
|
189
|
+
expect(final.error).toBe("Execution interrupted: runner worker was shut down. Retry or resume.");
|
|
190
|
+
// The transcript the turn produced is kept and the row appended (the
|
|
191
|
+
// runtime's arm, Q-S3-7); the tool row rides its own empty AI message
|
|
192
|
+
// (F-M0-1, S4's).
|
|
193
|
+
expect(final.messages.map((m) => [m.type, m.content])).toEqual([
|
|
194
|
+
[MessageType.MESSAGE_AI, "Let me look."],
|
|
195
|
+
[MessageType.MESSAGE_AI, ""],
|
|
196
|
+
[
|
|
197
|
+
MessageType.MESSAGE_SYSTEM,
|
|
198
|
+
"Execution interrupted: the runner worker was shut down while the agent was still running. You can retry or resume.",
|
|
199
|
+
],
|
|
200
|
+
]);
|
|
201
|
+
expect(final.messages.flatMap((m) => m.toolCalls).map((tc) => tc.name), "the read that landed before the drain is kept").toEqual(["read_file"]);
|
|
202
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
203
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
204
|
+
await expect(json).toMatchFileSnapshot("./goldens/worker-shutdown.status.json");
|
|
205
|
+
});
|
|
206
|
+
});
|