@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
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
* Pins the turn context's pure decisions — the three places `turn-context.ts`
|
|
3
3
|
* decides something rather than fetching it:
|
|
4
4
|
*
|
|
5
|
-
* - `isReinvocation`: the one reading of "does
|
|
6
|
-
*
|
|
7
|
-
* engine-minted arm is
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - `approvalDecisionsOf
|
|
5
|
+
* - `isReinvocation`: the runtime's one reading of "does this invocation
|
|
6
|
+
* resume rather than begin", keyed on how the harness mints its state id.
|
|
7
|
+
* The engine-minted arm is `!!threadId`; the deterministic arm is the
|
|
8
|
+
* persisted transcript's non-emptiness, whatever the thread id says (the
|
|
9
|
+
* id exists on the first turn too), and never the live engine checkpoint.
|
|
10
|
+
* - `approvalDecisionsOf` (`approval-decisions.ts`): the runtime's reader of adjudicated approvals,
|
|
11
11
|
* over a transcript that carries every row shape it must include or
|
|
12
12
|
* exclude. Its agreement with the Cursor adapter's own reader
|
|
13
13
|
* (`reconstructAdjudicatedApprovals`) is pinned from the adapter's side
|
|
14
14
|
* (`activities/execute-cursor/__tests__/approval-state.test.ts`), because
|
|
15
15
|
* `src/harness/` never imports `src/activities/`, tests included.
|
|
16
|
-
* - `decideReinvocation`: the
|
|
17
|
-
*
|
|
18
|
-
* partially discarded one completes with
|
|
16
|
+
* - `decideReinvocation`: the three ways a resume proceeds (any decision
|
|
17
|
+
* runs the agent — REJECT included since S3 M1 —, a clean file-review
|
|
18
|
+
* resume completes, a failed or partially discarded one completes with
|
|
19
|
+
* its facts carried).
|
|
20
|
+
* - `regeneratesApprovedWrites`: which pause primitives make the runner pin
|
|
21
|
+
* an approved whole-file write's bytes itself (the engine re-runs the
|
|
22
|
+
* model) and which never do (the engine applies the exact args it paused
|
|
23
|
+
* on). S3 M2a, Q-M2a-3.
|
|
19
24
|
*
|
|
20
25
|
* The phases that fetch and provision are exercised end to end by the
|
|
21
26
|
* hermetic goldens (`activities/execute-cursor/__tests__/hermetic/`), which
|
|
@@ -25,30 +30,53 @@
|
|
|
25
30
|
|
|
26
31
|
import { describe, expect, it } from "vitest";
|
|
27
32
|
import { create } from "@bufbuild/protobuf";
|
|
28
|
-
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
33
|
+
import { AgentExecutionSchema, AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
29
34
|
import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
30
35
|
import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
31
36
|
import { ApprovalAction, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
32
37
|
|
|
33
|
-
import { approvalDecisionsOf
|
|
38
|
+
import { approvalDecisionsOf } from "../approval-decisions.js";
|
|
39
|
+
import { decideReinvocation, isReinvocation, regeneratesApprovedWrites, type ReinvocationFacts } from "../turn-context.js";
|
|
34
40
|
|
|
35
41
|
const INPUT = { executionId: "aex_1", threadId: "", turnSeq: 0 } as const;
|
|
36
42
|
|
|
43
|
+
/** An execution with no persisted history (a first run) or with `n` persisted AI rows (a continuation). */
|
|
44
|
+
function executionWithTranscript(rows: number) {
|
|
45
|
+
return create(AgentExecutionSchema, {
|
|
46
|
+
status: {
|
|
47
|
+
messages: Array.from({ length: rows }, (_, i) =>
|
|
48
|
+
create(AgentMessageSchema, { type: MessageType.MESSAGE_AI, content: `row ${i}` }),
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const FIRST_RUN = executionWithTranscript(0);
|
|
54
|
+
const CONTINUATION = executionWithTranscript(1);
|
|
55
|
+
|
|
37
56
|
describe("isReinvocation", () => {
|
|
38
57
|
it("engine-minted: an empty state id is a first turn, any id is a resume", () => {
|
|
39
|
-
expect(isReinvocation("engine-minted", INPUT)).toBe(false);
|
|
40
|
-
expect(isReinvocation("engine-minted", { ...INPUT, threadId: "agent-42" })).toBe(true);
|
|
58
|
+
expect(isReinvocation("engine-minted", INPUT, FIRST_RUN)).toBe(false);
|
|
59
|
+
expect(isReinvocation("engine-minted", { ...INPUT, threadId: "agent-42" }, FIRST_RUN)).toBe(true);
|
|
41
60
|
});
|
|
42
61
|
|
|
43
62
|
it("engine-minted: turnSeq is NOT the signal (a pure-reconcile turn does not advance it; the legacy wire shape yields 0)", () => {
|
|
44
|
-
expect(isReinvocation("engine-minted", { ...INPUT, turnSeq: 3 })).toBe(false);
|
|
45
|
-
expect(isReinvocation("engine-minted", { ...INPUT, threadId: "agent-42", turnSeq: 0 })).toBe(true);
|
|
63
|
+
expect(isReinvocation("engine-minted", { ...INPUT, turnSeq: 3 }, FIRST_RUN)).toBe(false);
|
|
64
|
+
expect(isReinvocation("engine-minted", { ...INPUT, threadId: "agent-42", turnSeq: 0 }, FIRST_RUN)).toBe(true);
|
|
46
65
|
});
|
|
47
66
|
|
|
48
|
-
it("
|
|
49
|
-
expect(
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
it("engine-minted: the transcript is NOT the signal (the bound id is; a follow-up turn with a bound id and an empty transcript still resumes the engine)", () => {
|
|
68
|
+
expect(isReinvocation("engine-minted", { ...INPUT, threadId: "agent-42" }, FIRST_RUN)).toBe(true);
|
|
69
|
+
expect(isReinvocation("engine-minted", INPUT, CONTINUATION)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("deterministic: the persisted transcript is the signal — empty is a first run, any committed history is a continuation", () => {
|
|
73
|
+
expect(isReinvocation("deterministic", { ...INPUT, threadId: "thread-ses_1" }, FIRST_RUN)).toBe(false);
|
|
74
|
+
expect(isReinvocation("deterministic", { ...INPUT, threadId: "thread-ses_1" }, CONTINUATION)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("deterministic: the thread id is NOT the signal (a deterministic harness has one on its first turn too)", () => {
|
|
78
|
+
expect(isReinvocation("deterministic", { ...INPUT, threadId: "thread-ses_1", turnSeq: 0 }, FIRST_RUN)).toBe(false);
|
|
79
|
+
expect(isReinvocation("deterministic", { ...INPUT, threadId: "", turnSeq: 0 }, CONTINUATION)).toBe(true);
|
|
52
80
|
});
|
|
53
81
|
});
|
|
54
82
|
|
|
@@ -135,21 +163,22 @@ describe("decideReinvocation", () => {
|
|
|
135
163
|
expect(decideReinvocation(facts())).toBeUndefined();
|
|
136
164
|
});
|
|
137
165
|
|
|
138
|
-
it("
|
|
166
|
+
it("a REJECT runs the agent like any other decision — the run continues without the tool (stigmer#197; S3 M1, Q-S3-2)", () => {
|
|
167
|
+
// Until S3 M1 this returned `{ kind: "rejected-by-user" }` and the runtime
|
|
168
|
+
// FAILED the execution before any engine ran, contradicting the proto, the
|
|
169
|
+
// conformance suite and the native harness (S2 M4 finding F1).
|
|
139
170
|
const decisions = new Map([
|
|
140
171
|
["tc-1", ApprovalAction.APPROVE],
|
|
141
172
|
["tc-2", ApprovalAction.REJECT],
|
|
142
173
|
]);
|
|
143
|
-
expect(decideReinvocation(facts({ approvalDecisions: decisions }))).
|
|
174
|
+
expect(decideReinvocation(facts({ approvalDecisions: decisions }))).toBeUndefined();
|
|
175
|
+
expect(decideReinvocation(facts({ approvalDecisions: new Map([["tc-2", ApprovalAction.REJECT]]) }))).toBeUndefined();
|
|
144
176
|
});
|
|
145
177
|
|
|
146
|
-
it("
|
|
178
|
+
it("any adjudicated decision runs the agent, even when file review was also reconciled", () => {
|
|
147
179
|
const decisions = new Map([["tc-1", ApprovalAction.APPROVE]]);
|
|
148
180
|
expect(decideReinvocation(facts({ approvalDecisions: decisions, reconciledFileReview: true }))).toBeUndefined();
|
|
149
|
-
expect(
|
|
150
|
-
decideReinvocation(facts({ approvalDecisions: new Map([["tc-1", ApprovalAction.SKIP]]) })),
|
|
151
|
-
"a SKIP is not a reject",
|
|
152
|
-
).toBeUndefined();
|
|
181
|
+
expect(decideReinvocation(facts({ approvalDecisions: new Map([["tc-1", ApprovalAction.SKIP]]) }))).toBeUndefined();
|
|
153
182
|
});
|
|
154
183
|
|
|
155
184
|
it("a pure file-review resume completes, carrying the reconcile's facts", () => {
|
|
@@ -176,3 +205,21 @@ describe("decideReinvocation", () => {
|
|
|
176
205
|
});
|
|
177
206
|
});
|
|
178
207
|
});
|
|
208
|
+
|
|
209
|
+
describe("regeneratesApprovedWrites", () => {
|
|
210
|
+
it("a deny-and-retry engine re-prompts the model with grants, so the runner pins the approved bytes", () => {
|
|
211
|
+
expect(regeneratesApprovedWrites("deny-and-retry")).toBe(true);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("a callback engine's resumed session re-attempts the call, so the runner pins the approved bytes", () => {
|
|
215
|
+
expect(regeneratesApprovedWrites("callback")).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("an interrupt engine applies the exact args it paused on — a runner-side write would land twice and pre-empt the row", () => {
|
|
219
|
+
expect(regeneratesApprovedWrites("interrupt")).toBe(false);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("a gate-less engine has nothing approved to apply", () => {
|
|
223
|
+
expect(regeneratesApprovedWrites("none")).toBe(false);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime's one reader and one settler of a user's approval decision.
|
|
3
|
+
*
|
|
4
|
+
* A decision lives on the tool-call row the server owns
|
|
5
|
+
* (`ToolCall.approval_action`, written by `SubmitApproval`); the runtime
|
|
6
|
+
* reads it at the start of a reinvocation and settles the rows that never
|
|
7
|
+
* run at the end of it. Both acts are here so the two halves of one
|
|
8
|
+
* transition cannot drift apart:
|
|
9
|
+
*
|
|
10
|
+
* - {@link approvalDecisionsOf} reads every top-level row still
|
|
11
|
+
* WAITING_APPROVAL whose action is set. The WAITING filter is what keeps a
|
|
12
|
+
* decided row from being re-gated on the NEXT reinvocation (its row is
|
|
13
|
+
* COMPLETED or SKIPPED by then) and what lets the adapter still describe
|
|
14
|
+
* the decision to its engine on THIS one (Cursor's reinvocation prompt
|
|
15
|
+
* names the skipped and rejected actions in human terms).
|
|
16
|
+
* - {@link terminalizeNonExecutingDecisions} settles SKIP and REJECT rows to
|
|
17
|
+
* TOOL_CALL_SKIPPED (REJECT also carries `error: "Rejected by user"`),
|
|
18
|
+
* root and sub-agent transcripts alike. The runtime calls it once,
|
|
19
|
+
* immediately after `adapter.runTurn` returns and before any persist of
|
|
20
|
+
* the outcome, for EVERY outcome: a decision that never runs the tool has
|
|
21
|
+
* no engine event to flip its row, so the row would otherwise persist
|
|
22
|
+
* WAITING on a completed execution. APPROVE and APPROVE_ALL are not
|
|
23
|
+
* touched — the tool executes, and the adapter's own events settle it.
|
|
24
|
+
*
|
|
25
|
+
* Why after the adapter, not before: both readers above filter on WAITING, so
|
|
26
|
+
* stamping first would hide the decision from the reinvocation prompt and
|
|
27
|
+
* from the exact-apply. Why by decision and not by row status: the outcome
|
|
28
|
+
* of SKIP and REJECT is fully determined by the recorded decision, whatever
|
|
29
|
+
* transient status an engine left (a denial ToolMessage with no tool events,
|
|
30
|
+
* or a replay that never re-drove the gate), so the stamp is idempotent and
|
|
31
|
+
* checkpointer-independent by construction. REJECT and SKIP share SKIPPED as
|
|
32
|
+
* the terminal status (the tool did not run) and stay distinguishable by
|
|
33
|
+
* `approval_action` and by the server's append-only approval-event stream.
|
|
34
|
+
*
|
|
35
|
+
* This is the contract as of stigmer#197: REJECT denies the tool and the run
|
|
36
|
+
* CONTINUES; it never fails the execution. The native harness proved it
|
|
37
|
+
* (`hitl.ts` `reconcileNonExecutingDecisions`, moved here at S3 M1) and the
|
|
38
|
+
* conformance suite (`agentexecution-approval.conformance.test.ts`) is its
|
|
39
|
+
* arbiter for both harnesses.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
43
|
+
import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
44
|
+
import { ApprovalAction, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
45
|
+
|
|
46
|
+
/** The row's error for a REJECT; byte-pinned by the native goldens (`reject.turn2.status.json`) and the conformance suite. */
|
|
47
|
+
export const REJECTED_BY_USER_ERROR = "Rejected by user";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Every top-level tool-call row still WAITING_APPROVAL whose `approvalAction`
|
|
51
|
+
* the server has set, keyed by tool-call id, in transcript order. Derived
|
|
52
|
+
* from the status on every invocation, never stored, so it cannot drift from
|
|
53
|
+
* the rows. The same rows, in the same order, that the Cursor adapter's
|
|
54
|
+
* `reconstructAdjudicatedApprovals` reads for its own facts (the
|
|
55
|
+
* pending-approval protos and content digests); a test pins the two
|
|
56
|
+
* readers' agreement.
|
|
57
|
+
*/
|
|
58
|
+
export function approvalDecisionsOf(status: AgentExecutionStatus): ReadonlyMap<string, ApprovalAction> {
|
|
59
|
+
const decisions = new Map<string, ApprovalAction>();
|
|
60
|
+
for (const message of status.messages) {
|
|
61
|
+
for (const row of message.toolCalls) {
|
|
62
|
+
if (row.status !== ToolCallStatus.TOOL_CALL_WAITING_APPROVAL) continue;
|
|
63
|
+
if (row.approvalAction === ApprovalAction.UNSPECIFIED) continue;
|
|
64
|
+
decisions.set(row.id, row.approvalAction);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return decisions;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Settle every SKIP and REJECT row to TOOL_CALL_SKIPPED, root and sub-agent
|
|
72
|
+
* transcripts, in place. Idempotent: a row already settled carries the same
|
|
73
|
+
* decision and re-settles identically. A REJECT row's `error` is set only
|
|
74
|
+
* when empty, so an engine's own denial text, if any, stands.
|
|
75
|
+
*/
|
|
76
|
+
export function terminalizeNonExecutingDecisions(status: AgentExecutionStatus): void {
|
|
77
|
+
const settle = (messages: readonly AgentMessage[]): void => {
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
for (const row of message.toolCalls) {
|
|
80
|
+
switch (row.approvalAction) {
|
|
81
|
+
case ApprovalAction.SKIP:
|
|
82
|
+
row.status = ToolCallStatus.TOOL_CALL_SKIPPED;
|
|
83
|
+
break;
|
|
84
|
+
case ApprovalAction.REJECT:
|
|
85
|
+
row.status = ToolCallStatus.TOOL_CALL_SKIPPED;
|
|
86
|
+
if (!row.error) row.error = REJECTED_BY_USER_ERROR;
|
|
87
|
+
break;
|
|
88
|
+
case ApprovalAction.UNSPECIFIED:
|
|
89
|
+
case ApprovalAction.APPROVE:
|
|
90
|
+
case ApprovalAction.APPROVE_ALL:
|
|
91
|
+
break;
|
|
92
|
+
default: {
|
|
93
|
+
const exhaustive: never = row.approvalAction;
|
|
94
|
+
throw new Error(`terminalizeNonExecutingDecisions: unknown approval action ${String(exhaustive)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
settle(status.messages);
|
|
101
|
+
for (const subAgent of status.subAgentExecutions) {
|
|
102
|
+
settle(subAgent.messages);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
* Every row of the matrix below is either a flag here or internal to one
|
|
10
10
|
* adapter; nothing in it needed a third kind of thing.
|
|
11
11
|
*
|
|
12
|
+
* One flag is declared for the matrix and read by no runtime phase today:
|
|
13
|
+
* `systemPrompt`. Its readers are the adapters' own prompt builders, by
|
|
14
|
+
* placement (the native builder renders a system prompt rebuilt every turn;
|
|
15
|
+
* the Cursor builder renders the session's first user message), and the
|
|
16
|
+
* shared prompt glue (`shared/prompt-sections.ts`) is written so that
|
|
17
|
+
* placement stays each adapter's. The runtime would branch on it only if it
|
|
18
|
+
* ever composed prompts itself — the S4/S5 unification question, not a phase
|
|
19
|
+
* that exists (S3 M5, Q-M5-6).
|
|
20
|
+
*
|
|
12
21
|
* The matrix the contract was designed against (2026-09; Claude and Codex are
|
|
13
22
|
* the surveyed SDKs, not built harnesses):
|
|
14
23
|
*
|
|
@@ -63,13 +72,15 @@ export type PausePrimitive = "interrupt" | "deny-and-retry" | "callback" | "none
|
|
|
63
72
|
export type StateIdSource = "deterministic" | "engine-minted";
|
|
64
73
|
|
|
65
74
|
/**
|
|
66
|
-
* The two facts the runtime's file-review reconcile
|
|
67
|
-
* and cannot know: the id the projection reads from the
|
|
68
|
-
* (`shared/filereview/capture.ts`
|
|
75
|
+
* The two facts the runtime's file-review capture and reconcile need from
|
|
76
|
+
* the harness and cannot know: the id the projection reads from the
|
|
77
|
+
* BASELINE payload (`shared/filereview/capture.ts`), and the
|
|
69
78
|
* workspace-relative paths the harness writes into the repo that a turn's
|
|
70
79
|
* diff must never show (Cursor's transient `.cursor/hooks.json`). Declared
|
|
71
|
-
* once per adapter, read by `
|
|
72
|
-
*
|
|
80
|
+
* once per adapter, read by `harness/capture.ts` (the pin, the progress
|
|
81
|
+
* slice, the candidate) and `turn-context.ts` (the reconcile). Ruled Q-M2-6
|
|
82
|
+
* as an argument, homed here at S2 M3; the capture joined the reconcile as a
|
|
83
|
+
* reader at S3 M4.
|
|
73
84
|
*/
|
|
74
85
|
export interface FileReviewIdentity {
|
|
75
86
|
readonly harnessId: string;
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime's file-review capture — apply-then-review for every harness,
|
|
3
|
+
* written once: the baseline pinned before the engine runs, the mid-run
|
|
4
|
+
* progress the chokepoint refreshes on every write, the candidate captured
|
|
5
|
+
* after the engine's turn, the DD-28 approved-command provenance, the flowed
|
|
6
|
+
* transcript rows stamped with the change set, and the one question the
|
|
7
|
+
* outcome table asks afterwards: is a review pending?
|
|
8
|
+
*
|
|
9
|
+
* ONE CAPTURE, ONE MOMENT, ONE OWNER
|
|
10
|
+
* ----------------------------------
|
|
11
|
+
* A turn's reviewable change is a property of the TREE, not of the engine:
|
|
12
|
+
* whatever sits on the working tree when the engine's turn is over is what
|
|
13
|
+
* the user reviews, whoever wrote it — the engine, a sub-agent, a recovery
|
|
14
|
+
* retry, a stdio MCP server the engine spawned. So the runtime pins the
|
|
15
|
+
* baseline as its LAST act before `adapter.runTurn` (after its own writes
|
|
16
|
+
* into the tree — the reconcile, the skill and attachment mounts — so they
|
|
17
|
+
* cancel out of the diff) and captures ONCE after `runTurn` returns, over
|
|
18
|
+
* the whole turn. Anything written into the tree after the pin is the
|
|
19
|
+
* turn's change. An adapter needs neither the baseline nor the change-set
|
|
20
|
+
* id and never learns whether a review is pending; the one fact it holds
|
|
21
|
+
* that the runtime cannot read is what its engine observed touching
|
|
22
|
+
* CAS-owned paths (`TurnSink.bindCasObservations`).
|
|
23
|
+
*
|
|
24
|
+
* Until S3 M4 both adapters carried this whole act (Cursor in its
|
|
25
|
+
* `turn-boundary.ts` between the stream and `run.wait()`, the deep-agent in
|
|
26
|
+
* its `turn-settle.ts`), each with its own evidence source and its own
|
|
27
|
+
* scoping rules for the provenance and the stamp. The two rules here are the
|
|
28
|
+
* one reading both engines already produce:
|
|
29
|
+
*
|
|
30
|
+
* - PROVENANCE (DD-28, `shared/filereview/command-provenance.ts` is the
|
|
31
|
+
* rule; this is its two inputs). "This turn's commands" are the top-level
|
|
32
|
+
* rows whose id had not SETTLED before the engine ran — never a message
|
|
33
|
+
* position, because on BOTH engines an approved command executes on the
|
|
34
|
+
* row the runtime seeded for it (the deep-agent's builder and Cursor's
|
|
35
|
+
* translator both reconcile the re-attempt onto the seeded WAITING row),
|
|
36
|
+
* at its prior position; a positional scope misses it (F-M4-P3: Cursor's
|
|
37
|
+
* did, so its auto-keep never qualified a gated-then-approved command).
|
|
38
|
+
* "Executed" is a COMPLETED row. "Consented" is the row's own
|
|
39
|
+
* server-authored `approval_action` — the seeded row keeps its id and its
|
|
40
|
+
* verdict when it executes on resume, so the executed command IS its
|
|
41
|
+
* consent row. Any sub-agent activity fails closed (DD-28 D1).
|
|
42
|
+
* - THE STAMP (`shared/tool-row.ts` `stampFlowedFileEditRows`). A write or
|
|
43
|
+
* delete row created this turn that reached COMPLETED flowed onto the
|
|
44
|
+
* tree and is badged with the change set; a WAITING (denied, gated) or
|
|
45
|
+
* FAILED row did not and is not. The row's own status is the evidence,
|
|
46
|
+
* not a ledger of denial tokens.
|
|
47
|
+
*
|
|
48
|
+
* WHAT IS NOT HERE
|
|
49
|
+
* ----------------
|
|
50
|
+
* The substrate and the ledger authoring (`shared/filereview/capture.ts`,
|
|
51
|
+
* `cas-touched.ts`, `progress.ts`); the resume-time reconcile
|
|
52
|
+
* (`turn-context.ts` `reconcileReinvocation`, already the runtime's since
|
|
53
|
+
* S2); the outcome matrix that turns "a review is pending" into a phase
|
|
54
|
+
* (`run-turn.ts`, through `terminal-table.ts` `awaitingReviewArm`).
|
|
55
|
+
*
|
|
56
|
+
* TWO GAPS THIS MODULE DOES NOT CLOSE (recorded for S5, S3 M4 F-M4-P-gaps)
|
|
57
|
+
* ----------------------------------------------------------------------
|
|
58
|
+
* A turn that ends `interrupted` is not captured (a stop fired; the tree may
|
|
59
|
+
* be mid-edit). So a platform STOP returns COMPLETED with unreviewed edits
|
|
60
|
+
* on the tree, and a pause or worker shutdown reinvokes the same turn, whose
|
|
61
|
+
* new pin absorbs the pre-pause edits into its baseline and authors a second
|
|
62
|
+
* BASELINE for the same change set. Both predate the lift on both harnesses.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
import { ApprovalAction, FileCaptureClass, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
66
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
67
|
+
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
68
|
+
import type { TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
69
|
+
|
|
70
|
+
import type { ArtifactStorage } from "../shared/artifact-storage.js";
|
|
71
|
+
import { captureBaselineToLedger, captureCandidateToLedger } from "../shared/filereview/capture.js";
|
|
72
|
+
import { createCasProgressSubstrate } from "../shared/filereview/cas-progress.js";
|
|
73
|
+
import { buildCasTurnCaptures, type CasTouchedReader, type CasTouchedSnapshot } from "../shared/filereview/cas-touched.js";
|
|
74
|
+
import { qualifyTurnCommandProvenance } from "../shared/filereview/command-provenance.js";
|
|
75
|
+
import { hasCandidateCaptured } from "../shared/filereview/events.js";
|
|
76
|
+
import {
|
|
77
|
+
createGitProgressSubstrate,
|
|
78
|
+
createHybridProgressSubstrate,
|
|
79
|
+
newProgressCaptureState,
|
|
80
|
+
type ProgressCaptureState,
|
|
81
|
+
type ProgressSubstrate,
|
|
82
|
+
} from "../shared/filereview/progress.js";
|
|
83
|
+
import { collectSettledToolCallIds, collectSubAgentToolCallIds, stampFlowedFileEditRows, stampFlowedSubAgentFileEditRows } from "../shared/tool-row.js";
|
|
84
|
+
import type { FileReviewIdentity } from "./capabilities.js";
|
|
85
|
+
import type { TurnWorkspace } from "./types.js";
|
|
86
|
+
|
|
87
|
+
/** The mid-run progress the chokepoint refreshes on every write: the substrate and the floor it throttles by. */
|
|
88
|
+
export interface TurnProgress {
|
|
89
|
+
readonly changeSetId: string;
|
|
90
|
+
readonly substrate: ProgressSubstrate;
|
|
91
|
+
readonly state: ProgressCaptureState;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* One turn's capture as the runtime holds it between the pin and the
|
|
96
|
+
* boundary — a `runTurn` closure value beside the usage accumulator and the
|
|
97
|
+
* watchdog, on neither `TurnInput` (the adapter needs none of it) nor the
|
|
98
|
+
* frame (nothing here is a resource to release). Absent outside capture
|
|
99
|
+
* mode.
|
|
100
|
+
*/
|
|
101
|
+
export interface TurnCapture {
|
|
102
|
+
readonly changeSetId: string;
|
|
103
|
+
/** The pre-turn tree on a git work tree; `""` on a non-git one (the CAS manifest is the baseline there). */
|
|
104
|
+
readonly baselineTree: string;
|
|
105
|
+
readonly progress: TurnProgress;
|
|
106
|
+
/** Top-level rows already settled before the engine ran: the complement is "this turn's" (the provenance scope). */
|
|
107
|
+
readonly priorSettledToolCallIds: ReadonlySet<string>;
|
|
108
|
+
/** Sub-agent rows present before the engine ran: the stamp's scope, and any growth fails provenance closed. */
|
|
109
|
+
readonly priorSubAgentToolCallIds: ReadonlySet<string>;
|
|
110
|
+
/**
|
|
111
|
+
* What the engine observed touching CAS-owned paths, once the adapter has
|
|
112
|
+
* bound it (`TurnSink.bindCasObservations`); read at each progress capture
|
|
113
|
+
* and at the boundary. Unbound reads as nothing observed: git-only capture.
|
|
114
|
+
*/
|
|
115
|
+
casObservations: CasTouchedReader | undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const NOTHING_OBSERVED: CasTouchedSnapshot = { before: new Map(), blockedSecretPaths: new Set() };
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Turn start, in capture mode: snapshot the pre-turn ids, pin the pre-turn
|
|
122
|
+
* tree behind the baseline ref and author BASELINE_CAPTURED under the
|
|
123
|
+
* harness's identity (the event rides the next persist), and build the
|
|
124
|
+
* progress substrate for this workspace shape — the git slice over the
|
|
125
|
+
* baseline and the harness's excluded paths, the CAS slice over whatever
|
|
126
|
+
* observations the adapter later binds (a delegating reader, since the bind
|
|
127
|
+
* happens inside `runTurn`), hybrid when both apply. The CAS slice exists
|
|
128
|
+
* only when artifact storage does: without it a gitignored write is
|
|
129
|
+
* deny-gated by the harness (`captureIgnored`), never captured, and the
|
|
130
|
+
* ledger could not store its blobs anyway.
|
|
131
|
+
*
|
|
132
|
+
* Returns `undefined` outside capture mode: nothing to pin, nothing to
|
|
133
|
+
* capture, the classic deny-gate governs writes.
|
|
134
|
+
*/
|
|
135
|
+
export async function pinCaptureBaseline(args: {
|
|
136
|
+
readonly status: AgentExecutionStatus;
|
|
137
|
+
readonly executionId: string;
|
|
138
|
+
readonly workspace: TurnWorkspace;
|
|
139
|
+
readonly fileReview: FileReviewIdentity;
|
|
140
|
+
readonly artifactStorage: ArtifactStorage | undefined;
|
|
141
|
+
}): Promise<TurnCapture | undefined> {
|
|
142
|
+
const { status, executionId, workspace, fileReview, artifactStorage } = args;
|
|
143
|
+
if (!workspace.captureMode) return undefined;
|
|
144
|
+
const { primaryDir, gitWorkspace, changeSetId } = workspace;
|
|
145
|
+
|
|
146
|
+
const priorSettledToolCallIds = collectSettledToolCallIds(status.messages);
|
|
147
|
+
const priorSubAgentToolCallIds = collectSubAgentToolCallIds(status.subAgentExecutions);
|
|
148
|
+
|
|
149
|
+
const baselineTree = await captureBaselineToLedger({
|
|
150
|
+
status,
|
|
151
|
+
gitRoot: primaryDir,
|
|
152
|
+
executionId,
|
|
153
|
+
changeSetId,
|
|
154
|
+
harnessId: fileReview.harnessId,
|
|
155
|
+
excludePaths: fileReview.excludePaths,
|
|
156
|
+
gitWorkspace,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const capture: TurnCapture = {
|
|
160
|
+
changeSetId,
|
|
161
|
+
baselineTree,
|
|
162
|
+
progress: {
|
|
163
|
+
changeSetId,
|
|
164
|
+
state: newProgressCaptureState(),
|
|
165
|
+
substrate: buildProgressSubstrate({
|
|
166
|
+
executionId,
|
|
167
|
+
workspace,
|
|
168
|
+
baselineTree,
|
|
169
|
+
excludePaths: fileReview.excludePaths,
|
|
170
|
+
casSlice: artifactStorage !== undefined,
|
|
171
|
+
// Delegates to whatever the adapter binds later; `capture` is
|
|
172
|
+
// assigned before any capture() can run.
|
|
173
|
+
readObservations: () => capture.casObservations?.() ?? NOTHING_OBSERVED,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
priorSettledToolCallIds,
|
|
177
|
+
priorSubAgentToolCallIds,
|
|
178
|
+
casObservations: undefined,
|
|
179
|
+
};
|
|
180
|
+
return capture;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The progress substrate for this workspace shape (DD-32 / DD-33): a git
|
|
185
|
+
* tree diffs its tracked paths by `--numstat` against the baseline; the CAS
|
|
186
|
+
* slice reads the engine's observations for the gitignored (git tree) or
|
|
187
|
+
* every (non-git) path; a git tree with storage composes both. The truth
|
|
188
|
+
* table is `deriveCaptureMode`'s: capture mode holds only for a git tree or
|
|
189
|
+
* a workspace with storage, so the fourth cell is unreachable and says so.
|
|
190
|
+
*/
|
|
191
|
+
function buildProgressSubstrate(args: {
|
|
192
|
+
readonly executionId: string;
|
|
193
|
+
readonly workspace: TurnWorkspace;
|
|
194
|
+
readonly baselineTree: string;
|
|
195
|
+
readonly excludePaths: readonly string[];
|
|
196
|
+
readonly casSlice: boolean;
|
|
197
|
+
readonly readObservations: CasTouchedReader;
|
|
198
|
+
}): ProgressSubstrate {
|
|
199
|
+
const { executionId, workspace, baselineTree, excludePaths, casSlice, readObservations } = args;
|
|
200
|
+
const git = workspace.gitWorkspace
|
|
201
|
+
? createGitProgressSubstrate({ workspaceRoot: workspace.primaryDir, executionId, baselineTree, excludePaths })
|
|
202
|
+
: undefined;
|
|
203
|
+
const cas = casSlice ? createCasProgressSubstrate({ workspaceRoot: workspace.primaryDir, read: readObservations }) : undefined;
|
|
204
|
+
if (git && cas) return createHybridProgressSubstrate(git, cas);
|
|
205
|
+
const one = git ?? cas;
|
|
206
|
+
if (!one) throw new Error(`capture: capture mode on a non-git tree without storage (execution=${executionId}); deriveCaptureMode forbids this`);
|
|
207
|
+
return one;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Turn end: compose the CAS captures from the bound observations, derive the
|
|
212
|
+
* approved-command provenance, author CANDIDATE_CAPTURED (the seam authors
|
|
213
|
+
* nothing for a turn that changed nothing), and — iff a candidate exists —
|
|
214
|
+
* stamp the flowed rows. The working tree is LEFT applied: the user reviews
|
|
215
|
+
* the real change, and a reject snaps files back on the resume
|
|
216
|
+
* (`reconcileReinvocation`). Returns whether a review is now pending.
|
|
217
|
+
*/
|
|
218
|
+
export async function captureCandidate(args: {
|
|
219
|
+
readonly status: AgentExecutionStatus;
|
|
220
|
+
readonly executionId: string;
|
|
221
|
+
readonly workspace: TurnWorkspace;
|
|
222
|
+
readonly fileReview: FileReviewIdentity;
|
|
223
|
+
readonly artifactStorage: ArtifactStorage | undefined;
|
|
224
|
+
readonly capture: TurnCapture;
|
|
225
|
+
/** `spec.auto_approve_all`, the one whole-run bypass: qualifies every executed command as consented (DD-28). */
|
|
226
|
+
readonly globalBypass: boolean;
|
|
227
|
+
}): Promise<boolean> {
|
|
228
|
+
const { status, executionId, workspace, fileReview, artifactStorage, capture, globalBypass } = args;
|
|
229
|
+
const { primaryDir, gitWorkspace } = workspace;
|
|
230
|
+
const { changeSetId } = capture;
|
|
231
|
+
|
|
232
|
+
const casCaptureClass = gitWorkspace ? FileCaptureClass.GIT_IGNORED_CAPTURED : FileCaptureClass.NON_GIT_CAS;
|
|
233
|
+
const observed = artifactStorage && capture.casObservations ? await capture.casObservations() : NOTHING_OBSERVED;
|
|
234
|
+
const { casCaptures, unreviewablePaths } = await buildCasTurnCaptures(observed, primaryDir, casCaptureClass);
|
|
235
|
+
|
|
236
|
+
const commandProvenance = deriveCommandProvenance(status, capture, globalBypass);
|
|
237
|
+
if (commandProvenance) {
|
|
238
|
+
console.log(
|
|
239
|
+
`capture: turn qualifies for approved-command auto-keep ` +
|
|
240
|
+
`(consent rows: ${commandProvenance.consentToolCallIds.join(",") || "(auto_approve_all)"}); ` +
|
|
241
|
+
`attaching provenance to candidate (execution=${executionId})`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
await captureCandidateToLedger({
|
|
246
|
+
status,
|
|
247
|
+
gitRoot: primaryDir,
|
|
248
|
+
executionId,
|
|
249
|
+
changeSetId,
|
|
250
|
+
baselineTree: capture.baselineTree,
|
|
251
|
+
harnessId: fileReview.harnessId,
|
|
252
|
+
excludePaths: fileReview.excludePaths,
|
|
253
|
+
casCaptures,
|
|
254
|
+
storage: artifactStorage,
|
|
255
|
+
unreviewablePaths,
|
|
256
|
+
unreviewableCaptureClass: casCaptureClass,
|
|
257
|
+
gitWorkspace,
|
|
258
|
+
commandProvenance,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const pending = hasCandidateCaptured(status, changeSetId);
|
|
262
|
+
if (pending) {
|
|
263
|
+
// A row must never reference a change set that does not exist, so the
|
|
264
|
+
// stamp is gated on the authored candidate.
|
|
265
|
+
stampFlowedFileEditRows(status.messages, changeSetId, { flowed: isCompletedRow });
|
|
266
|
+
stampFlowedSubAgentFileEditRows(status.subAgentExecutions, changeSetId, capture.priorSubAgentToolCallIds, isCompletedRow);
|
|
267
|
+
console.log(`capture: change set ${changeSetId} authored to the file_review ledger; working tree left applied for review (execution=${executionId})`);
|
|
268
|
+
}
|
|
269
|
+
return pending;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** The row's own evidence that its effect landed on the tree. */
|
|
273
|
+
function isCompletedRow(tc: ToolCall): boolean {
|
|
274
|
+
return tc.status === ToolCallStatus.TOOL_CALL_COMPLETED;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The runtime's two inputs to the shared DD-28 rule (see the header). Pure
|
|
279
|
+
* over the status and the pre-turn snapshots; exported for its own tests.
|
|
280
|
+
*/
|
|
281
|
+
export function deriveCommandProvenance(
|
|
282
|
+
status: AgentExecutionStatus,
|
|
283
|
+
capture: Pick<TurnCapture, "priorSettledToolCallIds" | "priorSubAgentToolCallIds">,
|
|
284
|
+
globalBypass: boolean,
|
|
285
|
+
): TurnCommandProvenance | undefined {
|
|
286
|
+
// DD-28 D1: any sub-agent activity this turn disqualifies — a sub-agent
|
|
287
|
+
// that ran contributes at least one row id absent from the pre-turn set,
|
|
288
|
+
// and its writes fold into this change set without a consented command.
|
|
289
|
+
for (const id of collectSubAgentToolCallIds(status.subAgentExecutions)) {
|
|
290
|
+
if (!capture.priorSubAgentToolCallIds.has(id)) return undefined;
|
|
291
|
+
}
|
|
292
|
+
const turnToolCalls = status.messages.flatMap((m) => m.toolCalls).filter((tc) => !capture.priorSettledToolCallIds.has(tc.id));
|
|
293
|
+
return qualifyTurnCommandProvenance({
|
|
294
|
+
turnToolCalls,
|
|
295
|
+
messages: status.messages,
|
|
296
|
+
isExecutedCommand: isCompletedRow,
|
|
297
|
+
resolveDirectConsent: (tc) =>
|
|
298
|
+
tc.approvalAction === ApprovalAction.APPROVE || tc.approvalAction === ApprovalAction.APPROVE_ALL ? tc.id : undefined,
|
|
299
|
+
globalBypass,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
@@ -10,13 +10,21 @@
|
|
|
10
10
|
* any built-in write row targeting a secret-like path, top-level and
|
|
11
11
|
* sub-agent. The single airtight choke point for a deny-gate harness and
|
|
12
12
|
* the only guarantee under `auto_approve_all`. Idempotent.
|
|
13
|
-
* 2. `
|
|
13
|
+
* 2. `file_change_progress` is refreshed from the turn's capture (the "N
|
|
14
|
+
* files changed so far" strip, DD-32) when the turn is in capture mode:
|
|
15
|
+
* the tree is read at most once per floor interval and only re-attached
|
|
16
|
+
* when it moved (`shared/filereview/progress.ts`), so a write that
|
|
17
|
+
* changed nothing on disk carries the snapshot it already had. Before
|
|
18
|
+
* S3 M4 each adapter's stream loop did this before its own persists; the
|
|
19
|
+
* runtime owns the capture now, so the display field it derives is
|
|
20
|
+
* refreshed where every other runtime-owned field is — here.
|
|
21
|
+
* 3. `streaming_usage` is refreshed from the accumulator when it has turns,
|
|
14
22
|
* so the summary the UI shows is the one the write carries.
|
|
15
|
-
*
|
|
23
|
+
* 4. `persistStatus` (`shared/status.ts`): tool-output offload, the
|
|
16
24
|
* aggregate size cap, transient retry, oversize recovery. Never rejects.
|
|
17
|
-
*
|
|
25
|
+
* 5. A Temporal heartbeat, so a long streaming turn's liveness is proven by
|
|
18
26
|
* the writes it makes (Q-S2-10).
|
|
19
|
-
*
|
|
27
|
+
* 6. The control plane's answer: STOP becomes the runtime's platform-stop
|
|
20
28
|
* cause on the stop controller, which aborts the adapter's signal.
|
|
21
29
|
*
|
|
22
30
|
* Single-flight with coalescing: an adapter's `requestPersist()` while a
|
|
@@ -39,9 +47,11 @@ import { ExecutionControlSignal } from "@stigmer/protos/ai/stigmer/agentic/agent
|
|
|
39
47
|
import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
|
|
40
48
|
|
|
41
49
|
import type { StigmerClient } from "../client/stigmer-client.js";
|
|
50
|
+
import { captureFileChangeProgress } from "../shared/filereview/progress.js";
|
|
42
51
|
import { persistStatus } from "../shared/status.js";
|
|
43
52
|
import type { ToolOutputOffloadContext } from "../shared/status-offload.js";
|
|
44
53
|
import { withholdSecretContentFromMessages } from "../shared/tool-row.js";
|
|
54
|
+
import type { TurnProgress } from "./capture.js";
|
|
45
55
|
import type { UsageAccumulator } from "./usage-accumulator.js";
|
|
46
56
|
|
|
47
57
|
export interface PersistChokepointDeps {
|
|
@@ -56,6 +66,12 @@ export interface PersistChokepointDeps {
|
|
|
56
66
|
* and is `undefined` for a write made before then (no usage yet anyway).
|
|
57
67
|
*/
|
|
58
68
|
readonly usage: () => UsageAccumulator | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* The turn's mid-run progress, read at WRITE time for the same reason as
|
|
71
|
+
* `usage`: it exists only once the runtime has pinned the capture baseline,
|
|
72
|
+
* after resolution, and is `undefined` outside capture mode.
|
|
73
|
+
*/
|
|
74
|
+
readonly progress: () => TurnProgress | undefined;
|
|
59
75
|
/** The Temporal heartbeat, pulsed after each write. */
|
|
60
76
|
readonly heartbeat: () => void;
|
|
61
77
|
/** The control plane answered STOP to this write. */
|
|
@@ -99,6 +115,10 @@ export class PersistChokepoint {
|
|
|
99
115
|
async write(): Promise<void> {
|
|
100
116
|
const { client, executionId, status, offload, heartbeat } = this.deps;
|
|
101
117
|
withholdSecretContentFromMessages(status.messages, status.subAgentExecutions);
|
|
118
|
+
const progress = this.deps.progress();
|
|
119
|
+
if (progress) {
|
|
120
|
+
await captureFileChangeProgress({ status, changeSetId: progress.changeSetId, substrate: progress.substrate, state: progress.state });
|
|
121
|
+
}
|
|
102
122
|
const usage = this.deps.usage();
|
|
103
123
|
if (usage?.hasTurns) {
|
|
104
124
|
status.streamingUsage = create(StreamingUsageSummarySchema, usage.snapshot());
|