@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
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
* is the one point a scenario can rely on: the STOP is keyed on the
|
|
33
33
|
* persisted status's CONTENT (the COMPLETED row), not on which persist
|
|
34
34
|
* happens to carry it, because a tool-call transition force-flushes
|
|
35
|
-
* (`
|
|
35
|
+
* (the builder's `dirty` flag) whatever the cadence says. (Until #1096 the scheduler
|
|
36
36
|
* paced text-only persists on REAL `performance.now()` time; the scripted
|
|
37
|
-
* clock fakes it since
|
|
37
|
+
* clock fakes it since then, so every persist is deterministic now.)
|
|
38
38
|
*
|
|
39
|
-
* Engine disposition and cancellation since
|
|
40
|
-
* adapter cancels the SDK run on EVERY stop (
|
|
39
|
+
* Engine disposition and cancellation since #1070: the
|
|
40
|
+
* adapter cancels the SDK run on EVERY stop (before, a platform stop
|
|
41
41
|
* broke the loop and left the run executing) and parks the handle on every
|
|
42
|
-
* non-failed exit (
|
|
42
|
+
* non-failed exit (before, a stall dropped it — the adapter cannot
|
|
43
43
|
* tell a stall from a cost cap, and a wedged parked handle is caught by the
|
|
44
44
|
* poisoned-handle recovery on the next turn). The two assertions that moved
|
|
45
|
-
* say so on their line. The cost-cap golden was regenerated
|
|
45
|
+
* say so on their line. The cost-cap golden was regenerated in #1070 with a
|
|
46
46
|
* timestamp-only diff (every terminal stamp one scripted second earlier):
|
|
47
47
|
* the run is now cancelled the instant the signal aborts, before the SDK
|
|
48
48
|
* double pulls — and the clock ticks on — one more step.
|
|
@@ -64,6 +64,9 @@ import {
|
|
|
64
64
|
vi.mock("@cursor/sdk", async () =>
|
|
65
65
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
66
66
|
);
|
|
67
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
68
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
69
|
+
);
|
|
67
70
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
68
71
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
69
72
|
);
|
|
@@ -185,7 +188,7 @@ describe("ExecuteCursor hermetic — the stream loop's self-stop arms", () => {
|
|
|
185
188
|
// ── Assert: engine disposition ───────────────────────────────────────────
|
|
186
189
|
expect(agent.runs[0].cancelCalls, "the runtime's stop cancelled the run once").toHaveLength(1);
|
|
187
190
|
expect(agent.closeCalls, "the handle is not closed").toBe(0);
|
|
188
|
-
//
|
|
191
|
+
// Since #1070: parked on every non-failed exit; before, a stall dropped it.
|
|
189
192
|
expect(_parkedAgentCountForTests(), "parked for the session's next turn").toBe(1);
|
|
190
193
|
expect(record.sessionUpdates).toHaveLength(1);
|
|
191
194
|
|
|
@@ -213,7 +216,7 @@ describe("ExecuteCursor hermetic — the stream loop's self-stop arms", () => {
|
|
|
213
216
|
step.event(ev.init()),
|
|
214
217
|
step.event(ev.assistant("Reading the whole repository first.")),
|
|
215
218
|
// 600 000 input tokens at the fixture's $1/M base rate = $0.60 > $0.50.
|
|
216
|
-
step.turnEnded({ inputTokens: 600_000, outputTokens: 0 }),
|
|
219
|
+
step.turnEnded({ inputTokens: 600_000, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
217
220
|
step.event(ev.assistant(NEVER_SEEN)),
|
|
218
221
|
step.finished({ result: NEVER_SEEN }),
|
|
219
222
|
],
|
|
@@ -315,7 +318,7 @@ describe("ExecuteCursor hermetic — the stream loop's self-stop arms", () => {
|
|
|
315
318
|
expect(final.messages.some((m) => m.content === NEVER_SEEN), "the event after the STOP is never processed").toBe(false);
|
|
316
319
|
|
|
317
320
|
// ── Assert: engine disposition ───────────────────────────────────────────
|
|
318
|
-
//
|
|
321
|
+
// Since #1070: the SDK run is cancelled on every stop; before, a
|
|
319
322
|
// platform stop broke the loop and left the run executing behind the
|
|
320
323
|
// parked handle.
|
|
321
324
|
expect(agent.runs[0].cancelCalls, "the runtime's stop cancelled the run once").toHaveLength(1);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: SUB-AGENT DELEGATION on the Cursor harness — the root agent
|
|
3
|
+
* hands work to a sub-agent through the SDK's `task` tool, and the sub-agent's
|
|
4
|
+
* own conversation arrives, whole, in the `task` call's result.
|
|
5
|
+
*
|
|
6
|
+
* Invariant pinned: the translator opens a sub-agent for every
|
|
7
|
+
* `tool_call` named `task` (`sub_agent_started` before the row's own start) as
|
|
8
|
+
* well as writing the row to the root transcript. The `running` event opens a `SubAgentExecution` keyed by the
|
|
9
|
+
* task call id (`name` from `subagentType`, `subject` from `description`,
|
|
10
|
+
* `input` from `prompt`, IN_PROGRESS); the `completed` event closes it
|
|
11
|
+
* COMPLETED with `output` (the stringified result) and rebuilds its transcript
|
|
12
|
+
* from the result's `conversationSteps` (`sub-agent-steps.ts`): an
|
|
13
|
+
* `assistantMessage` step is an AI row, and a `toolCall` step is a tool row on
|
|
14
|
+
* the transcript's LAST AI message — the assistant step that proposed it — named
|
|
15
|
+
* from its `<kind>ToolCall` key (`buildSubAgentToolCall`), with the SDK's
|
|
16
|
+
* `toolCallId` as its id, its status from the `result` oneof, both timestamps,
|
|
17
|
+
* `args` and `argsPreview`; a tool step with no assistant step before it gets
|
|
18
|
+
* an empty AI message of its own, the root transcript's rule. The root
|
|
19
|
+
* transcript carries the `task` row itself (COMPLETED, `result` = the same
|
|
20
|
+
* stringified object) on the message that proposed it. This is Cursor's
|
|
21
|
+
* timing — the sub-agent's rows exist only at completion — and stays Cursor's.
|
|
22
|
+
*
|
|
23
|
+
* Moved 2026-09-14 (#1097), the ONE hunk this golden was
|
|
24
|
+
* predicted to take: the sub-agent's `read` row sits on the AI message of the
|
|
25
|
+
* assistant step that precedes it (`messages[0]`, "Looking at README.md."),
|
|
26
|
+
* and the `content: ""` message that carried it until then (`messages[1]`) is
|
|
27
|
+
* gone — the canonical builder attaches a tool row to its scope's current AI
|
|
28
|
+
* message, the shape native's sub-agent transcripts took in #1096, and the
|
|
29
|
+
* accumulator's steps extractor was aligned before the swap to the shared
|
|
30
|
+
* builder so the swap moved nothing.
|
|
31
|
+
*
|
|
32
|
+
* On the record as found (#1134): the `task` row's `result` and the
|
|
33
|
+
* sub-agent's `output` are two copies of one stringified object.
|
|
34
|
+
*
|
|
35
|
+
* Why this net exists: none of the seventeen earlier Cursor goldens carried
|
|
36
|
+
* a sub-agent row, and #1097 deleted the transcript-rebuild whole.
|
|
37
|
+
*
|
|
38
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
39
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
43
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
44
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
45
|
+
import {
|
|
46
|
+
ExecutionPhase,
|
|
47
|
+
MessageType,
|
|
48
|
+
SubAgentStatus,
|
|
49
|
+
ToolCallStatus,
|
|
50
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
51
|
+
|
|
52
|
+
vi.mock("@cursor/sdk", async () =>
|
|
53
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
54
|
+
);
|
|
55
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
56
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
import {
|
|
60
|
+
ScriptedClock,
|
|
61
|
+
createHermeticEnvironment,
|
|
62
|
+
type HermeticEnvironment,
|
|
63
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
64
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
65
|
+
import {
|
|
66
|
+
FIXTURE,
|
|
67
|
+
SDK_CATALOG,
|
|
68
|
+
beginCursorScenario,
|
|
69
|
+
cursorExecutionRecord,
|
|
70
|
+
runCursorTurn,
|
|
71
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
72
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
73
|
+
|
|
74
|
+
const AGENT_ID = "agent-hermetic-subagent-0001";
|
|
75
|
+
const RUN_ID = "run-hermetic-subagent-0001";
|
|
76
|
+
const TASK_CALL_ID = "call-hermetic-task-0001";
|
|
77
|
+
const SUB_READ_CALL_ID = "call-hermetic-sub-read-0001";
|
|
78
|
+
const USER_MESSAGE = "Ask the helper what README.md says.";
|
|
79
|
+
const ROOT_OPENING = "Delegating to the helper.";
|
|
80
|
+
const TASK_ARGS = {
|
|
81
|
+
subagentType: "helper",
|
|
82
|
+
description: "Read README.md and report its content.",
|
|
83
|
+
prompt: "Read README.md and reply with its content in one line.",
|
|
84
|
+
};
|
|
85
|
+
const HELPER_OPENING = "Looking at README.md.";
|
|
86
|
+
const HELPER_READ_ARGS = { path: "README.md" };
|
|
87
|
+
const HELPER_READ_RESULT = "# Hermetic\n\nA fixture readme.\n";
|
|
88
|
+
const HELPER_ANSWER = "README.md is a one-line fixture description.";
|
|
89
|
+
/** The SDK's task result: the sub-agent's conversation as protobuf-oneof JSON steps. */
|
|
90
|
+
const TASK_RESULT = {
|
|
91
|
+
conversationSteps: [
|
|
92
|
+
{ type: "assistantMessage", message: { text: HELPER_OPENING } },
|
|
93
|
+
{
|
|
94
|
+
toolCall: {
|
|
95
|
+
toolCallId: SUB_READ_CALL_ID,
|
|
96
|
+
readToolCall: { args: HELPER_READ_ARGS, result: { success: { content: HELPER_READ_RESULT } } },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{ type: "assistantMessage", message: { text: HELPER_ANSWER } },
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
const ROOT_CLOSING = "The helper reports: README.md is a one-line fixture description.";
|
|
103
|
+
|
|
104
|
+
describe("ExecuteCursor hermetic — sub-agent delegation", () => {
|
|
105
|
+
let env: HermeticEnvironment;
|
|
106
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
107
|
+
const clock = new ScriptedClock();
|
|
108
|
+
|
|
109
|
+
beforeAll(() => {
|
|
110
|
+
env = createHermeticEnvironment();
|
|
111
|
+
registry = stubRegistryFetch();
|
|
112
|
+
clock.install();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
afterAll(() => {
|
|
116
|
+
clock.uninstall();
|
|
117
|
+
registry.restore();
|
|
118
|
+
env.dispose();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("tracks the task as a SubAgentExecution keyed by the call id and rebuilds its transcript at completion", async () => {
|
|
122
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
123
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
124
|
+
const agent = new ScriptedCursorAgent({
|
|
125
|
+
agentId: AGENT_ID,
|
|
126
|
+
runIds: [RUN_ID],
|
|
127
|
+
observeStep: () => clock.tick(),
|
|
128
|
+
turns: [
|
|
129
|
+
[
|
|
130
|
+
step.event(ev.init()),
|
|
131
|
+
step.event(ev.assistant(ROOT_OPENING)),
|
|
132
|
+
step.event(ev.toolCall(TASK_CALL_ID, "task", "running", TASK_ARGS)),
|
|
133
|
+
step.event(ev.toolCall(TASK_CALL_ID, "task", "completed", TASK_ARGS, TASK_RESULT)),
|
|
134
|
+
step.event(ev.assistant(ROOT_CLOSING)),
|
|
135
|
+
step.turnEnded({ inputTokens: 3_900, outputTokens: 102, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
136
|
+
step.finished({ result: ROOT_CLOSING, model: { id: FIXTURE.model, params: [] } }),
|
|
137
|
+
],
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
141
|
+
const scenario = beginCursorScenario({
|
|
142
|
+
env,
|
|
143
|
+
clock,
|
|
144
|
+
record,
|
|
145
|
+
sdk: { agents: [agent], catalog: SDK_CATALOG },
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
149
|
+
const invocation = await runCursorTurn(scenario);
|
|
150
|
+
|
|
151
|
+
// ── Assert: outcome ──────────────────────────────────────────────────────
|
|
152
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
153
|
+
expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
154
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
155
|
+
|
|
156
|
+
// ── Assert: the root transcript ──────────────────────────────────────────
|
|
157
|
+
const final = record.lastFullStatus!;
|
|
158
|
+
expect(record.toolCalls().map((tc) => [tc.id, tc.name, tc.status])).toEqual([
|
|
159
|
+
[TASK_CALL_ID, "task", ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
160
|
+
]);
|
|
161
|
+
expect(final.messages.map((m) => [m.type, m.content, m.toolCalls.map((tc) => tc.id)])).toEqual([
|
|
162
|
+
[MessageType.MESSAGE_AI, ROOT_OPENING, [TASK_CALL_ID]],
|
|
163
|
+
[MessageType.MESSAGE_AI, ROOT_CLOSING, []],
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
// ── Assert: the sub-agent execution ──────────────────────────────────────
|
|
167
|
+
expect(final.subAgentExecutions).toHaveLength(1);
|
|
168
|
+
const sub = final.subAgentExecutions[0];
|
|
169
|
+
expect(sub.id, "keyed by the task call id").toBe(TASK_CALL_ID);
|
|
170
|
+
expect(sub.name).toBe("helper");
|
|
171
|
+
expect(sub.subject).toBe(TASK_ARGS.description);
|
|
172
|
+
expect(sub.input).toBe(TASK_ARGS.prompt);
|
|
173
|
+
expect(sub.status).toBe(SubAgentStatus.SUB_AGENT_COMPLETED);
|
|
174
|
+
expect(sub.startedAt < sub.completedAt).toBe(true);
|
|
175
|
+
expect(sub.output, "the stringified task result").toBe(JSON.stringify(TASK_RESULT));
|
|
176
|
+
|
|
177
|
+
// The rebuilt transcript: the tool step joins the assistant step that
|
|
178
|
+
// proposed it, the root transcript's own boundary rule.
|
|
179
|
+
expect(sub.messages.map((m) => [m.type, m.content, m.toolCalls.map((tc) => tc.id)])).toEqual([
|
|
180
|
+
[MessageType.MESSAGE_AI, HELPER_OPENING, [SUB_READ_CALL_ID]],
|
|
181
|
+
[MessageType.MESSAGE_AI, HELPER_ANSWER, []],
|
|
182
|
+
]);
|
|
183
|
+
const subRead = sub.messages[0].toolCalls[0];
|
|
184
|
+
expect(subRead.name, "the bare tool name from the readToolCall key").toBe("read");
|
|
185
|
+
expect(subRead.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
186
|
+
expect(subRead.args).toEqual(HELPER_READ_ARGS);
|
|
187
|
+
expect(subRead.result).toBe(JSON.stringify({ content: HELPER_READ_RESULT }));
|
|
188
|
+
|
|
189
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
190
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
191
|
+
|
|
192
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
193
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
194
|
+
await expect(json).toMatchFileSnapshot("./goldens/sub-agent-delegation.status.json");
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: a turn with MODEL THINKING on both sides of a tool call,
|
|
3
|
+
* through the whole `ExecuteCursor` activity.
|
|
4
|
+
*
|
|
5
|
+
* Invariant pinned (the rule the canonical builder had to reproduce when it
|
|
6
|
+
* took over in #1097): the Cursor SDK has ONE `run_id` per `send()`, so the transcript's
|
|
7
|
+
* segmentation is not by run but by tool call — a `tool_call` event closes the
|
|
8
|
+
* run's streaming AI and THINKING rows (the translator's `message_finish` for
|
|
9
|
+
* the open segment; until #1097 the accumulator's own finalize on a tool call),
|
|
10
|
+
* and the thinking or text that follows opens NEW rows. Hence the shape:
|
|
11
|
+
* THINKING(T1), AI(A1 + the read row), THINKING(T2), AI(A2). The read row lands
|
|
12
|
+
* on A1 (the last AI message by backward scan; a THINKING row is skipped), and
|
|
13
|
+
* no row is left `isStreaming` when the turn ends.
|
|
14
|
+
*
|
|
15
|
+
* Predicted for the swap to the shared builder (2026-09-14): NO move.
|
|
16
|
+
* The builder keeps one THINKING row per segment on Cursor (the translator
|
|
17
|
+
* mints a segment id per text-or-thinking run between tool calls) and keeps
|
|
18
|
+
* the tool on the message that proposed it. A diff in this golden is a
|
|
19
|
+
* pause, not a regeneration.
|
|
20
|
+
*
|
|
21
|
+
* Why this net exists: none of the seventeen earlier Cursor goldens carried
|
|
22
|
+
* a THINKING row, and the thinking rule is one #1097 rewrote.
|
|
23
|
+
*
|
|
24
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
25
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
29
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
30
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
31
|
+
import { ExecutionPhase, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
32
|
+
|
|
33
|
+
vi.mock("@cursor/sdk", async () =>
|
|
34
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
35
|
+
);
|
|
36
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
37
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
ScriptedClock,
|
|
42
|
+
createHermeticEnvironment,
|
|
43
|
+
type HermeticEnvironment,
|
|
44
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
45
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
46
|
+
import {
|
|
47
|
+
FIXTURE,
|
|
48
|
+
SDK_CATALOG,
|
|
49
|
+
beginCursorScenario,
|
|
50
|
+
cursorExecutionRecord,
|
|
51
|
+
runCursorTurn,
|
|
52
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
53
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
54
|
+
|
|
55
|
+
const AGENT_ID = "agent-hermetic-thinking-0001";
|
|
56
|
+
const RUN_ID = "run-hermetic-thinking-0001";
|
|
57
|
+
const CALL_ID = "call-hermetic-thinking-read-0001";
|
|
58
|
+
const USER_MESSAGE = "Summarise README.md.";
|
|
59
|
+
const THINKING_BEFORE = "The user wants a summary; I should read the file first.";
|
|
60
|
+
const TEXT_BEFORE = "Reading the file.";
|
|
61
|
+
const READ_ARGS = { path: "README.md" };
|
|
62
|
+
const READ_RESULT = "# Hermetic\n\nA fixture readme.\n";
|
|
63
|
+
const THINKING_AFTER = "One line of content; a one-sentence summary will do.";
|
|
64
|
+
const TEXT_AFTER = "README.md is a one-line fixture description.";
|
|
65
|
+
|
|
66
|
+
describe("ExecuteCursor hermetic — thinking on both sides of a tool call", () => {
|
|
67
|
+
let env: HermeticEnvironment;
|
|
68
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
69
|
+
const clock = new ScriptedClock();
|
|
70
|
+
|
|
71
|
+
beforeAll(() => {
|
|
72
|
+
env = createHermeticEnvironment();
|
|
73
|
+
registry = stubRegistryFetch();
|
|
74
|
+
clock.install();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterAll(() => {
|
|
78
|
+
clock.uninstall();
|
|
79
|
+
registry.restore();
|
|
80
|
+
env.dispose();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("folds into THINKING, AI+row, THINKING, AI with nothing left streaming and completes", async () => {
|
|
84
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
85
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
86
|
+
const agent = new ScriptedCursorAgent({
|
|
87
|
+
agentId: AGENT_ID,
|
|
88
|
+
runIds: [RUN_ID],
|
|
89
|
+
observeStep: () => clock.tick(),
|
|
90
|
+
turns: [
|
|
91
|
+
[
|
|
92
|
+
step.event(ev.init()),
|
|
93
|
+
step.event(ev.thinking(THINKING_BEFORE)),
|
|
94
|
+
step.event(ev.assistant(TEXT_BEFORE)),
|
|
95
|
+
step.event(ev.toolCall(CALL_ID, "read", "running", READ_ARGS)),
|
|
96
|
+
step.event(ev.toolCall(CALL_ID, "read", "completed", READ_ARGS, READ_RESULT)),
|
|
97
|
+
step.event(ev.thinking(THINKING_AFTER)),
|
|
98
|
+
step.event(ev.assistant(TEXT_AFTER)),
|
|
99
|
+
step.turnEnded({ inputTokens: 2_400, outputTokens: 140, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
100
|
+
step.finished({ result: TEXT_AFTER, model: { id: FIXTURE.model, params: [] } }),
|
|
101
|
+
],
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
105
|
+
const scenario = beginCursorScenario({
|
|
106
|
+
env,
|
|
107
|
+
clock,
|
|
108
|
+
record,
|
|
109
|
+
sdk: { agents: [agent], catalog: SDK_CATALOG },
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
113
|
+
const invocation = await runCursorTurn(scenario);
|
|
114
|
+
|
|
115
|
+
// ── Assert: outcome ──────────────────────────────────────────────────────
|
|
116
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
117
|
+
expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
118
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
119
|
+
|
|
120
|
+
// ── Assert: the segmentation ─────────────────────────────────────────────
|
|
121
|
+
const final = record.lastFullStatus!;
|
|
122
|
+
expect(
|
|
123
|
+
final.messages.map((m) => [m.type, m.content]),
|
|
124
|
+
"a tool call closes the run's THINKING and AI rows; what follows opens new ones",
|
|
125
|
+
).toEqual([
|
|
126
|
+
[MessageType.MESSAGE_THINKING, THINKING_BEFORE],
|
|
127
|
+
[MessageType.MESSAGE_AI, TEXT_BEFORE],
|
|
128
|
+
[MessageType.MESSAGE_THINKING, THINKING_AFTER],
|
|
129
|
+
[MessageType.MESSAGE_AI, TEXT_AFTER],
|
|
130
|
+
]);
|
|
131
|
+
expect(final.messages.every((m) => !m.isStreaming), "finalize clears every streaming flag").toBe(true);
|
|
132
|
+
|
|
133
|
+
// ── Assert: the row sits on the text that proposed it ────────────────────
|
|
134
|
+
const rows = record.toolCalls();
|
|
135
|
+
expect(rows.map((tc) => [tc.id, tc.status])).toEqual([[CALL_ID, ToolCallStatus.TOOL_CALL_COMPLETED]]);
|
|
136
|
+
expect(final.messages[1].toolCalls.map((tc) => tc.id), "the read row is on AI(A1), not on a THINKING row").toEqual([CALL_ID]);
|
|
137
|
+
|
|
138
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
139
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
140
|
+
|
|
141
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
142
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
143
|
+
await expect(json).toMatchFileSnapshot("./goldens/thinking-turn.status.json");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* plane ever sees for these executions is EXECUTION_FAILED — `persistedPhases`
|
|
29
29
|
* is `[FAILED]`, not `[IN_PROGRESS, FAILED]`.
|
|
30
30
|
*
|
|
31
|
-
* Golden hunk since
|
|
31
|
+
* Golden hunk since #1096: `sdk-error-at-create`
|
|
32
32
|
* carries an EMPTY `fileChangeProgress` (`changeSetId`, `capturedAt`, no
|
|
33
33
|
* entries). The runtime refreshes the strip on every write, and this turn's
|
|
34
34
|
* FAILED write is its first write in capture mode (the hermetic environment
|
|
@@ -49,6 +49,9 @@ import { ExecutionPhase, MessageType } from "@stigmer/protos/ai/stigmer/agentic/
|
|
|
49
49
|
vi.mock("@cursor/sdk", async () =>
|
|
50
50
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
51
51
|
);
|
|
52
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
53
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
54
|
+
);
|
|
52
55
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
53
56
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
54
57
|
);
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: the agent's TO-DO LIST — two `updateTodos` tool calls, the
|
|
3
|
+
* second a `merge`, through the whole `ExecuteCursor` activity.
|
|
4
|
+
*
|
|
5
|
+
* Invariant pinned: a todo tool call is a row like any other AND a projection.
|
|
6
|
+
* The shared builder projects a COMPLETED `ToolKind.TODO` row's `args.todos`
|
|
7
|
+
* into `status.todos` through the shared `applyTodoUpdate` — a full replace by
|
|
8
|
+
* default, a MERGE when the SDK's args say `merge: true` (the first write's
|
|
9
|
+
* `createdAt` survives, `updatedAt` moves) — and the row stays on the message
|
|
10
|
+
* that proposed it: an `updateTodos`
|
|
11
|
+
* row (COMPLETED, `toolKind` TODO, `args`, `argsPreview`, `result`) on
|
|
12
|
+
* `messages[0]` and another on `messages[1]`. So: three AI messages, two
|
|
13
|
+
* rows, two todos. The web console hides the row (`isInternalTool`); the CLI
|
|
14
|
+
* TUI shows it, as it does for native.
|
|
15
|
+
*
|
|
16
|
+
* Moved 2026-09-14 (#1097), the TWO hunks this golden was
|
|
17
|
+
* predicted to take: until then the accumulator DROPPED the row (a suppressed-
|
|
18
|
+
* tool-names set) and the transcript showed three AI messages with
|
|
19
|
+
* no rows — a per-harness branch on a tool name the canonical builder does
|
|
20
|
+
* not have (native has always kept its `write_todos` row). The accumulator
|
|
21
|
+
* was aligned before the swap to the shared builder so the swap moved nothing;
|
|
22
|
+
* `todos` was and is byte-identical. The todo tracker went at the swap, where
|
|
23
|
+
* the builder took over projecting the todos from the row.
|
|
24
|
+
*
|
|
25
|
+
* Why this net exists: no earlier Cursor golden carried a todo.
|
|
26
|
+
*
|
|
27
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
28
|
+
* npx vitest run src/activities/execute-cursor/__tests__/hermetic -u
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
32
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
33
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
34
|
+
import { ExecutionPhase, MessageType, TodoStatus, ToolCallStatus, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
35
|
+
|
|
36
|
+
vi.mock("@cursor/sdk", async () =>
|
|
37
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
38
|
+
);
|
|
39
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
40
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
import {
|
|
44
|
+
ScriptedClock,
|
|
45
|
+
createHermeticEnvironment,
|
|
46
|
+
type HermeticEnvironment,
|
|
47
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
48
|
+
import { ScriptedCursorAgent, sdkEvents, step } from "../../__test-utils__/scripted-agent.js";
|
|
49
|
+
import {
|
|
50
|
+
FIXTURE,
|
|
51
|
+
SDK_CATALOG,
|
|
52
|
+
beginCursorScenario,
|
|
53
|
+
cursorExecutionRecord,
|
|
54
|
+
runCursorTurn,
|
|
55
|
+
} from "../../__test-utils__/hermetic-cursor.js";
|
|
56
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
57
|
+
|
|
58
|
+
const AGENT_ID = "agent-hermetic-todo-0001";
|
|
59
|
+
const RUN_ID = "run-hermetic-todo-0001";
|
|
60
|
+
const FIRST_CALL_ID = "call-hermetic-todo-0001";
|
|
61
|
+
const SECOND_CALL_ID = "call-hermetic-todo-0002";
|
|
62
|
+
const USER_MESSAGE = "Plan the two steps, then do the first.";
|
|
63
|
+
const TEXT_PLAN = "Two steps; writing them down.";
|
|
64
|
+
/** The current SDK's `updateTodos` shape: camelCase statuses, no per-item id. */
|
|
65
|
+
const FIRST_WRITE = {
|
|
66
|
+
todos: [
|
|
67
|
+
{ content: "Read the fixture", status: "pending" },
|
|
68
|
+
{ content: "Summarise it", status: "pending" },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
const TEXT_WORK = "First step done.";
|
|
72
|
+
const SECOND_WRITE = {
|
|
73
|
+
todos: [{ content: "Read the fixture", status: "completed" }],
|
|
74
|
+
merge: true,
|
|
75
|
+
};
|
|
76
|
+
const TEXT_CLOSING = "The first step is complete; the second is pending.";
|
|
77
|
+
|
|
78
|
+
describe("ExecuteCursor hermetic — todo writes", () => {
|
|
79
|
+
let env: HermeticEnvironment;
|
|
80
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
81
|
+
const clock = new ScriptedClock();
|
|
82
|
+
|
|
83
|
+
beforeAll(() => {
|
|
84
|
+
env = createHermeticEnvironment();
|
|
85
|
+
registry = stubRegistryFetch();
|
|
86
|
+
clock.install();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterAll(() => {
|
|
90
|
+
clock.uninstall();
|
|
91
|
+
registry.restore();
|
|
92
|
+
env.dispose();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("projects a replace then a merge into status.todos and keeps a row per write in the transcript", async () => {
|
|
96
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
97
|
+
const ev = sdkEvents(AGENT_ID, RUN_ID);
|
|
98
|
+
const agent = new ScriptedCursorAgent({
|
|
99
|
+
agentId: AGENT_ID,
|
|
100
|
+
runIds: [RUN_ID],
|
|
101
|
+
observeStep: () => clock.tick(),
|
|
102
|
+
turns: [
|
|
103
|
+
[
|
|
104
|
+
step.event(ev.init()),
|
|
105
|
+
step.event(ev.assistant(TEXT_PLAN)),
|
|
106
|
+
step.event(ev.toolCall(FIRST_CALL_ID, "updateTodos", "running", FIRST_WRITE)),
|
|
107
|
+
step.event(ev.toolCall(FIRST_CALL_ID, "updateTodos", "completed", FIRST_WRITE, "ok")),
|
|
108
|
+
step.event(ev.assistant(TEXT_WORK)),
|
|
109
|
+
step.event(ev.toolCall(SECOND_CALL_ID, "updateTodos", "running", SECOND_WRITE)),
|
|
110
|
+
step.event(ev.toolCall(SECOND_CALL_ID, "updateTodos", "completed", SECOND_WRITE, "ok")),
|
|
111
|
+
step.event(ev.assistant(TEXT_CLOSING)),
|
|
112
|
+
step.turnEnded({ inputTokens: 2_600, outputTokens: 160, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
113
|
+
step.finished({ result: TEXT_CLOSING, model: { id: FIXTURE.model, params: [] } }),
|
|
114
|
+
],
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
const record = cursorExecutionRecord({ message: USER_MESSAGE });
|
|
118
|
+
const scenario = beginCursorScenario({
|
|
119
|
+
env,
|
|
120
|
+
clock,
|
|
121
|
+
record,
|
|
122
|
+
sdk: { agents: [agent], catalog: SDK_CATALOG },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
126
|
+
const invocation = await runCursorTurn(scenario);
|
|
127
|
+
|
|
128
|
+
// ── Assert: outcome ──────────────────────────────────────────────────────
|
|
129
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
130
|
+
expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
131
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
132
|
+
|
|
133
|
+
// ── Assert: the transcript — a row per write, on the message that proposed it ──
|
|
134
|
+
const final = record.lastFullStatus!;
|
|
135
|
+
expect(
|
|
136
|
+
final.messages.map((m) => [m.type, m.content, m.toolCalls.length]),
|
|
137
|
+
"the todo call splits the text like any tool, and its row stays",
|
|
138
|
+
).toEqual([
|
|
139
|
+
[MessageType.MESSAGE_AI, TEXT_PLAN, 1],
|
|
140
|
+
[MessageType.MESSAGE_AI, TEXT_WORK, 1],
|
|
141
|
+
[MessageType.MESSAGE_AI, TEXT_CLOSING, 0],
|
|
142
|
+
]);
|
|
143
|
+
const rows = record.toolCalls();
|
|
144
|
+
expect(rows.map((tc) => [tc.id, tc.toolKind, tc.status])).toEqual([
|
|
145
|
+
[FIRST_CALL_ID, ToolKind.TODO, ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
146
|
+
[SECOND_CALL_ID, ToolKind.TODO, ToolCallStatus.TOOL_CALL_COMPLETED],
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
// ── Assert: the todos, by key (byte-identical across the swap) ──────────
|
|
150
|
+
expect(Object.keys(final.todos).sort()).toEqual(["todo-0", "todo-1"]);
|
|
151
|
+
const first = final.todos["todo-0"];
|
|
152
|
+
const second = final.todos["todo-1"];
|
|
153
|
+
expect([first.content, first.status]).toEqual(["Read the fixture", TodoStatus.TODO_COMPLETED]);
|
|
154
|
+
expect(first.createdAt < first.updatedAt, "the merge kept the first write's createdAt and moved updatedAt").toBe(true);
|
|
155
|
+
expect([second.content, second.status]).toEqual(["Summarise it", TodoStatus.TODO_PENDING]);
|
|
156
|
+
expect(second.createdAt, "untouched by the merge").toBe(second.updatedAt);
|
|
157
|
+
|
|
158
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
159
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
160
|
+
|
|
161
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
162
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
163
|
+
await expect(json).toMatchFileSnapshot("./goldens/todo-write.status.json");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* event's args, `result` from the completed event, `startedAt`/`completedAt`
|
|
9
9
|
* from the two events' instants, `toolKind` classified, no approval fields set
|
|
10
10
|
* (a read-only built-in is not gated) — and the turn ends COMPLETED. The golden
|
|
11
|
-
* (`goldens/tool-call.status.json`) is the row shape
|
|
12
|
-
* builder must reproduce byte for byte.
|
|
11
|
+
* (`goldens/tool-call.status.json`) is the row shape the canonical transcript
|
|
12
|
+
* builder (#1097) must reproduce byte for byte.
|
|
13
13
|
*
|
|
14
14
|
* Parent phase rows exercised beyond `plain-turn`: run the turn and consume
|
|
15
|
-
* the engine stream (
|
|
15
|
+
* the engine stream (`translator.ts` into the shared `TranscriptBuilder`); the turn
|
|
16
16
|
* boundary with nothing to gate or capture (a read tool writes no file; the
|
|
17
17
|
* per-session workspace is unchanged, so the capture finds no candidate).
|
|
18
18
|
*
|
|
@@ -28,6 +28,9 @@ import { ExecutionPhase, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agent
|
|
|
28
28
|
vi.mock("@cursor/sdk", async () =>
|
|
29
29
|
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
30
30
|
);
|
|
31
|
+
vi.mock("@cursor/sdk/sqlite", async () =>
|
|
32
|
+
(await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSqliteModule(),
|
|
33
|
+
);
|
|
31
34
|
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
32
35
|
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
33
36
|
);
|
|
@@ -86,7 +89,7 @@ describe("ExecuteCursor hermetic — ungated tool call", () => {
|
|
|
86
89
|
step.event(ev.toolCall(CALL_ID, "read", "running", READ_ARGS)),
|
|
87
90
|
step.event(ev.toolCall(CALL_ID, "read", "completed", READ_ARGS, READ_RESULT)),
|
|
88
91
|
step.event(ev.assistant(ASSISTANT_TEXT)),
|
|
89
|
-
step.turnEnded({ inputTokens: 2_000, outputTokens: 90 }),
|
|
92
|
+
step.turnEnded({ inputTokens: 2_000, outputTokens: 90, cacheReadTokens: 0, cacheWriteTokens: 0 }),
|
|
90
93
|
step.finished({ result: ASSISTANT_TEXT, model: { id: FIXTURE.model, params: [] } }),
|
|
91
94
|
],
|
|
92
95
|
],
|