@stigmer/runner 3.15.3-dev.20260914121148 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- 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 +2 -2
- 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 +47 -12
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +41 -10
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +15 -6
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +17 -4
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.d.ts +4 -4
- package/dist/activities/execute-cursor/adapter.js +4 -4
- 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/service-tier.js +1 -2
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- 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/activities/generate-session-subject.d.ts +25 -4
- package/dist/activities/generate-session-subject.js +17 -7
- package/dist/activities/generate-session-subject.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +17 -6
- package/dist/client/stigmer-client.js +47 -14
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/config.d.ts +6 -3
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- 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/interceptors/run-credential-activity.d.ts +28 -0
- package/dist/interceptors/run-credential-activity.js +56 -0
- package/dist/interceptors/run-credential-activity.js.map +1 -0
- 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 +10 -4
- package/dist/runner-manager.js.map +1 -1
- package/dist/shared/activity-input.d.ts +9 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.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-zip.d.ts +2 -2
- package/dist/shared/attachment-zip.js +2 -2
- package/dist/shared/channel-attachment.d.ts +8 -5
- package/dist/shared/channel-attachment.js +10 -6
- package/dist/shared/channel-attachment.js.map +1 -1
- 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/filereview/progress.d.ts +13 -0
- package/dist/shared/filereview/progress.js +13 -0
- package/dist/shared/filereview/progress.js.map +1 -1
- 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/memory-attachment.d.ts +16 -9
- package/dist/shared/memory-attachment.js +18 -10
- package/dist/shared/memory-attachment.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/run-credential-store.d.ts +32 -0
- package/dist/shared/run-credential-store.js +39 -0
- package/dist/shared/run-credential-store.js.map +1 -0
- package/dist/shared/run-credential.d.ts +61 -0
- package/dist/shared/run-credential.js +68 -0
- package/dist/shared/run-credential.js.map +1 -0
- 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/synthesized-attachment.d.ts +30 -3
- package/dist/shared/synthesized-attachment.js +26 -0
- package/dist/shared/synthesized-attachment.js.map +1 -1
- 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/dist/worker.js +10 -3
- package/dist/worker.js.map +1 -1
- package/dist/workflow-source.d.ts +10 -0
- package/dist/workflow-source.js +12 -0
- package/dist/workflow-source.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +15 -0
- package/dist/workflows/execute-from-execution.js +11 -0
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/dist/workflows/index.d.ts +5 -3
- package/dist/workflows/index.js +5 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/interceptors/run-credential.d.ts +34 -0
- package/dist/workflows/interceptors/run-credential.js +71 -0
- package/dist/workflows/interceptors/run-credential.js.map +1 -0
- package/package.json +4 -4
- 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__/run-credential-e2e.test.ts +233 -0
- package/src/__tests__/run-credential-registration.test.ts +80 -0
- package/src/__tests__/runner-manager.test.ts +1 -1
- package/src/activities/__tests__/generate-session-subject-wire.test.ts +119 -0
- package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +1 -1
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +2 -2
- 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 +67 -17
- package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +19 -5
- 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-event-recorder.test.ts +142 -0
- package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +19 -8
- package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +10 -8
- 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 +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -1
- 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 +7 -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 +5 -5
- package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +2 -1
- 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 +8 -8
- 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 +1 -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 +3 -3
- 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 +13 -1
- package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +2 -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__/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 +4 -4
- 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/service-tier.ts +1 -2
- 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/activities/generate-session-subject.ts +29 -4
- package/src/client/__tests__/stigmer-client.test.ts +92 -0
- package/src/client/stigmer-client.ts +48 -14
- package/src/config.ts +6 -3
- 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/interceptors/__tests__/run-credential-activity.test.ts +133 -0
- package/src/interceptors/run-credential-activity.ts +67 -0
- 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 +14 -4
- 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__/channel-attachment.test.ts +12 -0
- package/src/shared/__tests__/cost-guard.test.ts +1 -1
- package/src/shared/__tests__/exact-apply.test.ts +2 -1
- package/src/shared/__tests__/memory-attachment.test.ts +17 -0
- package/src/shared/__tests__/persist-decision.test.ts +19 -33
- package/src/shared/__tests__/prompt-sections.test.ts +1 -1
- package/src/shared/__tests__/run-credential.test.ts +111 -0
- 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/activity-input.ts +21 -0
- 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-zip.ts +2 -2
- package/src/shared/channel-attachment.ts +14 -6
- 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/filereview/progress.ts +13 -0
- package/src/shared/heartbeat.ts +1 -1
- package/src/shared/llm-backend.ts +0 -4
- package/src/shared/memory-attachment.ts +22 -10
- 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/run-credential-store.ts +45 -0
- package/src/shared/run-credential.ts +70 -0
- 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/synthesized-attachment.ts +38 -3
- 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/src/worker.ts +15 -3
- package/src/workflow-source.ts +15 -0
- package/src/workflows/__tests__/run-credential-interceptor.test.ts +178 -0
- package/src/workflows/execute-from-execution.ts +17 -0
- package/src/workflows/index.ts +5 -3
- package/src/workflows/interceptors/run-credential.ts +101 -0
- 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
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
* the facts the runtime owes on it: the terminal phase, the copy
|
|
20
20
|
* (`terminal-table.ts` `TERMINAL_COPY`, byte-pinned), `status.error`,
|
|
21
21
|
* `completedAt`, "the step after never runs", the whole-activity heartbeat,
|
|
22
|
-
* one bind for an engine-minted harness, the slim return
|
|
22
|
+
* one bind for an engine-minted harness, the slim return, and — on every
|
|
23
|
+
* arm, whatever the outcome — a transcript that streams nothing once the
|
|
24
|
+
* turn has settled ({@link assertTranscriptStreamsNothing}). What is NOT
|
|
23
25
|
* asserted here is engine cadence — how many IN_PROGRESS persists precede
|
|
24
26
|
* the WAITING write, the exact setup labels beyond the runtime's own, the
|
|
25
27
|
* exact price a vendor's table yields — because those are a subject's facts;
|
|
@@ -216,13 +218,47 @@ export class RuntimeExecutionDriver {
|
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
220
|
|
|
219
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* The final full status the record holds, checked for the one fact every
|
|
223
|
+
* settled turn owes its transcript before the arm reads anything else. An
|
|
224
|
+
* arm that persisted nothing is a failure, named.
|
|
225
|
+
*/
|
|
220
226
|
function finalStatusOf(harness: RuntimeContractHarness, driver: RuntimeExecutionDriver): AgentExecutionStatus {
|
|
221
227
|
const final = driver.record.lastFullStatus;
|
|
222
228
|
if (!final) throw new Error(`${harness.subject.name}: the runtime persisted no full status`);
|
|
229
|
+
assertTranscriptStreamsNothing(harness.subject.name, final);
|
|
223
230
|
return final;
|
|
224
231
|
}
|
|
225
232
|
|
|
233
|
+
/**
|
|
234
|
+
* No streaming flag survives a settled turn — on any message, any tool-call
|
|
235
|
+
* row, in the root transcript or a sub-agent's — however the turn ended. A
|
|
236
|
+
* flag left on is a spinner the console never stops. The runtime closes
|
|
237
|
+
* them once, after `runTurn` returns, on every path (`harness/run-turn.ts`),
|
|
238
|
+
* so the fact holds for every subject and every outcome, and every arm
|
|
239
|
+
* checks it through {@link finalStatusOf}. Before the runtime owned that
|
|
240
|
+
* close, this fired on both real adapters' internal-failure arm: a turn that
|
|
241
|
+
* failed before its settle left its last message streaming. Exported so the
|
|
242
|
+
* kit's self-check can hand it a status that still streams and prove it
|
|
243
|
+
* fires.
|
|
244
|
+
*/
|
|
245
|
+
export function assertTranscriptStreamsNothing(subjectName: string, status: AgentExecutionStatus): void {
|
|
246
|
+
const streaming: string[] = [];
|
|
247
|
+
const sweep = (scope: string, messages: AgentExecutionStatus["messages"]): void => {
|
|
248
|
+
messages.forEach((m, i) => {
|
|
249
|
+
if (m.isStreaming) streaming.push(`${scope}messages[${i}]`);
|
|
250
|
+
for (const tc of m.toolCalls) {
|
|
251
|
+
if (tc.isStreaming) streaming.push(`${scope}messages[${i}].toolCalls[${tc.id}]`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
};
|
|
255
|
+
sweep("", status.messages);
|
|
256
|
+
for (const sub of status.subAgentExecutions) sweep(`subAgentExecutions[${sub.id}].`, sub.messages);
|
|
257
|
+
if (streaming.length > 0) {
|
|
258
|
+
throw new Error(`${subjectName}: a settled turn's transcript is still streaming at ${streaming.join(", ")}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
226
262
|
/** What every arm hands back so a subject's file can add its own facts. */
|
|
227
263
|
export interface RuntimeArmResult {
|
|
228
264
|
readonly driver: RuntimeExecutionDriver;
|
|
@@ -357,11 +393,11 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
|
|
|
357
393
|
expect(rows.get(keepId)?.fileChangeSetId, `${subject.name}: a flowed row is badged with the change set`).toBe(changeSetId);
|
|
358
394
|
expect(rows.get(dropId)?.fileChangeSetId).toBe(changeSetId);
|
|
359
395
|
expect(rows.get(keepId)?.status, `${subject.name}: a flowed row is COMPLETED, never gated`).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
360
|
-
// The strip is the runtime's, attached on its writes
|
|
396
|
+
// The strip is the runtime's, attached on its writes. Its COUNT is
|
|
361
397
|
// the capture floor's: a real engine's first persist precedes its first
|
|
362
398
|
// write and both writes land within one floor interval, so the pause can
|
|
363
|
-
// honestly read "0 files" beside a two-file candidate (
|
|
364
|
-
//
|
|
399
|
+
// honestly read "0 files" beside a two-file candidate (#1114: whether the
|
|
400
|
+
// terminal write should bypass the floor). The arm
|
|
365
401
|
// asserts the ownership, not the floor.
|
|
366
402
|
expect(afterCapture.fileChangeProgress?.changeSetId, `${subject.name}: the runtime carries this turn's strip`).toBe(changeSetId);
|
|
367
403
|
expect(afterCapture.completedAt, `${subject.name}: a review pause is not complete`).toBe("");
|
|
@@ -374,7 +410,7 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
|
|
|
374
410
|
const second = await driver.turn([]);
|
|
375
411
|
expect(slimOf(subject, second).phase, `${subject.name}: a pure file-review resume completes without the engine`).toBe("EXECUTION_COMPLETED");
|
|
376
412
|
// The kept file is the engine-not-re-run witness: a subject that counts
|
|
377
|
-
// executions by reading its ledger off disk (the native one
|
|
413
|
+
// executions by reading its ledger off disk (the native one) reads
|
|
378
414
|
// the DISCARDED file's count as 0 now, because the reconcile just restored
|
|
379
415
|
// that file to its baseline — which is the reconcile working, not the
|
|
380
416
|
// engine forgetting.
|
|
@@ -398,7 +434,7 @@ export async function assertFileReviewRoundTrip(harness: RuntimeContractHarness,
|
|
|
398
434
|
* settles the row SKIPPED (a REJECT row carries `error: "Rejected by user"`),
|
|
399
435
|
* so no WAITING row survives on a finished execution. This is the proto
|
|
400
436
|
* contract as of stigmer#197 and the runtime's own write
|
|
401
|
-
* (`harness/approval-decisions.ts`); before
|
|
437
|
+
* (`harness/approval-decisions.ts`); before #1096 the runtime FAILED a
|
|
402
438
|
* REJECTed reinvocation before any engine ran.
|
|
403
439
|
*/
|
|
404
440
|
export async function assertNonExecutingDecisionSettlesSkipped(
|
|
@@ -440,14 +476,14 @@ export async function assertNonExecutingDecisionSettlesSkipped(
|
|
|
440
476
|
* the next message), `error` starting with the cross-repo prefix Stigmer
|
|
441
477
|
* Cloud's channel delivery matches on, the user copy as the one system row,
|
|
442
478
|
* `completedAt` stamped, RETURNED (a retry would spend the same budget), and
|
|
443
|
-
* the step after the limit never processed. Before
|
|
444
|
-
* RETURNED this terminal without persisting it
|
|
479
|
+
* the step after the limit never processed. Before #1096 the native harness
|
|
480
|
+
* RETURNED this terminal without persisting it; the
|
|
445
481
|
* runtime's arm is the one persisted terminal.
|
|
446
482
|
*
|
|
447
483
|
* The execution SETS the budget it exhausts: the record carries the smallest
|
|
448
484
|
* valid `max_tool_rounds` (an unbounded execution has no limit to reach — on
|
|
449
485
|
* native `resolveRecursionLimit(0)` is unlimited, and the first run of this
|
|
450
|
-
* arm against it spun until the test timed out
|
|
486
|
+
* arm against it spun until the test timed out). The fake
|
|
451
487
|
* exhausts whatever budget it is given; a real engine spins its cheapest
|
|
452
488
|
* ungated tool until the graph stops it.
|
|
453
489
|
*/
|
|
@@ -625,12 +661,12 @@ const STALL_CLOCK_INTERVAL_MS = 25;
|
|
|
625
661
|
* park is reported while the chunks the engine produced before parking are
|
|
626
662
|
* still in its event pipeline ahead of the adapter's loop, so activity marks
|
|
627
663
|
* land AFTER a single tick and a frozen clock reads idle 0 from then on
|
|
628
|
-
* (
|
|
664
|
+
* (measured on the native adapter: 35 marks at the park, 37
|
|
629
665
|
* within 10 ms). A pull-based double's park implies everything before it
|
|
630
666
|
* was seen; a real stall is wall time passing while the engine is silent,
|
|
631
667
|
* whichever shape the engine has. The interval advances a SCRIPTED clock —
|
|
632
668
|
* the kit's rule against timers is about stopping a turn on a guess, which
|
|
633
|
-
* this never does
|
|
669
|
+
* this never does.
|
|
634
670
|
*/
|
|
635
671
|
export async function assertStallFailsTheTurn(harness: RuntimeContractHarness, label = "rt-stall"): Promise<RuntimeArmResult> {
|
|
636
672
|
const { subject, clock } = harness;
|
|
@@ -692,7 +728,7 @@ export async function assertCostCapTerminates(harness: RuntimeContractHarness, l
|
|
|
692
728
|
/**
|
|
693
729
|
* A platform STOP answered to a mid-turn persist: the turn completes early
|
|
694
730
|
* with the platform-stop row and no error, and the step after the STOP never
|
|
695
|
-
* runs — the awaited `requestPersist` at work
|
|
731
|
+
* runs — the awaited `requestPersist` at work. The persist the
|
|
696
732
|
* control plane answers is the one carrying a completed `read`: the discrete
|
|
697
733
|
* event every harness flushes on (the Cursor golden's lever), where a bare
|
|
698
734
|
* assistant message may ride an engine's cadence to the settle persist, too
|
|
@@ -786,7 +822,7 @@ export async function assertFailedSurfaceWritesItsCopy(harness: RuntimeContractH
|
|
|
786
822
|
|
|
787
823
|
/**
|
|
788
824
|
* A rejected `bindHarnessState` (the session write refused) ends the turn
|
|
789
|
-
* FAILED on the internal surface with nothing executed
|
|
825
|
+
* FAILED on the internal surface with nothing executed.
|
|
790
826
|
* Engine-minted harnesses only: a deterministic one never binds.
|
|
791
827
|
*/
|
|
792
828
|
export async function assertRejectedBindFails(harness: RuntimeContractHarness, label = "rt-bind-rejects"): Promise<RuntimeArmResult> {
|
|
@@ -884,7 +920,7 @@ export interface RuntimeContractOptions {
|
|
|
884
920
|
* to wait for (`scenario.cancelled()`, an SDK-side cancel); the arm is
|
|
885
921
|
* registered SKIPPED otherwise. On by default; the native subject turns it
|
|
886
922
|
* off (no LangGraph path ends a turn `cancelled` — a stop is the runtime's
|
|
887
|
-
* and settles `interrupted
|
|
923
|
+
* and settles `interrupted`).
|
|
888
924
|
*/
|
|
889
925
|
readonly engineCancel?: boolean;
|
|
890
926
|
}
|
|
@@ -968,7 +1004,7 @@ export function describeHarnessRuntimeContract(harness: RuntimeContractHarness,
|
|
|
968
1004
|
await assertFailedSurfaceWritesItsCopy(harness, surface, FAILURE_MESSAGES[surface]);
|
|
969
1005
|
});
|
|
970
1006
|
}
|
|
971
|
-
it.skipIf(subject.adapter.capabilities.stateIdSource !== "engine-minted")("a rejected bindHarnessState ends the turn failed with nothing executed
|
|
1007
|
+
it.skipIf(subject.adapter.capabilities.stateIdSource !== "engine-minted")("a rejected bindHarnessState ends the turn failed with nothing executed", async () => {
|
|
972
1008
|
clock.reset();
|
|
973
1009
|
await assertRejectedBindFails(harness);
|
|
974
1010
|
});
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
*
|
|
23
23
|
* What is deliberately simple here and would be real work in an adapter:
|
|
24
24
|
* "executing" a side effect is incrementing a counter; the engine state id is
|
|
25
|
-
* a counter too;
|
|
26
|
-
*
|
|
25
|
+
* a counter too; there is no engine event to translate, so each step emits
|
|
26
|
+
* the canonical `TranscriptEvent`s a translator would (a run for a `say`, a
|
|
27
|
+
* start and a finish for a tool, a proposal for a gate) straight into
|
|
28
|
+
* `sink.transcript` — the one way any harness creates a transcript row, and
|
|
29
|
+
* the whole of what a real adapter's translator adds on top of this file.
|
|
30
|
+
* What is NOT simplified is the contract behaviour itself —
|
|
27
31
|
* settling with an outcome and never rejecting, stopping at every step
|
|
28
32
|
* boundary and inside a hang, binding before the first persist, executing an
|
|
29
33
|
* approval exactly once, refusing to resume a state it never minted — because
|
|
@@ -34,8 +38,8 @@
|
|
|
34
38
|
* is on the row (the server's field) and the transition follows from it with
|
|
35
39
|
* no engine knowledge, so it belongs to the runtime — one writer per field,
|
|
36
40
|
* as `approvalDecisionsOf` is the runtime's one reader and
|
|
37
|
-
* `terminalizeNonExecutingDecisions` its one writer (
|
|
38
|
-
*
|
|
41
|
+
* `terminalizeNonExecutingDecisions` its one writer (landed in #1096,
|
|
42
|
+
* `harness/approval-decisions.ts`). An adapter's whole duty for a
|
|
39
43
|
* non-executing decision is to not execute.
|
|
40
44
|
*
|
|
41
45
|
* Scenarios are arranged PER SESSION (the engine is per session in every real
|
|
@@ -57,7 +61,7 @@ import type { HarnessAdapter, TurnInput, TurnOutcome, TurnSink } from "../../har
|
|
|
57
61
|
import { DEEP_AGENT_VISION_PROFILE } from "../../shared/attachment-vision.js";
|
|
58
62
|
import type { ProposedAction } from "../approval-contract/types.js";
|
|
59
63
|
import { testConfig } from "../config-fixture.js";
|
|
60
|
-
import {
|
|
64
|
+
import { findToolCallRow } from "../proto-helpers.js";
|
|
61
65
|
import type { EngineView, HarnessContractSubject, ScenarioStep, TurnScenario } from "./types.js";
|
|
62
66
|
|
|
63
67
|
export interface ScriptedHarnessOptions {
|
|
@@ -101,6 +105,8 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
101
105
|
private readonly executions = new Map<string, number>();
|
|
102
106
|
private readonly hangWaiters: Array<() => void> = [];
|
|
103
107
|
private mintCounter = 0;
|
|
108
|
+
/** One `runId` per `say`, as an engine mints one per model run; the builder keys a message on it. */
|
|
109
|
+
private runCounter = 0;
|
|
104
110
|
|
|
105
111
|
constructor(options: ScriptedHarnessOptions) {
|
|
106
112
|
this.name = options.name ?? `scripted(${options.pausePrimitive}, ${options.stateIdSource})`;
|
|
@@ -211,7 +217,12 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
211
217
|
private async play(step: ScenarioStep, input: TurnInput, sink: TurnSink): Promise<TurnOutcome | undefined> {
|
|
212
218
|
switch (step.kind) {
|
|
213
219
|
case "say": {
|
|
214
|
-
|
|
220
|
+
// One model run: start, its text, finish — the shape every
|
|
221
|
+
// translator emits for an assistant message.
|
|
222
|
+
const runId = `run-${++this.runCounter}`;
|
|
223
|
+
sink.transcript.apply({ kind: "message_start", runId });
|
|
224
|
+
sink.transcript.apply({ kind: "text_delta", runId, text: step.text });
|
|
225
|
+
sink.transcript.apply({ kind: "message_finish", runId });
|
|
215
226
|
sink.recordActivity();
|
|
216
227
|
// Awaited, as the Cursor loop awaits its own: a platform STOP the
|
|
217
228
|
// runtime reads from this write aborts the signal before the next
|
|
@@ -223,14 +234,12 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
223
234
|
if (step.flows) return this.flowingWrite(step.toolCallId, step.action, input, sink);
|
|
224
235
|
return this.propose(step.toolCallId, step.action, input, sink);
|
|
225
236
|
case "read": {
|
|
226
|
-
// Ungated: the row
|
|
227
|
-
// and the persist is awaited like `say`'s — a real engine's tool
|
|
228
|
-
// is the discrete event its loop flushes on.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
message.toolCalls.push(row);
|
|
233
|
-
sink.status.messages.push(message);
|
|
237
|
+
// Ungated: the row starts and finishes at once, the effect counts as
|
|
238
|
+
// run, and the persist is awaited like `say`'s — a real engine's tool
|
|
239
|
+
// call is the discrete event its loop flushes on. The builder puts
|
|
240
|
+
// the row on the message whose text preceded it.
|
|
241
|
+
sink.transcript.apply({ kind: "tool_started", callId: step.toolCallId, name: "read", input: { path: step.path }, mcpServerSlug: "" });
|
|
242
|
+
sink.transcript.apply({ kind: "tool_finished", callId: step.toolCallId, result: `contents of ${step.path}` });
|
|
234
243
|
this.executions.set(step.toolCallId, this.executionCount(step.toolCallId) + 1);
|
|
235
244
|
sink.recordActivity("read");
|
|
236
245
|
await sink.requestPersist();
|
|
@@ -279,12 +288,18 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
279
288
|
switch (decision) {
|
|
280
289
|
case ApprovalAction.UNSPECIFIED: {
|
|
281
290
|
// Propose: surface the gated call as a WAITING row and stop the turn.
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
// `approval_proposed` is an upsert by id — a resumed turn that
|
|
292
|
+
// re-proposes reopens the seeded row, never writes a second one. No
|
|
293
|
+
// `args` on purpose: a `write` carrying whole-file content would make
|
|
294
|
+
// the runtime's exact-apply eligible on APPROVE, and this fake's
|
|
295
|
+
// execution count would then measure a path the engine never ran.
|
|
296
|
+
sink.transcript.apply({
|
|
297
|
+
kind: "approval_proposed",
|
|
298
|
+
callId: toolCallId,
|
|
299
|
+
name: action.kind,
|
|
300
|
+
mcpServerSlug: "",
|
|
301
|
+
message: `Approve ${action.kind} ${action.resource}?`,
|
|
302
|
+
});
|
|
288
303
|
sink.recordActivity();
|
|
289
304
|
sink.requestPersist();
|
|
290
305
|
return { kind: "awaiting_approval" };
|
|
@@ -292,7 +307,7 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
292
307
|
case ApprovalAction.APPROVE:
|
|
293
308
|
case ApprovalAction.APPROVE_ALL: {
|
|
294
309
|
this.executions.set(toolCallId, this.executionCount(toolCallId) + 1);
|
|
295
|
-
this.completeRow(
|
|
310
|
+
this.completeRow(toolCallId, action, sink);
|
|
296
311
|
return undefined;
|
|
297
312
|
}
|
|
298
313
|
case ApprovalAction.SKIP:
|
|
@@ -321,11 +336,8 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
321
336
|
const file = join(input.workspace.primaryDir, action.resource);
|
|
322
337
|
await mkdir(dirname(file), { recursive: true });
|
|
323
338
|
await writeFile(file, `${toolCallId}\n`);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
row.args = { path: action.resource };
|
|
327
|
-
message.toolCalls.push(row);
|
|
328
|
-
sink.status.messages.push(message);
|
|
339
|
+
sink.transcript.apply({ kind: "tool_started", callId: toolCallId, name: "write", input: { path: action.resource }, mcpServerSlug: "" });
|
|
340
|
+
sink.transcript.apply({ kind: "tool_finished", callId: toolCallId, result: "" });
|
|
329
341
|
this.executions.set(toolCallId, this.executionCount(toolCallId) + 1);
|
|
330
342
|
sink.recordActivity("write");
|
|
331
343
|
await sink.requestPersist();
|
|
@@ -333,22 +345,14 @@ export class ScriptedHarnessAdapter implements HarnessAdapter {
|
|
|
333
345
|
}
|
|
334
346
|
|
|
335
347
|
/**
|
|
336
|
-
* Report the executed action on its row, as a real engine's
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* losing the fact.
|
|
348
|
+
* Report the executed action on its row, as a real engine's re-issue does:
|
|
349
|
+
* a start (which flips the seeded WAITING row to RUNNING — or creates the
|
|
350
|
+
* row when a runtime decided out of band and no row exists, so the fact is
|
|
351
|
+
* recorded rather than lost) and a finish.
|
|
341
352
|
*/
|
|
342
|
-
private completeRow(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
} else {
|
|
346
|
-
const message = aiMessage("");
|
|
347
|
-
const row = waitingToolCall(toolCallId, action.kind, "");
|
|
348
|
-
row.status = ToolCallStatus.TOOL_CALL_COMPLETED;
|
|
349
|
-
message.toolCalls.push(row);
|
|
350
|
-
sink.status.messages.push(message);
|
|
351
|
-
}
|
|
353
|
+
private completeRow(toolCallId: string, action: ProposedAction, sink: TurnSink): void {
|
|
354
|
+
sink.transcript.apply({ kind: "tool_started", callId: toolCallId, name: action.kind, input: {}, mcpServerSlug: "" });
|
|
355
|
+
sink.transcript.apply({ kind: "tool_finished", callId: toolCallId, result: "" });
|
|
352
356
|
sink.recordActivity();
|
|
353
357
|
sink.requestPersist();
|
|
354
358
|
}
|
|
@@ -360,8 +364,8 @@ export interface ScriptedSubject extends HarnessContractSubject {
|
|
|
360
364
|
}
|
|
361
365
|
|
|
362
366
|
/**
|
|
363
|
-
* The fake declares the `deep-agent` row: in
|
|
364
|
-
* through this fake alone; since
|
|
367
|
+
* The fake declares the `deep-agent` row: in #1070 the runtime served that row
|
|
368
|
+
* through this fake alone; since #1096 the real native adapter takes it over
|
|
365
369
|
* in the runtime half
|
|
366
370
|
* (`execute-deep-agent/__test-utils__/contract-subject.ts`), and the fake
|
|
367
371
|
* keeps declaring it so the kit's self-check runs a full subject under both
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* and driven by the framework's own activity environment. Both harnesses now
|
|
15
15
|
* run on it (`execute-cursor/__test-utils__/hermetic-cursor.ts`,
|
|
16
16
|
* `execute-deep-agent/__test-utils__/hermetic-deep-agent.ts`), and the earlier
|
|
17
|
-
* native tests were re-homed onto the native driver (
|
|
17
|
+
* native tests were re-homed onto the native driver (#1096), their assertions
|
|
18
18
|
* carried into `execute-deep-agent/__tests__/hermetic/`.
|
|
19
19
|
*
|
|
20
20
|
* What is generic here and what is not: everything an activity touches that is
|
|
@@ -55,11 +55,22 @@
|
|
|
55
55
|
* Determinism: the clock is faked for `Date` ONLY (`vi.useFakeTimers({ toFake:
|
|
56
56
|
* ["Date"] })`) and TICKS — the harness double advances it a fixed quantum per
|
|
57
57
|
* script step through {@link ScriptedClock}. Frozen time would make
|
|
58
|
-
*
|
|
58
|
+
* the streaming scheduler's cadence (`Date.now()` against its last send) never
|
|
59
59
|
* elapse and silently skip the mid-stream persist path production always runs;
|
|
60
60
|
* a ticking clock runs the same path and lands on the same instants every run.
|
|
61
61
|
* Timers stay real so the periodic heartbeat and the stall watchdog behave.
|
|
62
62
|
*
|
|
63
|
+
* The live posture (#1097's live-run gate): a `CURSOR_API_KEY`-gated live
|
|
64
|
+
* instrument keeps all three substitutions above — the real `Context`, the
|
|
65
|
+
* record behind the client, the temp environment — and drops only the
|
|
66
|
+
* harness's SDK double, so the REAL activity runs against the REAL vendor SDK
|
|
67
|
+
* with everything the activity persists still readable from the record.
|
|
68
|
+
* Two things differ from a hermetic run and both are the caller's explicit
|
|
69
|
+
* choice: no `ScriptedClock` is installed (the real SDK is not ticked, and a
|
|
70
|
+
* frozen clock would skip the persist cadence production runs), and the
|
|
71
|
+
* environment may name an `eventRecordingDir` so the run leaves a recording.
|
|
72
|
+
* The harness's driver names the entry (`beginLiveCursorScenario`).
|
|
73
|
+
*
|
|
63
74
|
* Not in scope: this module never edits a production module and never
|
|
64
75
|
* normalizes output. If a golden is not byte-stable, the volatile source is
|
|
65
76
|
* controlled at its origin or the surprise is escalated — never redacted here.
|
|
@@ -151,6 +162,8 @@ export class ExecutionRecord {
|
|
|
151
162
|
private readonly controlSignal: (status: AgentExecutionStatus) => ExecutionControlSignal;
|
|
152
163
|
/** Every `updateStatus` payload, in order, snapshotted at write time. */
|
|
153
164
|
readonly persisted: AgentExecutionStatus[] = [];
|
|
165
|
+
/** Fired after every full-status `applyStatusUpdate`; {@link whenToolCallsSettled} subscribes here. */
|
|
166
|
+
private readonly persistListeners = new Set<() => void>();
|
|
154
167
|
/** Setup-progress labels reported before the stream started, in order. */
|
|
155
168
|
readonly setupProgress: string[] = [];
|
|
156
169
|
/**
|
|
@@ -205,6 +218,78 @@ export class ExecutionRecord {
|
|
|
205
218
|
return this.execution.status?.messages.flatMap((m) => m.toolCalls) ?? [];
|
|
206
219
|
}
|
|
207
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Resolves once every one of `ids` is a SETTLED row (not RUNNING) in a status
|
|
223
|
+
* the activity has persisted — root messages and every sub-agent's alike.
|
|
224
|
+
* Resolves at once for an empty list or when they already are.
|
|
225
|
+
*
|
|
226
|
+
* This is the barrier a real model gives the runtime for free and a
|
|
227
|
+
* scripted one does not: a model that takes 300 ms to answer never starts
|
|
228
|
+
* its next turn before the loop has folded and persisted the tool results
|
|
229
|
+
* it is answering, while `ScriptedModel` answers instantly and LangGraph's
|
|
230
|
+
* producer can run a whole turn ahead of the consumer while the consumer
|
|
231
|
+
* awaits one mid-stream persist (on a git workspace that persist shells out
|
|
232
|
+
* for `file_change_progress`). The stamps the consumer writes at apply time
|
|
233
|
+
* then land on either side of the scripted clock's next tick depending on
|
|
234
|
+
* I/O timing — a golden that flips between `:01` and `:02` on the day. The
|
|
235
|
+
* driver awaits this before it ticks (`hermetic-deep-agent.ts`), so every
|
|
236
|
+
* stamp of turn N precedes turn N+1's instant, run after run.
|
|
237
|
+
*
|
|
238
|
+
* Why "settled" and not "seen": the row's `completedAt` is stamped when the
|
|
239
|
+
* tool's end event is applied, and the producer calls the next model turn
|
|
240
|
+
* right after emitting that event, so a barrier on the row merely existing
|
|
241
|
+
* would still race the completion stamp. The persist that carries the
|
|
242
|
+
* settled row always exists: a tool's finish forces one (the builder's
|
|
243
|
+
* force flag), and a turn followed by another turn always has a tool call.
|
|
244
|
+
*
|
|
245
|
+
* Bounded by real time (timers are real under the scripted clock): a
|
|
246
|
+
* scenario that breaks the fact above — a persist that never carries the
|
|
247
|
+
* row — fails naming the ids still open, never hangs.
|
|
248
|
+
*/
|
|
249
|
+
whenToolCallsSettled(ids: readonly string[], timeoutMs = 10_000): Promise<void> {
|
|
250
|
+
const unsettled = (): string[] => {
|
|
251
|
+
const settled = this.settledToolCallIds();
|
|
252
|
+
return ids.filter((id) => !settled.has(id));
|
|
253
|
+
};
|
|
254
|
+
if (unsettled().length === 0) return Promise.resolve();
|
|
255
|
+
|
|
256
|
+
return new Promise<void>((resolve, reject) => {
|
|
257
|
+
const timer = setTimeout(() => {
|
|
258
|
+
this.persistListeners.delete(check);
|
|
259
|
+
reject(
|
|
260
|
+
new Error(
|
|
261
|
+
`ExecutionRecord: tool call(s) ${unsettled().join(", ")} never settled in a persisted status ` +
|
|
262
|
+
`within ${timeoutMs} ms (${this.persisted.length} persists seen). A model turn was asked to ` +
|
|
263
|
+
`wait for the results it answers; the loop never persisted them — a scenario or runtime bug.`,
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
}, timeoutMs);
|
|
267
|
+
const check = (): void => {
|
|
268
|
+
if (unsettled().length > 0) return;
|
|
269
|
+
clearTimeout(timer);
|
|
270
|
+
this.persistListeners.delete(check);
|
|
271
|
+
resolve();
|
|
272
|
+
};
|
|
273
|
+
this.persistListeners.add(check);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** The ids of every row not RUNNING in the held status, across the root and every sub-agent transcript. */
|
|
278
|
+
private settledToolCallIds(): Set<string> {
|
|
279
|
+
const out = new Set<string>();
|
|
280
|
+
const status = this.execution.status;
|
|
281
|
+
if (!status) return out;
|
|
282
|
+
const transcripts = [status.messages, ...status.subAgentExecutions.map((s) => s.messages)];
|
|
283
|
+
for (const messages of transcripts) {
|
|
284
|
+
for (const message of messages) {
|
|
285
|
+
for (const tc of message.toolCalls) {
|
|
286
|
+
if (tc.status !== ToolCallStatus.TOOL_CALL_RUNNING) out.add(tc.id);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
292
|
+
|
|
208
293
|
/** The tool calls currently paused for a decision. */
|
|
209
294
|
waitingToolCalls(): ToolCall[] {
|
|
210
295
|
return this.toolCalls().filter((tc) => tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
|
|
@@ -254,6 +339,7 @@ export class ExecutionRecord {
|
|
|
254
339
|
return ExecutionControlSignal.UNSPECIFIED;
|
|
255
340
|
}
|
|
256
341
|
this.execution.status = clone(AgentExecutionStatusSchema, snapshot);
|
|
342
|
+
for (const listener of [...this.persistListeners]) listener();
|
|
257
343
|
return this.controlSignal(snapshot);
|
|
258
344
|
}
|
|
259
345
|
|
|
@@ -343,14 +429,30 @@ export function hermeticStigmerClientModule(): { StigmerClient: new () => Stigme
|
|
|
343
429
|
* (an absent store flips capture off — a different code path).
|
|
344
430
|
* - `STIGMER_RUNNER_HITL_SECRET`: the one randomness source on the activity
|
|
345
431
|
* path, pinned so grant tokens and fingerprints are byte-stable.
|
|
346
|
-
* - `CURSOR_EVENT_RECORD_DIR` cleared: never write recordings from a test
|
|
432
|
+
* - `CURSOR_EVENT_RECORD_DIR` cleared: never write recordings from a test —
|
|
433
|
+
* unless the caller names an `eventRecordingDir`, the live instruments'
|
|
434
|
+
* opt-in (below), in which case this environment SETS it. Either way the
|
|
435
|
+
* variable has one owner here, and dispose restores it.
|
|
347
436
|
*/
|
|
348
437
|
const PINNED_ENV = {
|
|
349
438
|
ARTIFACT_STORAGE_TYPE: "local",
|
|
350
439
|
STIGMER_RUNNER_HITL_SECRET: "hermetic-fixture-secret-do-not-use-in-production",
|
|
351
440
|
} as const;
|
|
352
441
|
|
|
353
|
-
const CLEARED_ENV = ["
|
|
442
|
+
const CLEARED_ENV = ["STIGMER_PROXY_ENDPOINT", "STIGMER_CLOUD_API_URL"] as const;
|
|
443
|
+
|
|
444
|
+
/** The one name the Cursor recorder is switched on by (`turn-settle.ts` reads it). */
|
|
445
|
+
const CURSOR_EVENT_RECORD_DIR = "CURSOR_EVENT_RECORD_DIR";
|
|
446
|
+
|
|
447
|
+
export interface HermeticEnvironmentOptions {
|
|
448
|
+
/**
|
|
449
|
+
* Where the Cursor event recorder writes this run's raw SDK recording
|
|
450
|
+
* (`<dir>/<executionId>.cursor-events.jsonl`). Named ONLY by a live
|
|
451
|
+
* instrument, whose point is a recording of the real SDK; a hermetic
|
|
452
|
+
* scenario leaves it unset and the variable is cleared for the run.
|
|
453
|
+
*/
|
|
454
|
+
readonly eventRecordingDir?: string;
|
|
455
|
+
}
|
|
354
456
|
|
|
355
457
|
export interface HermeticEnvironment {
|
|
356
458
|
/** The temp `HOME` (the runner's `~/.stigmer` lands under it). */
|
|
@@ -368,7 +470,7 @@ export interface HermeticEnvironment {
|
|
|
368
470
|
* `beforeAll`) — the fingerprint secret is memoized per process on first use,
|
|
369
471
|
* and vitest isolates files in forks, so per-file is the natural unit.
|
|
370
472
|
*/
|
|
371
|
-
export function createHermeticEnvironment(): HermeticEnvironment {
|
|
473
|
+
export function createHermeticEnvironment(options: HermeticEnvironmentOptions = {}): HermeticEnvironment {
|
|
372
474
|
const root = mkdtempSync(join(tmpdir(), "stigmer-hermetic-"));
|
|
373
475
|
const home = join(root, "home");
|
|
374
476
|
const workspaceRootDir = join(root, "workspaces");
|
|
@@ -384,6 +486,7 @@ export function createHermeticEnvironment(): HermeticEnvironment {
|
|
|
384
486
|
set("LOCAL_ARTIFACT_PATH", artifactPath);
|
|
385
487
|
for (const [k, v] of Object.entries(PINNED_ENV)) set(k, v);
|
|
386
488
|
for (const k of CLEARED_ENV) set(k, undefined);
|
|
489
|
+
set(CURSOR_EVENT_RECORD_DIR, options.eventRecordingDir);
|
|
387
490
|
|
|
388
491
|
let disposed = false;
|
|
389
492
|
return {
|
|
@@ -411,15 +514,16 @@ export function createHermeticEnvironment(): HermeticEnvironment {
|
|
|
411
514
|
* advance together; timers stay real (the periodic heartbeat, the stall
|
|
412
515
|
* watchdog, `withTimeout` all need them). The harness double calls
|
|
413
516
|
* {@link ScriptedClock.tick} once per script step, so every clock-based
|
|
414
|
-
* decision on the activity path — status timestamps, the
|
|
415
|
-
*
|
|
517
|
+
* decision on the activity path — status timestamps, the builder's row
|
|
518
|
+
* instants, cache TTLs (`Date`); the streaming scheduler's persist cadence
|
|
416
519
|
* (`performance.now()`) — sees the same instants run after run and runs the
|
|
417
520
|
* same branches production runs.
|
|
418
521
|
*
|
|
419
|
-
* Why both (
|
|
522
|
+
* Why both (since #1096): until then only `Date` was faked, so the
|
|
420
523
|
* scheduler paced persists on REAL elapsed time and `fileChangeProgress`,
|
|
421
524
|
* which rides persists, could gain or lose a capture under load — a golden
|
|
422
|
-
* flake seen twice
|
|
525
|
+
* flake seen twice while the native adapter moved onto the runtime. One clock,
|
|
526
|
+
* two faces: `tick` advances through
|
|
423
527
|
* the fake clock's own `tick` (`vi.advanceTimersByTime`), which moves both
|
|
424
528
|
* `Date` and `performance.now()`; `vi.setSystemTime` would move `Date` alone
|
|
425
529
|
* (fake-timers keeps `performance` monotonic across a system-time jump), so
|
|
@@ -23,6 +23,15 @@
|
|
|
23
23
|
* never consulted and adding an entry is byte-invisible to the other
|
|
24
24
|
* harness's goldens (verified when the native entry landed: Cursor's
|
|
25
25
|
* seventeen unchanged).
|
|
26
|
+
*
|
|
27
|
+
* The live posture (`stubRegistryFetch({ live: true })`): a live instrument
|
|
28
|
+
* runs the real activity against the REAL `@cursor/sdk`, whose cloud-api
|
|
29
|
+
* client (`Cursor.models.list`, the user-key token exchange) goes through
|
|
30
|
+
* global `fetch` — only its Connect RPC stream rides connect-node. Such a run
|
|
31
|
+
* keeps this document for the registry (pricing and tier stay pinned; nothing
|
|
32
|
+
* a live turn proves lives there) and lets every other URL reach the network
|
|
33
|
+
* through the `fetch` that was global before the stub. The refusal stays the
|
|
34
|
+
* default: a hermetic run that grows a network dependency must still fail.
|
|
26
35
|
*/
|
|
27
36
|
|
|
28
37
|
import { vi } from "vitest";
|
|
@@ -76,21 +85,36 @@ export const REGISTRY_DOCUMENT = {
|
|
|
76
85
|
],
|
|
77
86
|
} as const;
|
|
78
87
|
|
|
88
|
+
export interface StubRegistryFetchOptions {
|
|
89
|
+
/**
|
|
90
|
+
* The live posture (header): every URL that is not the registry reaches the
|
|
91
|
+
* network through the pre-stub `fetch`. Default false — refuse, and fail the
|
|
92
|
+
* run.
|
|
93
|
+
*/
|
|
94
|
+
readonly live?: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
79
97
|
/**
|
|
80
|
-
* Stub `fetch` to answer the registry document and
|
|
81
|
-
* Returns the URLs fetched, for the "no other network"
|
|
98
|
+
* Stub `fetch` to answer the registry document and — by default — refuse
|
|
99
|
+
* everything else. Returns the URLs fetched, for the "no other network"
|
|
100
|
+
* assertion (and, in the live posture, for the record of what a real turn
|
|
101
|
+
* dialled).
|
|
82
102
|
*/
|
|
83
|
-
export function stubRegistryFetch(): { readonly urls: string[]; restore(): void } {
|
|
103
|
+
export function stubRegistryFetch(options: StubRegistryFetchOptions = {}): { readonly urls: string[]; restore(): void } {
|
|
84
104
|
const urls: string[] = [];
|
|
105
|
+
const networkFetch = globalThis.fetch;
|
|
85
106
|
vi.stubGlobal(
|
|
86
107
|
"fetch",
|
|
87
|
-
vi.fn(async (input: string | URL | Request) => {
|
|
108
|
+
vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
|
|
88
109
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
89
110
|
urls.push(url);
|
|
90
|
-
if (
|
|
91
|
-
|
|
111
|
+
if (url.includes("/model-registry")) {
|
|
112
|
+
return { ok: true, status: 200, json: async () => REGISTRY_DOCUMENT } as unknown as Response;
|
|
113
|
+
}
|
|
114
|
+
if (options.live) {
|
|
115
|
+
return networkFetch(input, init);
|
|
92
116
|
}
|
|
93
|
-
|
|
117
|
+
throw new Error(`hermetic run attempted a non-registry network call: ${url}`);
|
|
94
118
|
}),
|
|
95
119
|
);
|
|
96
120
|
return { urls, restore: () => vi.unstubAllGlobals() };
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
* The module specifiers a TypeScript file names, read off its syntax tree —
|
|
3
3
|
* the primitive behind the runner's import fences.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Three fences stand on it: `src/harness/` and production `src/shared/` never
|
|
6
6
|
* import `src/activities/` (`harness/__tests__/import-direction.test.ts`,
|
|
7
|
-
* the rule the turn runtime is written under)
|
|
7
|
+
* the rule the turn runtime is written under); the Cursor adapter never
|
|
8
8
|
* imports `@temporalio/*` (`execute-cursor/__tests__/adapter-is-temporal-
|
|
9
|
-
* free.test.ts`, what lets the contract kit run it outside an activity)
|
|
9
|
+
* free.test.ts`, what lets the contract kit run it outside an activity); and
|
|
10
|
+
* `src/harness/transcript/` names no package outside a short allow-list
|
|
11
|
+
* (`harness/__tests__/transcript-is-engine-free.test.ts`, what keeps the one
|
|
12
|
+
* transcript builder engine-neutral).
|
|
10
13
|
*
|
|
11
14
|
* A syntax tree, not a regex, so a path quoted in a header comment cannot
|
|
12
15
|
* trip a fence, and no import form can slip past one: static, type-only,
|
|
@@ -84,6 +87,32 @@ export function packageImports(filePath: string, source: string, pkg: string): s
|
|
|
84
87
|
return moduleSpecifiers(filePath, source).filter((specifier) => specifier === pkg || specifier.startsWith(`${pkg}/`));
|
|
85
88
|
}
|
|
86
89
|
|
|
90
|
+
/**
|
|
91
|
+
* The packages `source` names — every specifier that is neither a relative
|
|
92
|
+
* path nor a `node:` builtin, reduced to its package (`@scope/name` or
|
|
93
|
+
* `name`, the subpath dropped), deduplicated, in first-appearance order.
|
|
94
|
+
*
|
|
95
|
+
* What an ALLOW-list fence compares against, where `packageImports` serves a
|
|
96
|
+
* deny: a deny-list must be extended for every SDK that does not exist yet,
|
|
97
|
+
* an allow-list refuses it on the day it is written.
|
|
98
|
+
*/
|
|
99
|
+
export function packageNames(filePath: string, source: string): string[] {
|
|
100
|
+
const names = new Set<string>();
|
|
101
|
+
for (const specifier of moduleSpecifiers(filePath, source)) {
|
|
102
|
+
if (specifier.startsWith(".") || specifier.startsWith("node:")) continue;
|
|
103
|
+
names.add(packageName(specifier));
|
|
104
|
+
}
|
|
105
|
+
return [...names];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** `@scope/name/sub/path` → `@scope/name`; `name/sub/path` → `name`. */
|
|
109
|
+
function packageName(specifier: string): string {
|
|
110
|
+
const parts = specifier.split("/");
|
|
111
|
+
const [first, second] = parts;
|
|
112
|
+
if (first === undefined) return specifier;
|
|
113
|
+
return first.startsWith("@") && second !== undefined ? `${first}/${second}` : first;
|
|
114
|
+
}
|
|
115
|
+
|
|
87
116
|
/** Every `.ts` file under `dir`, sorted, skipping the named directories at any depth. */
|
|
88
117
|
export function typeScriptFilesUnder(dir: string, skipDirectories: ReadonlySet<string>): string[] {
|
|
89
118
|
const files: string[] = [];
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `adapter.ts`'s graph, or a static client import on the registry's, fails
|
|
21
21
|
* the first arm; a `boot` that stopped asserting fails the second.
|
|
22
22
|
*
|
|
23
|
-
* The table has two rows since
|
|
23
|
+
* The table has two rows since #1096 (2026-09-13): the child boots the
|
|
24
24
|
* Cursor adapter and then the native deep-agent adapter, whose `boot`
|
|
25
25
|
* registers the deepagents profiles and loads LangChain — after the patch,
|
|
26
26
|
* because it is the second row. A `boot` that reordered the rows, or a
|