@stigmer/runner 3.15.3-dev.20260914100347 → 3.15.3-dev.20260916211208
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 +1 -0
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/approval-contract/types.d.ts +1 -1
- package/dist/__test-utils__/hermetic-activity.d.ts +61 -7
- package/dist/__test-utils__/hermetic-activity.js +100 -9
- package/dist/__test-utils__/hermetic-activity.js.map +1 -1
- package/dist/__test-utils__/model-registry-fixture.d.ts +22 -3
- package/dist/__test-utils__/model-registry-fixture.js +22 -7
- package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +2 -2
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +14 -11
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +61 -0
- package/dist/activities/execute-cursor/__test-utils__/fold.js +94 -0
- package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +1 -1
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +52 -14
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +46 -12
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +21 -28
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -11
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +70 -26
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +82 -32
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.d.ts +8 -7
- package/dist/activities/execute-cursor/adapter.js +13 -9
- package/dist/activities/execute-cursor/adapter.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +10 -10
- package/dist/activities/execute-cursor/approval-policy.js +12 -23
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +35 -1
- package/dist/activities/execute-cursor/approval-state.js +65 -0
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/{message-translator.d.ts → boundary-rows.d.ts} +45 -341
- package/dist/activities/execute-cursor/boundary-rows.js +1018 -0
- package/dist/activities/execute-cursor/boundary-rows.js.map +1 -0
- package/dist/activities/execute-cursor/cursor-capabilities.d.ts +1 -1
- package/dist/activities/execute-cursor/cursor-capabilities.js +1 -1
- package/dist/activities/execute-cursor/cursor-event-recorder.d.ts +33 -14
- package/dist/activities/execute-cursor/cursor-event-recorder.js +43 -21
- package/dist/activities/execute-cursor/cursor-event-recorder.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +3 -3
- package/dist/activities/execute-cursor/prompt-builder.js +4 -4
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/sdk-warmup.d.ts +15 -6
- package/dist/activities/execute-cursor/sdk-warmup.js +15 -6
- package/dist/activities/execute-cursor/sdk-warmup.js.map +1 -1
- package/dist/activities/execute-cursor/service-tier.js +1 -2
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- package/dist/activities/execute-cursor/session-lifecycle.d.ts +38 -59
- package/dist/activities/execute-cursor/session-lifecycle.js +59 -96
- package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
- package/dist/activities/execute-cursor/session-store.d.ts +100 -0
- package/dist/activities/execute-cursor/session-store.js +157 -0
- package/dist/activities/execute-cursor/session-store.js.map +1 -0
- package/dist/activities/execute-cursor/sub-agent-steps.d.ts +79 -0
- package/dist/activities/execute-cursor/sub-agent-steps.js +180 -0
- package/dist/activities/execute-cursor/sub-agent-steps.js.map +1 -0
- package/dist/activities/execute-cursor/tool-result.d.ts +49 -0
- package/dist/activities/execute-cursor/tool-result.js +130 -0
- package/dist/activities/execute-cursor/tool-result.js.map +1 -0
- package/dist/activities/execute-cursor/translator.d.ts +178 -0
- package/dist/activities/execute-cursor/translator.js +485 -0
- package/dist/activities/execute-cursor/translator.js.map +1 -0
- package/dist/activities/execute-cursor/turn-boundary.d.ts +15 -5
- package/dist/activities/execute-cursor/turn-boundary.js +10 -14
- 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 +47 -34
- package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
- package/dist/activities/execute-cursor/turn-setup.d.ts +2 -2
- package/dist/activities/execute-cursor/turn-setup.js +5 -5
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +23 -14
- package/dist/activities/execute-cursor/turn-stream.js +41 -28
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-cursor/turn.d.ts +4 -4
- package/dist/activities/execute-cursor/turn.js +5 -5
- package/dist/activities/execute-cursor/usage-pricing.d.ts +2 -2
- package/dist/activities/execute-cursor/usage-pricing.js +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +16 -11
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -10
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +2 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +2 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +12 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +18 -4
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.d.ts +10 -1
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js +24 -11
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js.map +1 -1
- package/dist/activities/execute-deep-agent/adapter.d.ts +1 -1
- package/dist/activities/execute-deep-agent/adapter.js +1 -1
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +19 -19
- package/dist/activities/execute-deep-agent/approval-file-change.js +16 -15
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +2 -2
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +2 -2
- package/dist/activities/execute-deep-agent/hitl.d.ts +2 -2
- package/dist/activities/execute-deep-agent/hitl.js +2 -2
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +13 -3
- package/dist/activities/execute-deep-agent/inline-publisher.js +3 -3
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +3 -3
- package/dist/activities/execute-deep-agent/prompt-builder.js +6 -7
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +16 -8
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +20 -33
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +3 -3
- package/dist/activities/execute-deep-agent/subagent-transformer.js +1 -1
- package/dist/activities/execute-deep-agent/translator.d.ts +134 -0
- package/dist/activities/execute-deep-agent/translator.js +418 -0
- package/dist/activities/execute-deep-agent/translator.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-settle.d.ts +13 -7
- package/dist/activities/execute-deep-agent/turn-settle.js +34 -45
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +8 -8
- package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +13 -10
- package/dist/activities/execute-deep-agent/turn-stream.js +44 -41
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn.d.ts +3 -3
- package/dist/activities/execute-deep-agent/turn.js +3 -3
- package/dist/config.d.ts +2 -2
- package/dist/harness/approval-decisions.d.ts +1 -1
- package/dist/harness/approval-decisions.js +1 -1
- package/dist/harness/capabilities.d.ts +21 -11
- package/dist/harness/capabilities.js +2 -2
- package/dist/harness/capture.d.ts +4 -4
- package/dist/harness/capture.js +4 -4
- package/dist/harness/persist-chokepoint.d.ts +3 -3
- package/dist/harness/persist-chokepoint.js +3 -3
- package/dist/harness/registry.d.ts +2 -2
- package/dist/harness/registry.js +2 -2
- package/dist/harness/run-turn.d.ts +6 -4
- package/dist/harness/run-turn.js +31 -9
- package/dist/harness/run-turn.js.map +1 -1
- package/dist/harness/stop-controller.d.ts +1 -1
- package/dist/harness/stop-controller.js +1 -1
- package/dist/harness/terminal-table.d.ts +7 -7
- package/dist/harness/terminal-table.js +7 -7
- package/dist/harness/transcript/builder.d.ts +182 -0
- package/dist/harness/transcript/builder.js +693 -0
- package/dist/harness/transcript/builder.js.map +1 -0
- package/dist/harness/transcript/events.d.ts +218 -0
- package/dist/harness/transcript/events.js +50 -0
- package/dist/harness/transcript/events.js.map +1 -0
- package/dist/harness/transcript/state.d.ts +110 -0
- package/dist/harness/transcript/state.js +148 -0
- package/dist/harness/transcript/state.js.map +1 -0
- package/dist/harness/turn-context.d.ts +54 -38
- package/dist/harness/turn-context.js +42 -43
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/harness/types.d.ts +63 -22
- package/dist/harness/types.js +12 -11
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/usage-accumulator.d.ts +2 -2
- package/dist/harness/usage-accumulator.js +2 -2
- package/dist/harness-adapters.d.ts +5 -4
- package/dist/harness-adapters.js +5 -4
- package/dist/harness-adapters.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +6 -0
- package/dist/middleware/approval-gate.js +7 -57
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +3 -3
- package/dist/middleware/cost-advisory.js +3 -3
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/runner-manager.js +1 -1
- package/dist/runner-manager.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +53 -0
- package/dist/shared/approval-policy.js +76 -0
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +45 -22
- package/dist/shared/args-preview.js +51 -41
- package/dist/shared/args-preview.js.map +1 -1
- package/dist/shared/attachment-resolver.d.ts +4 -4
- package/dist/shared/attachment-resolver.js +4 -4
- package/dist/shared/attachment-vision.d.ts +2 -1
- package/dist/shared/attachment-vision.js.map +1 -1
- package/dist/shared/attachment-zip.d.ts +2 -2
- package/dist/shared/attachment-zip.js +2 -2
- package/dist/shared/cost-guard.d.ts +2 -2
- package/dist/shared/cost-guard.js +2 -2
- package/dist/shared/exact-apply.d.ts +1 -1
- package/dist/shared/exact-apply.js +1 -1
- package/dist/shared/execution-context.d.ts +2 -2
- package/dist/shared/execution-context.js +2 -2
- package/dist/shared/extract-structured-output.d.ts +2 -2
- package/dist/shared/extract-structured-output.js +2 -2
- package/dist/shared/filereview/cas-touched.d.ts +2 -2
- package/dist/shared/filereview/cas-touched.js +2 -2
- package/dist/shared/heartbeat.d.ts +1 -1
- package/dist/shared/heartbeat.js +1 -1
- package/dist/shared/llm-backend.d.ts +0 -4
- package/dist/shared/llm-backend.js +0 -4
- package/dist/shared/llm-backend.js.map +1 -1
- package/dist/shared/model-error.d.ts +1 -2
- package/dist/shared/model-error.js +1 -2
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/persist-decision.d.ts +18 -24
- package/dist/shared/persist-decision.js +19 -11
- package/dist/shared/persist-decision.js.map +1 -1
- package/dist/shared/prompt-sections.d.ts +4 -4
- package/dist/shared/prompt-sections.js +4 -4
- package/dist/shared/skill-resolver.d.ts +3 -3
- package/dist/shared/skill-resolver.js +3 -3
- package/dist/shared/status-offload.d.ts +1 -1
- package/dist/shared/status-offload.js +2 -2
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/streaming-scheduler.d.ts +2 -7
- package/dist/shared/streaming-scheduler.js +6 -14
- package/dist/shared/streaming-scheduler.js.map +1 -1
- package/dist/shared/subagent-rows.d.ts +2 -2
- package/dist/shared/subagent-rows.js +2 -2
- package/dist/shared/todos.d.ts +3 -3
- package/dist/shared/todos.js +3 -3
- package/dist/shared/tool-row.d.ts +19 -1
- package/dist/shared/tool-row.js +36 -2
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/worker-shutdown.d.ts +1 -1
- package/dist/shared/worker-shutdown.js +1 -1
- package/dist/shared/workspace/session-provision.d.ts +1 -1
- package/dist/shared/workspace/session-provision.js +1 -1
- package/dist/shared/workspace/sources/empty.d.ts +1 -1
- package/dist/shared/workspace/sources/empty.js +1 -2
- package/dist/shared/workspace/sources/empty.js.map +1 -1
- package/dist/shared/workspace/sources/git.js +4 -4
- package/dist/shared/workspace/sources/git.js.map +1 -1
- package/dist/shared/workspace/sources/local-path.js +6 -6
- package/dist/shared/workspace/sources/local-path.js.map +1 -1
- package/dist/shared/workspace/stigmer-link.d.ts +1 -1
- package/dist/shared/workspace/stigmer-link.js +1 -1
- package/dist/shared/workspace/types.d.ts +2 -5
- package/dist/shared/workspace/types.js +0 -6
- package/dist/shared/workspace/types.js.map +1 -1
- package/dist/shared/workspace/writeback-coordinator.d.ts +17 -4
- package/dist/shared/workspace/writeback-coordinator.js +7 -9
- package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
- package/package.json +5 -5
- package/src/__test-utils__/__tests__/execution-record-barrier.test.ts +70 -0
- package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +35 -1
- package/src/__test-utils__/approval-contract/contract.ts +1 -1
- package/src/__test-utils__/approval-contract/types.ts +1 -1
- package/src/__test-utils__/harness-boot-order-child.ts +1 -1
- package/src/__test-utils__/harness-contract/contract.ts +8 -8
- package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +52 -16
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +47 -43
- package/src/__test-utils__/hermetic-activity.ts +113 -9
- package/src/__test-utils__/model-registry-fixture.ts +31 -7
- package/src/__test-utils__/module-specifiers.ts +32 -3
- package/src/__tests__/harness-boot-order.test.ts +1 -1
- package/src/__tests__/harness-contract.test.ts +1 -1
- package/src/__tests__/runner-manager.test.ts +1 -1
- package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +32 -8
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +12 -10
- package/src/activities/execute-cursor/__test-utils__/fold.ts +116 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +1 -1
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +72 -19
- package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +35 -29
- package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +94 -38
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +14 -21
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +3 -3
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/cursor-baseurl-routing.test.ts +10 -8
- package/src/activities/execute-cursor/__tests__/cursor-event-recorder.test.ts +142 -0
- package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +24 -12
- package/src/activities/execute-cursor/__tests__/cursor-generate-image-live.test.ts +7 -2
- package/src/activities/execute-cursor/__tests__/cursor-hook-protocol-live.test.ts +163 -0
- package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +23 -20
- package/src/activities/execute-cursor/__tests__/cursor-transcript-live.test.ts +298 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +4 -2
- package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/mcp-tool-call.status.json +71 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-task-note.status.json +73 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +78 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +72 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +72 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sub-agent-delegation.status.json +109 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/thinking-turn.status.json +78 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/todo-write.status.json +119 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-error.status.json +68 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-only-step.status.json +67 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +10 -7
- package/src/activities/execute-cursor/__tests__/hermetic/mcp-tool-call.test.ts +146 -0
- package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +8 -5
- package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +6 -2
- package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +4 -1
- package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +6 -2
- package/src/activities/execute-cursor/__tests__/hermetic/sdk-task-note.test.ts +122 -0
- package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +276 -0
- package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +12 -9
- package/src/activities/execute-cursor/__tests__/hermetic/sub-agent-delegation.test.ts +196 -0
- package/src/activities/execute-cursor/__tests__/hermetic/thinking-turn.test.ts +145 -0
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +4 -1
- package/src/activities/execute-cursor/__tests__/hermetic/todo-write.test.ts +165 -0
- package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +7 -4
- package/src/activities/execute-cursor/__tests__/hermetic/tool-error.test.ts +138 -0
- package/src/activities/execute-cursor/__tests__/hermetic/tool-only-step.test.ts +130 -0
- package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +17 -2
- package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +43 -60
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +23 -28
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +5 -5
- package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +11 -13
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +8 -11
- package/src/activities/execute-cursor/__tests__/session-lifecycle.test.ts +86 -58
- package/src/activities/execute-cursor/__tests__/session-store.test.ts +220 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +8 -22
- package/src/activities/execute-cursor/__tests__/translator.test.ts +594 -0
- package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +8 -5
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +87 -63
- package/src/activities/execute-cursor/adapter.ts +13 -9
- package/src/activities/execute-cursor/approval-policy.ts +12 -24
- package/src/activities/execute-cursor/approval-state.ts +66 -1
- package/src/activities/execute-cursor/boundary-rows.ts +1132 -0
- package/src/activities/execute-cursor/cursor-capabilities.ts +1 -1
- package/src/activities/execute-cursor/cursor-event-recorder.ts +57 -28
- package/src/activities/execute-cursor/fetch-interceptor.ts +1 -1
- package/src/activities/execute-cursor/http2-interceptor.ts +1 -1
- package/src/activities/execute-cursor/prompt-builder.ts +6 -6
- package/src/activities/execute-cursor/sdk-warmup.ts +15 -6
- package/src/activities/execute-cursor/service-tier.ts +1 -2
- package/src/activities/execute-cursor/session-lifecycle.ts +63 -109
- package/src/activities/execute-cursor/session-store.ts +187 -0
- package/src/activities/execute-cursor/sub-agent-steps.ts +188 -0
- package/src/activities/execute-cursor/tool-result.ts +128 -0
- package/src/activities/execute-cursor/translator.ts +541 -0
- package/src/activities/execute-cursor/turn-boundary.ts +21 -14
- package/src/activities/execute-cursor/turn-settle.ts +46 -34
- package/src/activities/execute-cursor/turn-setup.ts +5 -5
- package/src/activities/execute-cursor/turn-stream.ts +49 -46
- package/src/activities/execute-cursor/turn.ts +5 -5
- package/src/activities/execute-cursor/usage-pricing.ts +2 -2
- package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +2 -2
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +23 -11
- package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +1 -1
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +2 -3
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +25 -4
- package/src/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.ts +25 -11
- package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +6 -17
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +25 -8
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +2 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +4 -14
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +4 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +2 -9
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +2 -9
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +109 -76
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +2 -10
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +2 -10
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +3 -6
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/write-todos.status.json +92 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +9 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +7 -6
- package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +10 -11
- package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +4 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +9 -8
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +21 -13
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +7 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +28 -12
- package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +21 -21
- package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/write-todos.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +35 -30
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +3 -4
- package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +12 -10
- package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +13 -3
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +26 -18
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/{subagent-tracker.test.ts → translator-subagents.test.ts} +76 -57
- package/src/activities/execute-deep-agent/__tests__/{v3-status-builder.test.ts → translator-through-builder.test.ts} +213 -299
- package/src/activities/execute-deep-agent/__tests__/translator.test.ts +505 -0
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +11 -29
- package/src/activities/execute-deep-agent/adapter.ts +1 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +20 -22
- package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +2 -2
- package/src/activities/execute-deep-agent/hitl.ts +2 -2
- package/src/activities/execute-deep-agent/inline-publisher.ts +16 -5
- package/src/activities/execute-deep-agent/prompt-builder.ts +6 -7
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +22 -31
- package/src/activities/execute-deep-agent/subagent-transformer.ts +3 -3
- package/src/activities/execute-deep-agent/translator.ts +463 -0
- package/src/activities/execute-deep-agent/turn-settle.ts +35 -43
- package/src/activities/execute-deep-agent/turn-setup.ts +8 -8
- package/src/activities/execute-deep-agent/turn-stream.ts +53 -51
- package/src/activities/execute-deep-agent/turn.ts +3 -3
- package/src/config.ts +2 -2
- package/src/harness/README.md +335 -0
- package/src/harness/__tests__/approval-decisions.test.ts +2 -2
- package/src/harness/__tests__/capture.test.ts +3 -3
- package/src/harness/__tests__/import-direction.test.ts +3 -2
- package/src/harness/__tests__/mount-skills.test.ts +4 -1
- package/src/harness/__tests__/readme-index.test.ts +339 -0
- package/src/harness/__tests__/run-turn.test.ts +131 -6
- package/src/harness/__tests__/transcript-is-engine-free.test.ts +83 -0
- package/src/harness/__tests__/transcript-writers.test.ts +191 -0
- package/src/harness/__tests__/turn-attachments-vision.test.ts +5 -2
- package/src/harness/__tests__/turn-context.test.ts +5 -5
- package/src/harness/approval-decisions.ts +1 -1
- package/src/harness/capabilities.ts +21 -11
- package/src/harness/capture.ts +4 -4
- package/src/harness/persist-chokepoint.ts +3 -3
- package/src/harness/registry.ts +2 -2
- package/src/harness/run-turn.ts +33 -9
- package/src/harness/stop-controller.ts +1 -1
- package/src/harness/terminal-table.ts +7 -7
- package/src/harness/transcript/__tests__/builder.test.ts +975 -0
- package/src/harness/transcript/__tests__/state.test.ts +143 -0
- package/src/harness/transcript/builder.ts +755 -0
- package/src/harness/transcript/events.ts +262 -0
- package/src/harness/transcript/state.ts +167 -0
- package/src/harness/turn-context.ts +61 -46
- package/src/harness/types.ts +64 -22
- package/src/harness/usage-accumulator.ts +2 -2
- package/src/harness-adapters.ts +5 -4
- package/src/middleware/__tests__/cost-advisory.test.ts +2 -2
- package/src/middleware/approval-gate.ts +7 -74
- package/src/middleware/cost-advisory.ts +3 -3
- package/src/middleware/index.ts +1 -1
- package/src/runner-manager.ts +1 -1
- package/src/shared/__tests__/approval-policy.test.ts +45 -0
- package/src/shared/__tests__/args-preview.test.ts +40 -20
- package/src/shared/__tests__/attachment-naming.test.ts +1 -1
- package/src/shared/__tests__/attachment-resolver.test.ts +5 -5
- package/src/shared/__tests__/attachment-zip.test.ts +1 -1
- package/src/shared/__tests__/blueprint-resolver.test.ts +1 -1
- package/src/shared/__tests__/cost-guard.test.ts +1 -1
- package/src/shared/__tests__/exact-apply.test.ts +2 -1
- package/src/shared/__tests__/persist-decision.test.ts +19 -33
- package/src/shared/__tests__/prompt-sections.test.ts +1 -1
- package/src/shared/__tests__/stamp-flowed-rows.test.ts +1 -1
- package/src/shared/__tests__/status-offload.test.ts +1 -1
- package/src/shared/__tests__/streaming-scheduler.test.ts +8 -9
- package/src/shared/__tests__/todos.test.ts +1 -1
- package/src/shared/__tests__/tool-row.test.ts +1 -1
- package/src/shared/approval-policy.ts +95 -0
- package/src/shared/args-preview.ts +51 -42
- package/src/shared/attachment-resolver.ts +4 -4
- package/src/shared/attachment-vision.ts +2 -1
- package/src/shared/attachment-zip.ts +2 -2
- package/src/shared/cost-guard.ts +2 -2
- package/src/shared/exact-apply.ts +1 -1
- package/src/shared/execution-context.ts +2 -2
- package/src/shared/extract-structured-output.ts +2 -2
- package/src/shared/filereview/__tests__/capture.test.ts +9 -5
- package/src/shared/filereview/__tests__/git-substrate.test.ts +4 -1
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +3 -3
- package/src/shared/filereview/cas-touched.ts +2 -2
- package/src/shared/heartbeat.ts +1 -1
- package/src/shared/llm-backend.ts +0 -4
- package/src/shared/model-error.ts +1 -2
- package/src/shared/persist-decision.ts +19 -28
- package/src/shared/prompt-sections.ts +4 -4
- package/src/shared/skill-resolver.ts +3 -3
- package/src/shared/status-offload.ts +2 -2
- package/src/shared/streaming-scheduler.ts +8 -13
- package/src/shared/subagent-rows.ts +2 -2
- package/src/shared/todos.ts +3 -3
- package/src/shared/tool-row.ts +38 -2
- package/src/shared/worker-shutdown.ts +1 -1
- package/src/shared/workspace/__tests__/git-source.test.ts +3 -3
- package/src/shared/workspace/__tests__/provisioner.test.ts +5 -5
- package/src/shared/workspace/__tests__/session-provision.test.ts +2 -2
- package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +8 -9
- package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +25 -25
- package/src/shared/workspace/provisioner.ts +1 -1
- package/src/shared/workspace/session-provision.ts +1 -1
- package/src/shared/workspace/sources/empty.ts +2 -2
- package/src/shared/workspace/sources/git.ts +3 -4
- package/src/shared/workspace/sources/local-path.ts +5 -6
- package/src/shared/workspace/stigmer-link.ts +1 -1
- package/src/shared/workspace/types.ts +2 -5
- package/src/shared/workspace/writeback-coordinator.ts +23 -12
- package/dist/activities/execute-cursor/delta-enricher.d.ts +0 -87
- package/dist/activities/execute-cursor/delta-enricher.js +0 -265
- package/dist/activities/execute-cursor/delta-enricher.js.map +0 -1
- package/dist/activities/execute-cursor/message-translator.js +0 -2040
- package/dist/activities/execute-cursor/message-translator.js.map +0 -1
- package/dist/activities/execute-cursor/todo-tracker.d.ts +0 -40
- package/dist/activities/execute-cursor/todo-tracker.js +0 -80
- package/dist/activities/execute-cursor/todo-tracker.js.map +0 -1
- package/dist/activities/execute-deep-agent/execution-state.d.ts +0 -54
- package/dist/activities/execute-deep-agent/execution-state.js +0 -68
- package/dist/activities/execute-deep-agent/execution-state.js.map +0 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +0 -65
- package/dist/activities/execute-deep-agent/status-builder-shared.js +0 -90
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +0 -1
- package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +0 -113
- package/dist/activities/execute-deep-agent/subagent-tracker.js +0 -400
- package/dist/activities/execute-deep-agent/subagent-tracker.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-events.d.ts +0 -102
- package/dist/activities/execute-deep-agent/v3-events.js +0 -35
- package/dist/activities/execute-deep-agent/v3-events.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.d.ts +0 -15
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js +0 -235
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +0 -111
- package/dist/activities/execute-deep-agent/v3-status-builder.js +0 -457
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +0 -1
- package/dist/shared/execution-status-writer.d.ts +0 -31
- package/dist/shared/execution-status-writer.js +0 -43
- package/dist/shared/execution-status-writer.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +0 -310
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -1033
- package/src/activities/execute-cursor/__tests__/todo-tracker.test.ts +0 -498
- package/src/activities/execute-cursor/delta-enricher.ts +0 -307
- package/src/activities/execute-cursor/message-translator.ts +0 -2319
- package/src/activities/execute-cursor/todo-tracker.ts +0 -91
- package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -148
- package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -154
- package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +0 -47
- package/src/activities/execute-deep-agent/__tests__/v3-protocol-normalizer.test.ts +0 -324
- package/src/activities/execute-deep-agent/execution-state.ts +0 -78
- package/src/activities/execute-deep-agent/status-builder-shared.ts +0 -142
- package/src/activities/execute-deep-agent/subagent-tracker.ts +0 -479
- package/src/activities/execute-deep-agent/v3-events.ts +0 -153
- package/src/activities/execute-deep-agent/v3-protocol-normalizer.ts +0 -264
- package/src/activities/execute-deep-agent/v3-status-builder.ts +0 -565
- package/src/shared/execution-status-writer.ts +0 -56
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: an MCP TOOL CALL — the SDK reports the call as `name: "mcp"`
|
|
3
|
+
* and packs the real identity into its args — through the whole
|
|
4
|
+
* `ExecuteCursor` activity.
|
|
5
|
+
*
|
|
6
|
+
* Invariant pinned: the translator unwraps
|
|
7
|
+
* `{ providerIdentifier, toolName, args }` from the event's args
|
|
8
|
+
* (`extractMcpToolDetails`) and the row is the INNER tool: `name` is `toolName`,
|
|
9
|
+
* `mcpServerSlug` is `providerIdentifier`, `args` is the inner args,
|
|
10
|
+
* `toolKind` is MCP, and `argsPreview` is the elided preview of the row's OWN
|
|
11
|
+
* `args` — `{"query":"fixture"}` — through the platform's one sanitizer. With
|
|
12
|
+
* no MCP server declared on the record there is no policy for the tool, so it
|
|
13
|
+
* is not gated; the provenance stamp still names the layer that cleared it.
|
|
14
|
+
*
|
|
15
|
+
* Moved 2026-09-14 (#1097), the ONE hunk this golden was
|
|
16
|
+
* predicted to take: until then the stream path stamped the OUTER
|
|
17
|
+
* `{ providerIdentifier, toolName, args }` envelope stringified — the
|
|
18
|
+
* envelope, not the arguments. The canonical builder derives every row's
|
|
19
|
+
* preview from its `args` and never from an engine's envelope; the
|
|
20
|
+
* accumulator was aligned to that rule before the swap to the shared builder,
|
|
21
|
+
* so the swap moved nothing.
|
|
22
|
+
*
|
|
23
|
+
* Why this net exists: no earlier Cursor golden carried an MCP-attributed
|
|
24
|
+
* row, and #1097 moved the unwrapping whole into the Cursor translator.
|
|
25
|
+
*
|
|
26
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
27
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
31
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
32
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
33
|
+
import { ExecutionPhase, ToolCallStatus, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
34
|
+
|
|
35
|
+
vi.mock("@cursor/sdk", async () =>
|
|
36
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
37
|
+
);
|
|
38
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
39
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
ScriptedClock,
|
|
44
|
+
createHermeticEnvironment,
|
|
45
|
+
type HermeticEnvironment,
|
|
46
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
47
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
48
|
+
import {
|
|
49
|
+
FIXTURE,
|
|
50
|
+
SDK_CATALOG,
|
|
51
|
+
beginCursorScenario,
|
|
52
|
+
cursorExecutionRecord,
|
|
53
|
+
runCursorTurn,
|
|
54
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
55
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
56
|
+
|
|
57
|
+
const AGENT_ID = "agent-hermetic-mcp-0001";
|
|
58
|
+
const RUN_ID = "run-hermetic-mcp-0001";
|
|
59
|
+
const CALL_ID = "call-hermetic-mcp-0001";
|
|
60
|
+
const USER_MESSAGE = "Search the docs for the fixture.";
|
|
61
|
+
const TEXT_BEFORE = "Searching the docs.";
|
|
62
|
+
const MCP_SERVER = "hermetic-docs";
|
|
63
|
+
const MCP_TOOL = "search_docs";
|
|
64
|
+
const INNER_ARGS = { query: "fixture" };
|
|
65
|
+
/** The SDK's MCP envelope: the real identity is inside the `mcp` call's args. */
|
|
66
|
+
const MCP_EVENT_ARGS = { providerIdentifier: MCP_SERVER, toolName: MCP_TOOL, args: INNER_ARGS };
|
|
67
|
+
const MCP_RESULT = "1 result: README.md — A fixture readme.";
|
|
68
|
+
const TEXT_AFTER = "One document mentions the fixture: README.md.";
|
|
69
|
+
|
|
70
|
+
describe("ExecuteCursor hermetic — MCP tool call", () => {
|
|
71
|
+
let env: HermeticEnvironment;
|
|
72
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
73
|
+
const clock = new ScriptedClock();
|
|
74
|
+
|
|
75
|
+
beforeAll(() => {
|
|
76
|
+
env = createHermeticEnvironment();
|
|
77
|
+
registry = stubRegistryFetch();
|
|
78
|
+
clock.install();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterAll(() => {
|
|
82
|
+
clock.uninstall();
|
|
83
|
+
registry.restore();
|
|
84
|
+
env.dispose();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("writes the row as the inner tool with its server slug and completes", async () => {
|
|
88
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
89
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
90
|
+
const agent = new ScriptedCursorAgent({
|
|
91
|
+
agentId: AGENT_ID,
|
|
92
|
+
runIds: [RUN_ID],
|
|
93
|
+
observeStep: () => clock.tick(),
|
|
94
|
+
turns: [
|
|
95
|
+
[
|
|
96
|
+
step.event(ev.init()),
|
|
97
|
+
step.event(ev.assistant(TEXT_BEFORE)),
|
|
98
|
+
step.event(ev.toolCall(CALL_ID, "mcp", "running", MCP_EVENT_ARGS)),
|
|
99
|
+
step.event(ev.toolCall(CALL_ID, "mcp", "completed", MCP_EVENT_ARGS, MCP_RESULT)),
|
|
100
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
101
|
+
step.turnEnded({ inputTokens: 2_100, outputTokens: 80, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
102
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
103
|
+
],
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
107
|
+
const scenario = beginCursorScenario({
|
|
108
|
+
env,
|
|
109
|
+
clock,
|
|
110
|
+
record,
|
|
111
|
+
sdk: { agents: [agent], catalog: SDK_CATALOG },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
115
|
+
const invocation = await runCursorTurn(scenario);
|
|
116
|
+
|
|
117
|
+
// ── Assert: outcome ──────────────────────────────────────────────────────
|
|
118
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
119
|
+
expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
120
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
121
|
+
|
|
122
|
+
// ── Assert: the attributed row ───────────────────────────────────────────
|
|
123
|
+
const rows = record.toolCalls();
|
|
124
|
+
expect(rows).toHaveLength(1);
|
|
125
|
+
const row = rows[0];
|
|
126
|
+
expect(row.id).toBe(CALL_ID);
|
|
127
|
+
expect(row.name, "the inner tool, never the `mcp` envelope").toBe(MCP_TOOL);
|
|
128
|
+
expect(row.mcpServerSlug).toBe(MCP_SERVER);
|
|
129
|
+
expect(row.toolKind).toBe(ToolKind.MCP);
|
|
130
|
+
expect(row.args, "the inner args").toEqual(INNER_ARGS);
|
|
131
|
+
expect(row.argsPreview, "the row's own args, never the engine's envelope").toBe(
|
|
132
|
+
JSON.stringify(INNER_ARGS),
|
|
133
|
+
);
|
|
134
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
135
|
+
expect(row.result).toBe(MCP_RESULT);
|
|
136
|
+
expect(row.requiresApproval, "no server declared, no policy, not gated").toBe(false);
|
|
137
|
+
|
|
138
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
139
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
140
|
+
|
|
141
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
142
|
+
const final = record.lastFullStatus!;
|
|
143
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
144
|
+
await expect(json).toMatchFileSnapshot("./goldens/mcp-tool-call.status.json");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* ONCE. The orchestrator this net was recorded on appended it twice — once in
|
|
27
27
|
* `resolvePreBoundaryTerminal` before the throw, once again in the outer
|
|
28
28
|
* `catch (CancelledFailure)`, which re-stamped and re-persisted — and the
|
|
29
|
-
* goldens pinned that double on purpose so
|
|
30
|
-
* for byte (stigmer#1054
|
|
29
|
+
* goldens pinned that double on purpose so #1070 could reproduce the wire byte
|
|
30
|
+
* for byte (removed in stigmer#1054). The runtime's `settleWith` is now the one
|
|
31
31
|
* writer of a terminal arm, so a second row has no code path to come from.
|
|
32
32
|
*
|
|
33
33
|
* Runtime phases exercised: the adapter's stop-signal cancel; the runtime's
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
* arms through `settleWith`; the `finally` teardown under a thrown exit.
|
|
36
36
|
*
|
|
37
37
|
* Regeneration history:
|
|
38
|
-
* -
|
|
38
|
+
* - #1070: timestamp-only diff — every terminal stamp
|
|
39
39
|
* one scripted second earlier: the adapter cancels the SDK run the instant
|
|
40
|
-
* the runtime's stop signal aborts
|
|
40
|
+
* the runtime's stop signal aborts, before the SDK double pulls,
|
|
41
41
|
* and the clock ticks on, one more step. Transcript unchanged.
|
|
42
|
-
* - 2026-09-12 (stigmer#1054, the PR after
|
|
42
|
+
* - 2026-09-12 (stigmer#1054, the PR after #1070): one removed system row per
|
|
43
43
|
* golden, nothing else. The transcript now carries each terminal row once.
|
|
44
44
|
*
|
|
45
45
|
* Regenerate ONLY after a deliberate behavior change:
|
|
@@ -54,6 +54,9 @@ import { ExecutionPhase, MessageType } from "@stigmer/protos/ai/stigmer/agentic/
|
|
|
54
54
|
vi.mock("@cursor/sdk", async () =>
|
|
55
55
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
56
56
|
);
|
|
57
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
58
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
59
|
+
);
|
|
57
60
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
58
61
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
59
62
|
);
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* scenario is a delta from, and the first proof that the activity is byte-
|
|
10
10
|
* stable under the hermetic driver.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Phase rows of the pre-runtime orchestrator this net was recorded against
|
|
13
|
+
* (#1048): normalize
|
|
13
14
|
* input / heartbeat; artifact storage + the `persist` chokepoint; fetch
|
|
14
15
|
* execution, session, agent, blueprint; resolve environment (NOT_FOUND ->
|
|
15
16
|
* empty); provision workspace (per-session dir); workspace turn lock; MCP
|
|
@@ -37,6 +38,9 @@ import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecutio
|
|
|
37
38
|
vi.mock("@cursor/sdk", async () =>
|
|
38
39
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
39
40
|
);
|
|
41
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
42
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
43
|
+
);
|
|
40
44
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
41
45
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
42
46
|
);
|
|
@@ -93,7 +97,7 @@ describe("ExecuteCursor hermetic — plain turn", () => {
|
|
|
93
97
|
run_id: RUN_ID,
|
|
94
98
|
message: { role: "assistant", content: [{ type: "text", text: ASSISTANT_TEXT }] },
|
|
95
99
|
}),
|
|
96
|
-
step.turnEnded({ inputTokens: 1_200, outputTokens: 40 }),
|
|
100
|
+
step.turnEnded({ inputTokens: 1_200, outputTokens: 40, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
97
101
|
step.finished({
|
|
98
102
|
result: ASSISTANT_TEXT,
|
|
99
103
|
model: {
|
|
@@ -33,6 +33,9 @@ import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecutio
|
|
|
33
33
|
vi.mock("@cursor/sdk", async () =>
|
|
34
34
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
35
35
|
);
|
|
36
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
37
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
38
|
+
);
|
|
36
39
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
37
40
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
38
41
|
);
|
|
@@ -104,7 +107,7 @@ describe("ExecuteCursor hermetic — poisoned-handle recovery on a fresh agent",
|
|
|
104
107
|
[
|
|
105
108
|
step.event(evFresh.init()),
|
|
106
109
|
step.event(evFresh.assistant(FINAL_TEXT)),
|
|
107
|
-
step.turnEnded({ inputTokens: 2_800, outputTokens: 55 }),
|
|
110
|
+
step.turnEnded({ inputTokens: 2_800, outputTokens: 55, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
108
111
|
step.finished({ result: FINAL_TEXT, model: { id: FIXTURE.model, params: [] } }),
|
|
109
112
|
],
|
|
110
113
|
],
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Phase 13 of the activity maps what the SDK's `run.wait()` resolves into a
|
|
6
6
|
* terminal phase, and the `error` case is where the error classifier decides
|
|
7
7
|
* whether the runner tries again on its own. The three scenarios here pin the
|
|
8
|
-
* three outcomes of that decision table that the
|
|
8
|
+
* three outcomes of that decision table that the first hermetic net (#1048)
|
|
9
|
+
* did not cover (the
|
|
9
10
|
* poisoned-handle recovery on a RESUMED agent is `recovery-fresh-agent.test.ts`):
|
|
10
11
|
*
|
|
11
12
|
* 1. A NON-RETRYABLE error (auth class): the classifier files it as
|
|
@@ -44,6 +45,9 @@ import { ExecutionPhase, MessageType } from "@stigmer/protos/ai/stigmer/agentic/
|
|
|
44
45
|
vi.mock("@cursor/sdk", async () =>
|
|
45
46
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
46
47
|
);
|
|
48
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
49
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
50
|
+
);
|
|
47
51
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
48
52
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
49
53
|
);
|
|
@@ -175,7 +179,7 @@ describe("ExecuteCursor hermetic — the run.wait() arms on a created agent", ()
|
|
|
175
179
|
[
|
|
176
180
|
step.event(evFresh.init()),
|
|
177
181
|
step.event(evFresh.assistant(FINAL_TEXT)),
|
|
178
|
-
step.turnEnded({ inputTokens: 2_200, outputTokens: 30 }),
|
|
182
|
+
step.turnEnded({ inputTokens: 2_200, outputTokens: 30, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
179
183
|
step.finished({ result: FINAL_TEXT, model: { id: FIXTURE.model, params: [] } }),
|
|
180
184
|
],
|
|
181
185
|
],
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: the SDK's `task` event — a status line about the run in
|
|
3
|
+
* the agent's own voice — between two assistant messages, through the whole
|
|
4
|
+
* `ExecuteCursor` activity.
|
|
5
|
+
*
|
|
6
|
+
* Invariant pinned (#1097; until then this event had no test on either
|
|
7
|
+
* tier): a `task` event with text becomes ONE SYSTEM
|
|
8
|
+
* message at its position in the transcript (`translateTask`); it hosts no
|
|
9
|
+
* tool rows and does not move the AI-message boundary — the tool call that
|
|
10
|
+
* follows it lands on the assistant message BEFORE the note, and the text
|
|
11
|
+
* after the tool call is a new message. A `task` event with no text produces
|
|
12
|
+
* nothing.
|
|
13
|
+
*
|
|
14
|
+
* Predicted for the swap to the shared builder: NO move. The translator
|
|
15
|
+
* maps `task.text` to the union's `system_note`, which the builder
|
|
16
|
+
* appends as a SYSTEM message in the scope with the same placement rule.
|
|
17
|
+
*
|
|
18
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
19
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
23
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
24
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
25
|
+
import { ExecutionPhase, MessageType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
26
|
+
|
|
27
|
+
vi.mock("@cursor/sdk", async () =>
|
|
28
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
29
|
+
);
|
|
30
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
31
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
32
|
+
);
|
|
33
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
34
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
ScriptedClock,
|
|
39
|
+
createHermeticEnvironment,
|
|
40
|
+
type HermeticEnvironment,
|
|
41
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
42
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
43
|
+
import {
|
|
44
|
+
FIXTURE,
|
|
45
|
+
SDK_CATALOG,
|
|
46
|
+
beginCursorScenario,
|
|
47
|
+
cursorExecutionRecord,
|
|
48
|
+
runCursorTurn,
|
|
49
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
50
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
51
|
+
|
|
52
|
+
const AGENT_ID = "agent-hermetic-tasknote-0001";
|
|
53
|
+
const RUN_ID = "run-hermetic-tasknote-0001";
|
|
54
|
+
const CALL_ID = "call-hermetic-tasknote-read-0001";
|
|
55
|
+
const USER_MESSAGE = "Summarise README.md.";
|
|
56
|
+
const TEXT_BEFORE = "Reading the file first.";
|
|
57
|
+
const TASK_NOTE = "Scanning the workspace for the file.";
|
|
58
|
+
const READ_ARGS = { path: "README.md" };
|
|
59
|
+
const READ_RESULT = "# Hermetic\n\nA fixture readme.\n";
|
|
60
|
+
const TEXT_AFTER = "README.md is a one-line fixture description.";
|
|
61
|
+
|
|
62
|
+
describe("ExecuteCursor hermetic — the SDK's task event as a system note", () => {
|
|
63
|
+
let env: HermeticEnvironment;
|
|
64
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
65
|
+
const clock = new ScriptedClock();
|
|
66
|
+
|
|
67
|
+
beforeAll(() => {
|
|
68
|
+
env = createHermeticEnvironment();
|
|
69
|
+
registry = stubRegistryFetch();
|
|
70
|
+
clock.install();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
afterAll(() => {
|
|
74
|
+
clock.uninstall();
|
|
75
|
+
registry.restore();
|
|
76
|
+
env.dispose();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("places the note as a SYSTEM message that hosts no rows and moves no boundary; an empty note produces nothing", async () => {
|
|
80
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
81
|
+
const agent = new ScriptedCursorAgent({
|
|
82
|
+
agentId: AGENT_ID,
|
|
83
|
+
runIds: [RUN_ID],
|
|
84
|
+
observeStep: () => clock.tick(),
|
|
85
|
+
turns: [
|
|
86
|
+
[
|
|
87
|
+
step.event(ev.init()),
|
|
88
|
+
step.event(ev.assistant(TEXT_BEFORE)),
|
|
89
|
+
step.event(ev.task(TASK_NOTE, "running")),
|
|
90
|
+
step.event(ev.task("", "running")),
|
|
91
|
+
step.event(ev.toolCall(CALL_ID, "read", "running", READ_ARGS)),
|
|
92
|
+
step.event(ev.toolCall(CALL_ID, "read", "completed", READ_ARGS, READ_RESULT)),
|
|
93
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
94
|
+
step.turnEnded({ inputTokens: 1_800, outputTokens: 50, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
95
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
96
|
+
],
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
100
|
+
const scenario = beginCursorScenario({ env, clock, record, sdk: { agents: [agent], catalog: SDK_CATALOG } });
|
|
101
|
+
|
|
102
|
+
const invocation = await runCursorTurn(scenario);
|
|
103
|
+
|
|
104
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
105
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
106
|
+
|
|
107
|
+
const messages = record.lastFullStatus!.messages;
|
|
108
|
+
expect(messages.map((m) => m.type), "AI, the note, AI").toEqual([
|
|
109
|
+
MessageType.MESSAGE_AI,
|
|
110
|
+
MessageType.MESSAGE_SYSTEM,
|
|
111
|
+
MessageType.MESSAGE_AI,
|
|
112
|
+
]);
|
|
113
|
+
expect(messages[1].content).toBe(TASK_NOTE);
|
|
114
|
+
expect(messages[1].toolCalls, "a note hosts no rows").toHaveLength(0);
|
|
115
|
+
expect(messages[0].toolCalls.map((tc) => tc.id), "the row lands on the assistant message before the note").toEqual([CALL_ID]);
|
|
116
|
+
expect(messages[2].content).toBe(TEXT_AFTER);
|
|
117
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
118
|
+
|
|
119
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, record.lastFullStatus!), null, 2) + "\n";
|
|
120
|
+
await expect(json).toMatchFileSnapshot("./goldens/sdk-task-note.status.json");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: the SDK's DELTA CHANNEL folding onto a shell tool's row —
|
|
3
|
+
* `tool-call-started`, `shell-output-delta` and `tool-call-completed` fired
|
|
4
|
+
* through `onDelta` between the stream's own `tool_call` events — through the
|
|
5
|
+
* whole `ExecuteCursor` activity.
|
|
6
|
+
*
|
|
7
|
+
* Why this net exists (#1097): the scripted double emitted only the
|
|
8
|
+
* `turn-ended` usage delta until then, so the rule that PR changed most —
|
|
9
|
+
* a completion learned from the timing delta and merged with the stream's
|
|
10
|
+
* later completion — had no end-to-end net; `delta-enricher.ts`'s
|
|
11
|
+
* twelve unit arms were the whole of it. The three arms below were pinned
|
|
12
|
+
* on the enricher's fold before the swap to the shared builder, and each
|
|
13
|
+
* header states what was predicted to move at the swap and after it.
|
|
14
|
+
*
|
|
15
|
+
* Arm 1 — the ordinary order. The stream's `running` creates the row; the
|
|
16
|
+
* deltas arrive; the stream's `completed` settles it. Pinned: `startedAt` is
|
|
17
|
+
* the stream's `running` instant; the live output reaches `result`
|
|
18
|
+
* (`streamingSource: OUTPUT` while it streams, cleared at finalize); the
|
|
19
|
+
* stream's `completed` stamps `completedAt` and its result wins; the shell
|
|
20
|
+
* row is a gated built-in (`requiresApproval`, "Run command: …") that the
|
|
21
|
+
* hook never saw, so no `approvalRequestedAt`. Predicted: NO move (the
|
|
22
|
+
* translator drains the queued deltas AFTER the stream event of the
|
|
23
|
+
* same window, as the enricher applied them, so the stream's instant wins
|
|
24
|
+
* exactly as before).
|
|
25
|
+
*
|
|
26
|
+
* Arm 2 — output BEFORE the row exists. A `shell-output-delta` fires before
|
|
27
|
+
* the stream's `running` for its call. Pinned: nothing is lost — the enricher
|
|
28
|
+
* buffers the chunk and applies it once the row exists, so the final `result`
|
|
29
|
+
* carries both chunks in order. Predicted: NO move (the translator holds
|
|
30
|
+
* a delta for a call the stream has not announced and replays it after the
|
|
31
|
+
* `tool_started` that announces it).
|
|
32
|
+
*
|
|
33
|
+
* Arm 3 — the completion delta reports an ERROR, and a model event sits
|
|
34
|
+
* between it and the stream's own `error`. Pinned: the translator honours the
|
|
35
|
+
* delta's `result.status: "error"` — the row is FAILED from the
|
|
36
|
+
* instant the failure was observed (`completedAt` and, for a gated shell,
|
|
37
|
+
* `approvalRequestedAt` at the delta's own tick), and the stream's `error`
|
|
38
|
+
* event that follows supplies the text. Until #1097 (as this net first
|
|
39
|
+
* pinned it) the enricher promoted every completion delta to
|
|
40
|
+
* COMPLETED, the monotonic merge then refused the stream's FAILED, and the
|
|
41
|
+
* row ended COMPLETED carrying an `error` — a row that lied. Moved in #1097
|
|
42
|
+
* by one line this header had not named when the net was written:
|
|
43
|
+
* the failure text is `error` alone; and, as predicted, in the
|
|
44
|
+
* status and in `approvalRequestedAt`.
|
|
45
|
+
*
|
|
46
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
47
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
51
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
52
|
+
import type { InteractionUpdate } from "@cursor/sdk";
|
|
53
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
54
|
+
import { ExecutionPhase, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
55
|
+
|
|
56
|
+
vi.mock("@cursor/sdk", async () =>
|
|
57
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
58
|
+
);
|
|
59
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
60
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
61
|
+
);
|
|
62
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
63
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
import {
|
|
67
|
+
ScriptedClock,
|
|
68
|
+
createHermeticEnvironment,
|
|
69
|
+
type HermeticEnvironment,
|
|
70
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
71
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
72
|
+
import {
|
|
73
|
+
FIXTURE,
|
|
74
|
+
SDK_CATALOG,
|
|
75
|
+
beginCursorScenario,
|
|
76
|
+
cursorExecutionRecord,
|
|
77
|
+
runCursorTurn,
|
|
78
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
79
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
80
|
+
|
|
81
|
+
const USER_MESSAGE = "Run the test suite.";
|
|
82
|
+
const COMMAND = "npm test";
|
|
83
|
+
const SHELL_ARGS = { command: COMMAND };
|
|
84
|
+
const CHUNK_1 = "> test\n";
|
|
85
|
+
const CHUNK_2 = "12 passing\n";
|
|
86
|
+
const FULL_OUTPUT = CHUNK_1 + CHUNK_2;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The delta channel's shapes, typed against the SDK so a drift fails `tsc`
|
|
90
|
+
* (the stigmer#1053 dividend — writing these is how the runner learned the
|
|
91
|
+
* timing deltas carry `modelCallId`, the LLM call that issued the tool;
|
|
92
|
+
* nothing in the runner reads it yet).
|
|
93
|
+
*/
|
|
94
|
+
const MODEL_CALL_ID = "model-call-hermetic-shelldelta";
|
|
95
|
+
const deltas = {
|
|
96
|
+
started(callId: string): InteractionUpdate {
|
|
97
|
+
return { type: "tool-call-started", callId, modelCallId: MODEL_CALL_ID, toolCall: { type: "shell", args: SHELL_ARGS } };
|
|
98
|
+
},
|
|
99
|
+
output(callId: string, data: string): InteractionUpdate {
|
|
100
|
+
return { type: "shell-output-delta", event: { callId, type: "stdout", data } };
|
|
101
|
+
},
|
|
102
|
+
completedOk(callId: string, stdout: string): InteractionUpdate {
|
|
103
|
+
return {
|
|
104
|
+
type: "tool-call-completed",
|
|
105
|
+
callId,
|
|
106
|
+
modelCallId: MODEL_CALL_ID,
|
|
107
|
+
toolCall: {
|
|
108
|
+
type: "shell",
|
|
109
|
+
args: SHELL_ARGS,
|
|
110
|
+
result: { status: "success", value: { exitCode: 0, signal: "", stdout, stderr: "", executionTime: 1_200 } },
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
completedError(callId: string, error: string): InteractionUpdate {
|
|
115
|
+
return {
|
|
116
|
+
type: "tool-call-completed",
|
|
117
|
+
callId,
|
|
118
|
+
modelCallId: MODEL_CALL_ID,
|
|
119
|
+
toolCall: { type: "shell", args: SHELL_ARGS, result: { status: "error", error } },
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
describe("ExecuteCursor hermetic — the delta channel on a shell row", () => {
|
|
125
|
+
let env: HermeticEnvironment;
|
|
126
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
127
|
+
const clock = new ScriptedClock();
|
|
128
|
+
|
|
129
|
+
beforeAll(() => {
|
|
130
|
+
env = createHermeticEnvironment();
|
|
131
|
+
registry = stubRegistryFetch();
|
|
132
|
+
clock.install();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
afterAll(() => {
|
|
136
|
+
clock.uninstall();
|
|
137
|
+
registry.restore();
|
|
138
|
+
env.dispose();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("arm 1: started, output and completed deltas between the stream's running and completed fold onto one row", async () => {
|
|
142
|
+
const AGENT_ID = "agent-hermetic-shelldelta-0001";
|
|
143
|
+
const RUN_ID = "run-hermetic-shelldelta-0001";
|
|
144
|
+
const CALL_ID = "call-hermetic-shelldelta-0001";
|
|
145
|
+
const TEXT_AFTER = "All 12 tests pass.";
|
|
146
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
147
|
+
const agent = new ScriptedCursorAgent({
|
|
148
|
+
agentId: AGENT_ID,
|
|
149
|
+
runIds: [RUN_ID],
|
|
150
|
+
observeStep: () => clock.tick(),
|
|
151
|
+
turns: [
|
|
152
|
+
[
|
|
153
|
+
step.event(ev.init()),
|
|
154
|
+
step.event(ev.assistant("Running the tests.")),
|
|
155
|
+
step.event(ev.toolCall(CALL_ID, "shell", "running", SHELL_ARGS)),
|
|
156
|
+
step.delta(deltas.started(CALL_ID)),
|
|
157
|
+
step.delta(deltas.output(CALL_ID, CHUNK_1)),
|
|
158
|
+
step.delta(deltas.output(CALL_ID, CHUNK_2)),
|
|
159
|
+
step.delta(deltas.completedOk(CALL_ID, FULL_OUTPUT)),
|
|
160
|
+
step.event(ev.toolCall(CALL_ID, "shell", "completed", SHELL_ARGS, FULL_OUTPUT)),
|
|
161
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
162
|
+
step.turnEnded({ inputTokens: 2_000, outputTokens: 60, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
163
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
164
|
+
],
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
168
|
+
const scenario = beginCursorScenario({ env, clock, record, sdk: { agents: [agent], catalog: SDK_CATALOG } });
|
|
169
|
+
|
|
170
|
+
const invocation = await runCursorTurn(scenario);
|
|
171
|
+
|
|
172
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
173
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
174
|
+
const rows = record.toolCalls();
|
|
175
|
+
expect(rows, "the stream's two events and the four deltas fold into ONE row").toHaveLength(1);
|
|
176
|
+
const row = rows[0];
|
|
177
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
178
|
+
expect(row.result, "the live output reached the row").toBe(FULL_OUTPUT);
|
|
179
|
+
expect(row.isStreaming, "finalize closed the OUTPUT stream").toBe(false);
|
|
180
|
+
expect(row.startedAt < row.completedAt).toBe(true);
|
|
181
|
+
expect(row.requiresApproval, "a shell is a gated built-in the hook never saw").toBe(true);
|
|
182
|
+
expect(row.approvalRequestedAt).toBe("");
|
|
183
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
184
|
+
|
|
185
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, record.lastFullStatus!), null, 2) + "\n";
|
|
186
|
+
await expect(json).toMatchFileSnapshot("./goldens/shell-output-delta.status.json");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("arm 2: an output delta that precedes the stream's running is held and lands once the row exists", async () => {
|
|
190
|
+
const AGENT_ID = "agent-hermetic-shelldelta-0002";
|
|
191
|
+
const RUN_ID = "run-hermetic-shelldelta-0002";
|
|
192
|
+
const CALL_ID = "call-hermetic-shelldelta-0002";
|
|
193
|
+
const TEXT_AFTER = "Done; the suite is green.";
|
|
194
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
195
|
+
const agent = new ScriptedCursorAgent({
|
|
196
|
+
agentId: AGENT_ID,
|
|
197
|
+
runIds: [RUN_ID],
|
|
198
|
+
observeStep: () => clock.tick(),
|
|
199
|
+
turns: [
|
|
200
|
+
[
|
|
201
|
+
step.event(ev.init()),
|
|
202
|
+
step.event(ev.assistant("Running the tests.")),
|
|
203
|
+
step.delta(deltas.started(CALL_ID)),
|
|
204
|
+
step.delta(deltas.output(CALL_ID, CHUNK_1)),
|
|
205
|
+
step.event(ev.toolCall(CALL_ID, "shell", "running", SHELL_ARGS)),
|
|
206
|
+
step.delta(deltas.output(CALL_ID, CHUNK_2)),
|
|
207
|
+
step.delta(deltas.completedOk(CALL_ID, FULL_OUTPUT)),
|
|
208
|
+
step.event(ev.toolCall(CALL_ID, "shell", "completed", SHELL_ARGS, FULL_OUTPUT)),
|
|
209
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
210
|
+
step.turnEnded({ inputTokens: 2_000, outputTokens: 60, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
211
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
212
|
+
],
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
216
|
+
const scenario = beginCursorScenario({ env, clock, record, sdk: { agents: [agent], catalog: SDK_CATALOG } });
|
|
217
|
+
|
|
218
|
+
await runCursorTurn(scenario);
|
|
219
|
+
|
|
220
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
221
|
+
const rows = record.toolCalls();
|
|
222
|
+
expect(rows).toHaveLength(1);
|
|
223
|
+
expect(rows[0].status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
224
|
+
expect(rows[0].result, "the early chunk was not lost").toBe(FULL_OUTPUT);
|
|
225
|
+
|
|
226
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, record.lastFullStatus!), null, 2) + "\n";
|
|
227
|
+
await expect(json).toMatchFileSnapshot("./goldens/shell-output-delta.output-first.status.json");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("arm 3: an error-status completion delta, with a model event before the stream's error, fails the row from the instant it was observed", async () => {
|
|
231
|
+
const AGENT_ID = "agent-hermetic-shelldelta-0003";
|
|
232
|
+
const RUN_ID = "run-hermetic-shelldelta-0003";
|
|
233
|
+
const CALL_ID = "call-hermetic-shelldelta-0003";
|
|
234
|
+
const SHELL_ERROR = "sh: npm: command not found";
|
|
235
|
+
const TEXT_AFTER = "npm is not installed here.";
|
|
236
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
237
|
+
const agent = new ScriptedCursorAgent({
|
|
238
|
+
agentId: AGENT_ID,
|
|
239
|
+
runIds: [RUN_ID],
|
|
240
|
+
observeStep: () => clock.tick(),
|
|
241
|
+
turns: [
|
|
242
|
+
[
|
|
243
|
+
step.event(ev.init()),
|
|
244
|
+
step.event(ev.assistant("Running the tests.")),
|
|
245
|
+
step.event(ev.toolCall(CALL_ID, "shell", "running", SHELL_ARGS)),
|
|
246
|
+
step.delta(deltas.started(CALL_ID)),
|
|
247
|
+
step.delta(deltas.completedError(CALL_ID, SHELL_ERROR)),
|
|
248
|
+
// The model reacts before the stream reports the tool's own outcome —
|
|
249
|
+
// the interleaving that lets a completion delta settle the row first.
|
|
250
|
+
step.event(ev.thinking("The command failed; npm may be missing.")),
|
|
251
|
+
step.event(ev.toolCall(CALL_ID, "shell", "error", SHELL_ARGS, SHELL_ERROR)),
|
|
252
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
253
|
+
step.turnEnded({ inputTokens: 2_000, outputTokens: 80, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
254
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
255
|
+
],
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
259
|
+
const scenario = beginCursorScenario({ env, clock, record, sdk: { agents: [agent], catalog: SDK_CATALOG } });
|
|
260
|
+
|
|
261
|
+
await runCursorTurn(scenario);
|
|
262
|
+
|
|
263
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
264
|
+
const rows = record.toolCalls();
|
|
265
|
+
expect(rows).toHaveLength(1);
|
|
266
|
+
const row = rows[0];
|
|
267
|
+
// The delta said the tool failed; the row says so from that instant.
|
|
268
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_FAILED);
|
|
269
|
+
expect(row.error, "the stream's error event supplied the text").toBe(SHELL_ERROR);
|
|
270
|
+
expect(row.result).toBe("");
|
|
271
|
+
expect(row.approvalRequestedAt, "a gated shell's gate stamp shares the observed instant").toBe(row.completedAt);
|
|
272
|
+
|
|
273
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, record.lastFullStatus!), null, 2) + "\n";
|
|
274
|
+
await expect(json).toMatchFileSnapshot("./goldens/shell-output-delta.error-delta.status.json");
|
|
275
|
+
});
|
|
276
|
+
});
|