@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,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The harness contract kit, BOTH halves, against the REAL native deep-agent
|
|
3
|
+
* adapter under the sqlite checkpointer.
|
|
4
|
+
*
|
|
5
|
+
* The adapter-side half (`__test-utils__/harness-contract/contract.ts`) is the
|
|
6
|
+
* single statement of what every `HarnessAdapter` owes the runtime;
|
|
7
|
+
* `src/__tests__/harness-contract.test.ts` runs it against the scripted fake
|
|
8
|
+
* under both pause primitives, `execute-cursor/__tests__/hermetic/
|
|
9
|
+
* harness-contract.test.ts` against the real Cursor adapter. The runtime-side
|
|
10
|
+
* half (`runtime-contract.ts`) is what the turn runtime owes every harness,
|
|
11
|
+
* through the real activity under `MockActivityEnvironment`. This file runs
|
|
12
|
+
* BOTH against `createDeepAgentAdapter()` through the native subject
|
|
13
|
+
* (`__test-utils__/contract-subject.ts`), which translates the kit's
|
|
14
|
+
* scenarios onto the `ScriptedModel` double and lets deepagents' real
|
|
15
|
+
* built-ins run over the real approval gate. Nothing of the adapter is
|
|
16
|
+
* mocked; the model client and the control-plane client are the two doubles
|
|
17
|
+
* every native hermetic golden already substitutes.
|
|
18
|
+
*
|
|
19
|
+
* The posture is sqlite, the production local default (`config.ts`): the
|
|
20
|
+
* test-only memory saver re-interrupts on replay and duplicates the gated
|
|
21
|
+
* row (S3 M0 finding F-M0-3), so it cannot carry the HITL arms. One sqlite
|
|
22
|
+
* file per session lands under the hermetic `HOME`, opened and closed per
|
|
23
|
+
* turn by the adapter.
|
|
24
|
+
*
|
|
25
|
+
* What this subject declares it cannot produce, so the kit reports the arm
|
|
26
|
+
* SKIPPED and never silently passes: the `engine` and `actionable` failure
|
|
27
|
+
* surfaces (`turn.ts` classifies every escaped error `internal` — the
|
|
28
|
+
* adapter has one surface), and an engine-side cancel (no native path ends a
|
|
29
|
+
* turn `cancelled`).
|
|
30
|
+
*
|
|
31
|
+
* Beside the kit, the native-only observations its vocabulary cannot
|
|
32
|
+
* express — the interrupt primitive's own facts: an APPROVED pending action
|
|
33
|
+
* executes at the resume, before the model is asked (the order the kit's
|
|
34
|
+
* invariant 4 used to assert the other way round, until this adapter showed
|
|
35
|
+
* it is the primitive's and not the contract's — S3 M3 F-M3-1, Q-M3-2); a
|
|
36
|
+
* REJECT reaches the model as the gate's denial `ToolMessage` in the
|
|
37
|
+
* conversation, and the model proposes the call exactly once (the denial is
|
|
38
|
+
* a tool result the model reads, never a second gate); and one sqlite
|
|
39
|
+
* checkpoint per session lands under the hermetic `HOME`, none for a session
|
|
40
|
+
* never served.
|
|
41
|
+
*
|
|
42
|
+
* No golden: the kit asserts behaviour, the goldens under `goldens/` pin
|
|
43
|
+
* transcripts. The hermetic environment is created at module level because
|
|
44
|
+
* the subject's `config` is read at collection time; the forks pool keeps it
|
|
45
|
+
* private to this file.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
49
|
+
import { existsSync } from "node:fs";
|
|
50
|
+
import { isAIMessage, isToolMessage } from "@langchain/core/messages";
|
|
51
|
+
import { ApprovalAction, ToolCallStatus } 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 { ExecutionDriver, describeHarnessContract } from "../../../../__test-utils__/harness-contract/contract.js";
|
|
61
|
+
import { describeHarnessRuntimeContract } from "../../../../__test-utils__/harness-contract/runtime-contract.js";
|
|
62
|
+
import { scenario } from "../../../../__test-utils__/harness-contract/types.js";
|
|
63
|
+
import { ScriptedClock, createHermeticEnvironment } from "../../../../__test-utils__/hermetic-activity.js";
|
|
64
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
65
|
+
import { findToolCallRow } from "../../../../__test-utils__/proto-helpers.js";
|
|
66
|
+
import { getCheckpointDbPath } from "../../../../shared/workspace/platform-dir.js";
|
|
67
|
+
import { createDeepAgentContractSubject } from "../../__test-utils__/contract-subject.js";
|
|
68
|
+
|
|
69
|
+
const env = createHermeticEnvironment();
|
|
70
|
+
const clock = new ScriptedClock();
|
|
71
|
+
const subject = createDeepAgentContractSubject(env);
|
|
72
|
+
const WRITE_GAMMA = { kind: "write", resource: "/work/gamma.txt" } as const;
|
|
73
|
+
|
|
74
|
+
describe("ExecuteDeepAgent hermetic — the harness contract kit against the real adapter", () => {
|
|
75
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
76
|
+
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
registry = stubRegistryFetch();
|
|
79
|
+
clock.install();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
afterAll(() => {
|
|
83
|
+
clock.uninstall();
|
|
84
|
+
registry.restore();
|
|
85
|
+
env.dispose();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describeHarnessContract(subject);
|
|
89
|
+
describeHarnessRuntimeContract({ subject, env, clock }, { failureSurfaces: ["internal"], engineCancel: false });
|
|
90
|
+
|
|
91
|
+
describe("native-only observations", () => {
|
|
92
|
+
beforeAll(async () => {
|
|
93
|
+
await subject.adapter.boot(subject.config);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("executes an APPROVED pending action at the resume, before the model is asked — even when the model then hangs and the turn is stopped (interrupt order, F-M3-1)", async () => {
|
|
97
|
+
const driver = new ExecutionDriver(subject, "obs-approved-at-resume");
|
|
98
|
+
const id = "obs-approved-at-resume-write";
|
|
99
|
+
|
|
100
|
+
const proposed = await driver.turn([scenario.propose(id, WRITE_GAMMA)]);
|
|
101
|
+
expect(proposed.outcome.kind).toBe("awaiting_approval");
|
|
102
|
+
driver.decide(id, ApprovalAction.APPROVE);
|
|
103
|
+
|
|
104
|
+
const hanging = driver.begin([scenario.say("working"), scenario.hang(), scenario.propose(id, WRITE_GAMMA)], { stopWhenHanging: "kit: user pause" });
|
|
105
|
+
const outcome = await hanging.settled;
|
|
106
|
+
|
|
107
|
+
expect(outcome.kind).toBe("interrupted");
|
|
108
|
+
expect(subject.executionCount(id), "the resume completed the pending task before the hang; the stop came after").toBe(1);
|
|
109
|
+
expect(findToolCallRow(hanging.sink.status, id)?.status, "and the row on the interrupted turn's status says so").toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("hands a REJECT to the model as the gate's denial ToolMessage; the call is proposed exactly once and never re-gated", async () => {
|
|
113
|
+
const driver = new ExecutionDriver(subject, "obs-reject-as-denial");
|
|
114
|
+
const id = "obs-reject-as-denial-write";
|
|
115
|
+
|
|
116
|
+
const proposed = await driver.turn([scenario.propose(id, WRITE_GAMMA)]);
|
|
117
|
+
expect(proposed.outcome.kind).toBe("awaiting_approval");
|
|
118
|
+
driver.decide(id, ApprovalAction.REJECT);
|
|
119
|
+
const resumed = await driver.turn([scenario.propose(id, WRITE_GAMMA), scenario.say("moving on")]);
|
|
120
|
+
expect(resumed.outcome.kind).toBe("completed");
|
|
121
|
+
|
|
122
|
+
const transcript = subject.lastTranscriptOf(driver.executionId)!;
|
|
123
|
+
const denials = transcript.filter((m) => isToolMessage(m) && m.tool_call_id === id);
|
|
124
|
+
const proposals = transcript.filter((m) => isAIMessage(m) && (m.tool_calls ?? []).some((tc) => tc.id === id));
|
|
125
|
+
expect(denials, "the model read the gate's answer as a tool result").toHaveLength(1);
|
|
126
|
+
expect(proposals, "and had proposed the call exactly once").toHaveLength(1);
|
|
127
|
+
expect(subject.executionCount(id)).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("keeps one sqlite checkpoint per session under the hermetic HOME, none for a session never served", async () => {
|
|
131
|
+
const served = new ExecutionDriver(subject, "obs-sqlite-a");
|
|
132
|
+
const other = new ExecutionDriver(subject, "obs-sqlite-b");
|
|
133
|
+
expect((await served.turn([scenario.say("first")])).outcome.kind).toBe("completed");
|
|
134
|
+
expect((await other.turn([scenario.say("second")])).outcome.kind).toBe("completed");
|
|
135
|
+
|
|
136
|
+
const [a, b] = [getCheckpointDbPath(served.sessionId), getCheckpointDbPath(other.sessionId)];
|
|
137
|
+
expect(a.startsWith(env.home), "the checkpoint lives under the runner-owned HOME tree").toBe(true);
|
|
138
|
+
expect(a).not.toBe(b);
|
|
139
|
+
expect(existsSync(a)).toBe(true);
|
|
140
|
+
expect(existsSync(b)).toBe(true);
|
|
141
|
+
expect(existsSync(getCheckpointDbPath("ses-never-served")), "a session never served has no checkpoint").toBe(false);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: APPROVE_ALL leases the gated action's CLASS for the rest
|
|
3
|
+
* of the run — two invocations on the durable (sqlite) checkpointer.
|
|
4
|
+
*
|
|
5
|
+
* Turn 1: the model thinks (a leading reasoning block, persisted as a
|
|
6
|
+
* THINKING message), says it will run two commands, and proposes the first
|
|
7
|
+
* gated `execute`; the gate pauses; WAITING_FOR_APPROVAL.
|
|
8
|
+
*
|
|
9
|
+
* Between turns: APPROVE_ALL on the WAITING row. `deriveActiveLeases`
|
|
10
|
+
* reduces it to the scope of the tool it was made on — for a built-in, its
|
|
11
|
+
* approval category (`shared/approval-policy.ts` `deriveLeaseScope`), here
|
|
12
|
+
* SHELL — and the reinvocation's gate carries that lease.
|
|
13
|
+
*
|
|
14
|
+
* Turn 2: the graph resumes inside the gate and runs command A; the model
|
|
15
|
+
* proposes command B; the gate finds SHELL leased and runs B WITHOUT a second
|
|
16
|
+
* interrupt (`ToolCall.approvalPolicySource` records the lease as the
|
|
17
|
+
* authorization); the model closes; COMPLETED.
|
|
18
|
+
*
|
|
19
|
+
* Invariants carried from the retired `hitl-resume-approve-all.test.ts`
|
|
20
|
+
* (the user-reported "leading thinking block and the first tool call vanish
|
|
21
|
+
* after approve-all" regression): the final status is a strict SUPERSET of
|
|
22
|
+
* run 1's transcript — the THINKING message survives, the gated call is
|
|
23
|
+
* reconciled in place (exactly one copy, now COMPLETED), and the leased
|
|
24
|
+
* follow-up is appended COMPLETED with no interrupt. That test leased an MCP
|
|
25
|
+
* SERVER (`getAppState` on open-computer-use); this arm leases a CATEGORY,
|
|
26
|
+
* the same `deriveLeaseScope` path with the other scope kind (S3 M0 ruling
|
|
27
|
+
* Q-M0-4). The server-scoped lease stays pinned by `approval-policy`'s unit
|
|
28
|
+
* tests and the conformance execution class.
|
|
29
|
+
*
|
|
30
|
+
* What every reinvocation golden here shows since S3 M2a (the runtime over
|
|
31
|
+
* the native adapter; Q-S3-16 and Q-M2a-2): `startedAt` is THIS turn's own,
|
|
32
|
+
* not turn 1's carried by the orchestrator's whole-status clone; the
|
|
33
|
+
* `fileReviewEventStream` carries only the events THIS turn authored (the
|
|
34
|
+
* runtime seeds messages, sub-agents, artifacts, write-backs, todos, usage,
|
|
35
|
+
* the memory report and the structured output — never the server-owned
|
|
36
|
+
* ledgers, which the server appends to by event id in `update-status.ts`
|
|
37
|
+
* `appendRunnerEvents`; the hermetic record replaces wholesale, so a
|
|
38
|
+
* golden reads as what the runner SENT); and `streamingUsage` carries the
|
|
39
|
+
* runtime accountant's fields (`model`, `estimatedCostUsd`, the requested
|
|
40
|
+
* tier and thinking mode).
|
|
41
|
+
*
|
|
42
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
43
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
47
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
48
|
+
import {
|
|
49
|
+
AgentExecutionStatusSchema,
|
|
50
|
+
type AgentExecutionStatus,
|
|
51
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
52
|
+
import {
|
|
53
|
+
ApprovalAction,
|
|
54
|
+
ApprovalPolicySource,
|
|
55
|
+
ExecutionPhase,
|
|
56
|
+
MessageType,
|
|
57
|
+
ToolCallStatus,
|
|
58
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
59
|
+
|
|
60
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
61
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
62
|
+
);
|
|
63
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
64
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
import {
|
|
68
|
+
ScriptedClock,
|
|
69
|
+
createHermeticEnvironment,
|
|
70
|
+
type HermeticEnvironment,
|
|
71
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
72
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
73
|
+
import {
|
|
74
|
+
beginDeepAgentScenario,
|
|
75
|
+
deepAgentExecutionRecord,
|
|
76
|
+
runDeepAgentTurn,
|
|
77
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
78
|
+
import { CLOSING_TURN, EXECUTE_CALL_A, EXECUTE_CALL_B } from "../../__test-utils__/hitl-script.js";
|
|
79
|
+
|
|
80
|
+
const USER_MESSAGE = "Run both commands.";
|
|
81
|
+
const REASONING = "Two commands are needed; I will run them in order.";
|
|
82
|
+
const OPENING_TEXT = "I will run the two commands.";
|
|
83
|
+
const DECIDED_AT = "2026-01-01T00:00:30.000Z";
|
|
84
|
+
|
|
85
|
+
function statusJson(status: AgentExecutionStatus): string {
|
|
86
|
+
return JSON.stringify(toJson(AgentExecutionStatusSchema, status), null, 2) + "\n";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
describe("ExecuteDeepAgent hermetic — APPROVE_ALL leases the class (sqlite)", () => {
|
|
90
|
+
let env: HermeticEnvironment;
|
|
91
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
92
|
+
const clock = new ScriptedClock();
|
|
93
|
+
|
|
94
|
+
beforeAll(() => {
|
|
95
|
+
env = createHermeticEnvironment();
|
|
96
|
+
registry = stubRegistryFetch();
|
|
97
|
+
clock.install();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
afterAll(() => {
|
|
101
|
+
clock.uninstall();
|
|
102
|
+
registry.restore();
|
|
103
|
+
env.dispose();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("runs the approved call and the leased follow-up in one resume, keeping the leading thinking", async () => {
|
|
107
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
108
|
+
const record = deepAgentExecutionRecord({ message: USER_MESSAGE });
|
|
109
|
+
const scenario = beginDeepAgentScenario({
|
|
110
|
+
env,
|
|
111
|
+
clock,
|
|
112
|
+
record,
|
|
113
|
+
checkpointer: "sqlite",
|
|
114
|
+
script: () => ({
|
|
115
|
+
turns: [
|
|
116
|
+
{
|
|
117
|
+
reasoning: REASONING,
|
|
118
|
+
text: OPENING_TEXT,
|
|
119
|
+
toolCalls: [EXECUTE_CALL_A],
|
|
120
|
+
usage: { inputTokens: 1_400, outputTokens: 80 },
|
|
121
|
+
},
|
|
122
|
+
{ toolCalls: [EXECUTE_CALL_B], usage: { inputTokens: 1_600, outputTokens: 40 } },
|
|
123
|
+
CLOSING_TURN,
|
|
124
|
+
],
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// ── Act 1 ────────────────────────────────────────────────────────────────
|
|
129
|
+
const turn1 = await runDeepAgentTurn(scenario, { turnSeq: 0 });
|
|
130
|
+
|
|
131
|
+
// ── Assert 1: paused on A, the thinking persisted ────────────────────────
|
|
132
|
+
expect(turn1.outcome.kind).toBe("returned");
|
|
133
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL);
|
|
134
|
+
const run1 = record.lastFullStatus!;
|
|
135
|
+
const thinking = run1.messages.filter((m) => m.type === MessageType.MESSAGE_THINKING);
|
|
136
|
+
expect(thinking.map((m) => m.content), "the reasoning block is exactly one THINKING message").toEqual([REASONING]);
|
|
137
|
+
expect(run1.messages[0].type, "and it leads the transcript").toBe(MessageType.MESSAGE_THINKING);
|
|
138
|
+
expect(record.waitingToolCalls().map((tc) => tc.id)).toEqual([EXECUTE_CALL_A.id]);
|
|
139
|
+
await expect(statusJson(run1)).toMatchFileSnapshot("./goldens/approve-all-lease.turn1.status.json");
|
|
140
|
+
|
|
141
|
+
// ── Between turns: APPROVE_ALL on A ──────────────────────────────────────
|
|
142
|
+
expect(record.decideWaitingToolCalls(ApprovalAction.APPROVE_ALL, DECIDED_AT)).toBe(1);
|
|
143
|
+
|
|
144
|
+
// ── Act 2 ────────────────────────────────────────────────────────────────
|
|
145
|
+
const turn2 = await runDeepAgentTurn(scenario, { turnSeq: 1 });
|
|
146
|
+
|
|
147
|
+
// ── Assert 2: A ran, B ran under the lease, no second interrupt ──────────
|
|
148
|
+
expect(turn2.outcome.kind).toBe("returned");
|
|
149
|
+
expect((turn2.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
150
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
151
|
+
expect(record.persistedPhases.filter((p) => p === ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL)).toHaveLength(1);
|
|
152
|
+
|
|
153
|
+
const final = record.lastFullStatus!;
|
|
154
|
+
const rows = final.messages.flatMap((m) => m.toolCalls);
|
|
155
|
+
expect(rows.map((tc) => tc.id), "A reconciled in place, B appended; one copy each").toEqual([
|
|
156
|
+
EXECUTE_CALL_A.id,
|
|
157
|
+
EXECUTE_CALL_B.id,
|
|
158
|
+
]);
|
|
159
|
+
const [a, b] = rows;
|
|
160
|
+
expect(a.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
161
|
+
expect(a.approvalAction).toBe(ApprovalAction.APPROVE_ALL);
|
|
162
|
+
expect(a.result).toContain("hermetic-a");
|
|
163
|
+
expect(b.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
164
|
+
expect(b.requiresApproval, "the leased class needs no interrupt").toBe(false);
|
|
165
|
+
expect(b.approvalPolicySource, "the lease is B's recorded authorization").toBe(
|
|
166
|
+
ApprovalPolicySource.APPROVAL_LEASE,
|
|
167
|
+
);
|
|
168
|
+
expect(b.approvalAction, "B was never decided by a person").toBe(ApprovalAction.UNSPECIFIED);
|
|
169
|
+
expect(b.result).toContain("hermetic-b");
|
|
170
|
+
|
|
171
|
+
// The superset invariant: every run-1 message survives, in order, at the front.
|
|
172
|
+
const run1Shape = run1.messages.map((m) => [m.type, m.content]);
|
|
173
|
+
const finalShape = final.messages.map((m) => [m.type, m.content]);
|
|
174
|
+
expect(finalShape.slice(0, run1Shape.length), "run 1's transcript is a prefix of the final").toEqual(run1Shape);
|
|
175
|
+
expect(final.messages[0].type, "the leading thinking survives the resume").toBe(MessageType.MESSAGE_THINKING);
|
|
176
|
+
expect(finalShape.at(-1)).toEqual([MessageType.MESSAGE_AI, CLOSING_TURN.text]);
|
|
177
|
+
|
|
178
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
179
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
180
|
+
|
|
181
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
182
|
+
await expect(statusJson(final)).toMatchFileSnapshot("./goldens/approve-all-lease.turn2.status.json");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: the three HITL round trips — APPROVE, SKIP, REJECT — each
|
|
3
|
+
* two invocations of the whole `ExecuteDeepAgent` activity on the durable
|
|
4
|
+
* (sqlite) checkpointer, the production local posture (`config.ts` resolves
|
|
5
|
+
* `sqlite` in local mode and `http` in cloud; both resume the same way).
|
|
6
|
+
*
|
|
7
|
+
* Turn 1 (shared by every arm, ONE golden `gate.turn1.status.json`): the
|
|
8
|
+
* model proposes a gated `execute`; the gate's `interrupt()` pauses the graph;
|
|
9
|
+
* the post-stream interrupt detection seeds a WAITING_APPROVAL row carrying
|
|
10
|
+
* the gate's message and provenance; the activity persists
|
|
11
|
+
* WAITING_FOR_APPROVAL and RETURNS. That the approve, skip and reject arms all
|
|
12
|
+
* match the same file is the assertion that the decision to come is invisible
|
|
13
|
+
* to the first invocation.
|
|
14
|
+
*
|
|
15
|
+
* Between turns the test does what the server's `SubmitApproval` does — writes
|
|
16
|
+
* `approval_action` on the WAITING row (`ExecutionRecord.decideWaitingToolCalls`),
|
|
17
|
+
* nothing else — and the second invocation reads the real persisted status.
|
|
18
|
+
*
|
|
19
|
+
* Turn 2 (one golden per decision): the activity seeds its status from the
|
|
20
|
+
* persisted transcript (`shouldSeedFromPersistedTranscript`), reads the
|
|
21
|
+
* checkpoint's pending interrupt, builds `Command({ resume })` from the
|
|
22
|
+
* decision (`hitl.ts`), and the graph resumes INSIDE the gate:
|
|
23
|
+
* - APPROVE: the tool runs; the row is reconciled IN PLACE to COMPLETED with
|
|
24
|
+
* the command's output (exactly one copy of the gated call, the run-1
|
|
25
|
+
* transcript a strict prefix of the final one — `hitl-resume-history`'s
|
|
26
|
+
* invariant), the model closes, COMPLETED.
|
|
27
|
+
* - SKIP: the gate returns a "skipped by user" ToolMessage; the tool never
|
|
28
|
+
* runs; `reconcileNonExecutingDecisions` stamps the row SKIPPED after the
|
|
29
|
+
* stream; COMPLETED.
|
|
30
|
+
* - REJECT (issue #197, `hitl-reject`'s invariant): the gate returns a denial
|
|
31
|
+
* ToolMessage and the run CONTINUES; the row is stamped SKIPPED with
|
|
32
|
+
* `error: "Rejected by user"` and keeps `approvalAction = REJECT` for
|
|
33
|
+
* audit; COMPLETED, never stuck at WAITING_APPROVAL.
|
|
34
|
+
*
|
|
35
|
+
* Carried from the retired `hitl-reject.test.ts` (both arms), `hitl-resume-
|
|
36
|
+
* history.test.ts` (seed + single copy), `hitl-resume-approve-all.test.ts`'s
|
|
37
|
+
* superset assertion (the leased variant lives in `hitl-approve-all-lease`).
|
|
38
|
+
*
|
|
39
|
+
* The REJECT arm is what Q-S3-2 aligns the runtime to (REJECT continues on
|
|
40
|
+
* both harnesses); this golden is the native truth it must reproduce.
|
|
41
|
+
*
|
|
42
|
+
* What every reinvocation golden here shows since S3 M2a (the runtime over
|
|
43
|
+
* the native adapter; Q-S3-16 and Q-M2a-2): `startedAt` is THIS turn's own,
|
|
44
|
+
* not turn 1's carried by the orchestrator's whole-status clone; the
|
|
45
|
+
* `fileReviewEventStream` carries only the events THIS turn authored (the
|
|
46
|
+
* runtime seeds messages, sub-agents, artifacts, write-backs, todos, usage,
|
|
47
|
+
* the memory report and the structured output — never the server-owned
|
|
48
|
+
* ledgers, which the server appends to by event id in `update-status.ts`
|
|
49
|
+
* `appendRunnerEvents`; the hermetic record replaces wholesale, so a
|
|
50
|
+
* golden reads as what the runner SENT); and `streamingUsage` carries the
|
|
51
|
+
* runtime accountant's fields (`model`, `estimatedCostUsd`, the requested
|
|
52
|
+
* tier and thinking mode).
|
|
53
|
+
*
|
|
54
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
55
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
56
|
+
*/
|
|
57
|
+
|
|
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
|
+
MessageType,
|
|
68
|
+
ToolCallStatus,
|
|
69
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
70
|
+
|
|
71
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
72
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
73
|
+
);
|
|
74
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
75
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
import {
|
|
79
|
+
ScriptedClock,
|
|
80
|
+
createHermeticEnvironment,
|
|
81
|
+
type HermeticEnvironment,
|
|
82
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
83
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
84
|
+
import {
|
|
85
|
+
beginDeepAgentScenario,
|
|
86
|
+
deepAgentExecutionRecord,
|
|
87
|
+
runDeepAgentTurn,
|
|
88
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
89
|
+
import {
|
|
90
|
+
CLOSING_TURN,
|
|
91
|
+
EXECUTE_CALL_A,
|
|
92
|
+
GATED_OPENING_TURN,
|
|
93
|
+
GATE_TURN1_GOLDEN,
|
|
94
|
+
executeApprovalMessage,
|
|
95
|
+
} from "../../__test-utils__/hitl-script.js";
|
|
96
|
+
|
|
97
|
+
const USER_MESSAGE = "Run the command for me.";
|
|
98
|
+
const DECIDED_AT = "2026-01-01T00:00:30.000Z";
|
|
99
|
+
|
|
100
|
+
function statusJson(status: AgentExecutionStatus): string {
|
|
101
|
+
return JSON.stringify(toJson(AgentExecutionStatusSchema, status), null, 2) + "\n";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
describe("ExecuteDeepAgent hermetic — HITL round trips (sqlite)", () => {
|
|
105
|
+
let env: HermeticEnvironment;
|
|
106
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
107
|
+
const clock = new ScriptedClock();
|
|
108
|
+
|
|
109
|
+
beforeAll(() => {
|
|
110
|
+
env = createHermeticEnvironment();
|
|
111
|
+
registry = stubRegistryFetch();
|
|
112
|
+
clock.install();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
afterAll(() => {
|
|
116
|
+
clock.uninstall();
|
|
117
|
+
registry.restore();
|
|
118
|
+
env.dispose();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe.each([
|
|
122
|
+
{ name: "approve", action: ApprovalAction.APPROVE },
|
|
123
|
+
{ name: "skip", action: ApprovalAction.SKIP },
|
|
124
|
+
{ name: "reject", action: ApprovalAction.REJECT },
|
|
125
|
+
])("$name", ({ name, action }) => {
|
|
126
|
+
it(`pauses on the gate, then ${name} carries the decision to COMPLETED`, async () => {
|
|
127
|
+
// ── Arrange ────────────────────────────────────────────────────────────
|
|
128
|
+
clock.reset();
|
|
129
|
+
const record = deepAgentExecutionRecord({ message: USER_MESSAGE });
|
|
130
|
+
const scenario = beginDeepAgentScenario({
|
|
131
|
+
env,
|
|
132
|
+
clock,
|
|
133
|
+
record,
|
|
134
|
+
checkpointer: "sqlite",
|
|
135
|
+
script: () => ({ turns: [GATED_OPENING_TURN, CLOSING_TURN] }),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ── Act 1: the gate pauses the turn ────────────────────────────────────
|
|
139
|
+
const turn1 = await runDeepAgentTurn(scenario, { turnSeq: 0 });
|
|
140
|
+
|
|
141
|
+
// ── Assert 1: WAITING_FOR_APPROVAL, one WAITING row, returned ──────────
|
|
142
|
+
expect(turn1.outcome.kind, "an approval pause RETURNS").toBe("returned");
|
|
143
|
+
expect((turn1.outcome as { value: Record<string, unknown> }).value.phase).toBe(
|
|
144
|
+
"EXECUTION_WAITING_FOR_APPROVAL",
|
|
145
|
+
);
|
|
146
|
+
expect(record.persistedPhases).toEqual([
|
|
147
|
+
ExecutionPhase.EXECUTION_IN_PROGRESS,
|
|
148
|
+
ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL,
|
|
149
|
+
]);
|
|
150
|
+
const waiting = record.waitingToolCalls();
|
|
151
|
+
expect(waiting).toHaveLength(1);
|
|
152
|
+
expect(waiting[0].id).toBe(EXECUTE_CALL_A.id);
|
|
153
|
+
expect(waiting[0].name).toBe("execute");
|
|
154
|
+
expect(waiting[0].requiresApproval).toBe(true);
|
|
155
|
+
expect(waiting[0].approvalMessage).toBe(executeApprovalMessage(EXECUTE_CALL_A));
|
|
156
|
+
expect(waiting[0].approvalAction, "no decision yet").toBe(ApprovalAction.UNSPECIFIED);
|
|
157
|
+
const run1 = record.lastFullStatus!;
|
|
158
|
+
await expect(statusJson(run1), "every decision's first turn is the same status").toMatchFileSnapshot(
|
|
159
|
+
GATE_TURN1_GOLDEN,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// ── Between turns: what SubmitApproval does to the row ─────────────────
|
|
163
|
+
expect(record.decideWaitingToolCalls(action, DECIDED_AT)).toBe(1);
|
|
164
|
+
|
|
165
|
+
// ── Act 2: the reinvocation resumes inside the gate ────────────────────
|
|
166
|
+
const turn2 = await runDeepAgentTurn(scenario, { turnSeq: 1 });
|
|
167
|
+
|
|
168
|
+
// ── Assert 2: COMPLETED, the row terminal, one copy, run 1 preserved ───
|
|
169
|
+
expect(turn2.outcome.kind).toBe("returned");
|
|
170
|
+
expect((turn2.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
171
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
172
|
+
|
|
173
|
+
const final = record.lastFullStatus!;
|
|
174
|
+
const rows = final.messages.flatMap((m) => m.toolCalls).filter((tc) => tc.id === EXECUTE_CALL_A.id);
|
|
175
|
+
expect(rows, "exactly one copy of the gated call — reconciled in place, never duplicated").toHaveLength(1);
|
|
176
|
+
const row = rows[0];
|
|
177
|
+
expect(row.approvalAction, "the decision is preserved for audit").toBe(action);
|
|
178
|
+
expect(row.approvalDecidedAt).toBe(DECIDED_AT);
|
|
179
|
+
expect(row.status, "never left at WAITING_APPROVAL").not.toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
|
|
180
|
+
if (action === ApprovalAction.APPROVE) {
|
|
181
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
182
|
+
expect(row.result, "the command ran").toContain("hermetic-a");
|
|
183
|
+
expect(row.error).toBe("");
|
|
184
|
+
} else {
|
|
185
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_SKIPPED);
|
|
186
|
+
expect(row.result, "the tool never ran").toBe("");
|
|
187
|
+
expect(row.error).toBe(action === ApprovalAction.REJECT ? "Rejected by user" : "");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const run1Ai = run1.messages.filter((m) => m.type === MessageType.MESSAGE_AI).map((m) => m.content);
|
|
191
|
+
const finalAi = final.messages.filter((m) => m.type === MessageType.MESSAGE_AI).map((m) => m.content);
|
|
192
|
+
expect(finalAi.slice(0, run1Ai.length), "run 1's transcript survives the resume as a prefix").toEqual(run1Ai);
|
|
193
|
+
expect(finalAi.at(-1)).toBe(CLOSING_TURN.text);
|
|
194
|
+
expect(final.completedAt).not.toBe("");
|
|
195
|
+
|
|
196
|
+
// ── Assert: hermeticity ────────────────────────────────────────────────
|
|
197
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
198
|
+
|
|
199
|
+
// ── Assert: the golden ─────────────────────────────────────────────────
|
|
200
|
+
await expect(statusJson(final)).toMatchFileSnapshot(`./goldens/${name}.turn2.status.json`);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|