@stigmer/runner 3.14.0 → 3.15.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/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/execution-record-fixture.d.ts +47 -0
- package/dist/__test-utils__/execution-record-fixture.js +68 -0
- package/dist/__test-utils__/execution-record-fixture.js.map +1 -0
- package/dist/__test-utils__/harness-contract/types.d.ts +166 -0
- package/dist/__test-utils__/harness-contract/types.js +49 -0
- package/dist/__test-utils__/harness-contract/types.js.map +1 -0
- package/dist/__test-utils__/hermetic-activity.d.ts +268 -0
- package/dist/__test-utils__/hermetic-activity.js +385 -0
- package/dist/__test-utils__/hermetic-activity.js.map +1 -0
- package/dist/__test-utils__/mock-client.d.ts +13 -0
- package/dist/__test-utils__/mock-client.js +45 -0
- package/dist/__test-utils__/mock-client.js.map +1 -0
- package/dist/__test-utils__/mock-workspace.d.ts +15 -0
- package/dist/__test-utils__/mock-workspace.js +21 -0
- package/dist/__test-utils__/mock-workspace.js.map +1 -0
- package/dist/__test-utils__/model-registry-fixture.d.ts +56 -0
- package/dist/__test-utils__/model-registry-fixture.js +66 -0
- package/dist/__test-utils__/model-registry-fixture.js.map +1 -0
- package/dist/__test-utils__/turn-input-fixture.d.ts +60 -0
- package/dist/__test-utils__/turn-input-fixture.js +118 -0
- package/dist/__test-utils__/turn-input-fixture.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +107 -0
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +344 -0
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +20 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +58 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +2 -32
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +148 -0
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +257 -0
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +183 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +262 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +138 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +191 -0
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -0
- package/dist/activities/execute-cursor/adapter.d.ts +55 -0
- package/dist/activities/execute-cursor/adapter.js +119 -0
- package/dist/activities/execute-cursor/adapter.js.map +1 -0
- package/dist/activities/execute-cursor/approval-state.d.ts +2 -2
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +15 -30
- package/dist/activities/execute-cursor/capture-flow.js +17 -26
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
- package/dist/activities/execute-cursor/cursor-capabilities.d.ts +23 -0
- package/dist/activities/execute-cursor/cursor-capabilities.js +33 -0
- package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +2 -2
- package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -1
- package/dist/activities/execute-cursor/fetch-interceptor.d.ts +15 -25
- package/dist/activities/execute-cursor/fetch-interceptor.js +39 -33
- package/dist/activities/execute-cursor/fetch-interceptor.js.map +1 -1
- package/dist/activities/execute-cursor/http2-interceptor.d.ts +21 -17
- package/dist/activities/execute-cursor/http2-interceptor.js +38 -31
- package/dist/activities/execute-cursor/http2-interceptor.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +28 -19
- package/dist/activities/execute-cursor/message-translator.js +76 -27
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +180 -11
- package/dist/activities/execute-cursor/prompt-builder.js +171 -0
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/rejection-capture.d.ts +3 -6
- package/dist/activities/execute-cursor/rejection-capture.js +5 -7
- package/dist/activities/execute-cursor/rejection-capture.js.map +1 -1
- package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -4
- package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
- package/dist/activities/execute-cursor/turn-boundary.d.ts +1 -1
- package/dist/activities/execute-cursor/turn-settle.d.ts +49 -0
- package/dist/activities/execute-cursor/turn-settle.js +507 -0
- package/dist/activities/execute-cursor/turn-settle.js.map +1 -0
- package/dist/activities/execute-cursor/turn-setup.d.ts +173 -0
- package/dist/activities/execute-cursor/turn-setup.js +496 -0
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -0
- package/dist/activities/execute-cursor/turn-stream.d.ts +41 -71
- package/dist/activities/execute-cursor/turn-stream.js +70 -135
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-cursor/turn.d.ts +43 -0
- package/dist/activities/execute-cursor/turn.js +222 -0
- package/dist/activities/execute-cursor/turn.js.map +1 -0
- package/dist/activities/execute-cursor/usage-pricing.d.ts +38 -0
- package/dist/activities/execute-cursor/usage-pricing.js +50 -0
- package/dist/activities/execute-cursor/usage-pricing.js.map +1 -0
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +1 -1
- package/dist/activities/execute-deep-agent/approval-file-change.js +1 -1
- package/dist/activities/execute-deep-agent/environment.js +1 -1
- package/dist/activities/execute-deep-agent/environment.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +3 -2
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/hydrate-workflow-execution.js +1 -1
- package/dist/activities/hydrate-workflow-execution.js.map +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.js.map +1 -1
- package/dist/harness/capabilities.d.ts +87 -0
- package/dist/harness/capabilities.js +37 -0
- package/dist/harness/capabilities.js.map +1 -0
- package/dist/harness/persist-chokepoint.d.ts +74 -0
- package/dist/harness/persist-chokepoint.js +88 -0
- package/dist/harness/persist-chokepoint.js.map +1 -0
- package/dist/harness/registry.d.ts +106 -0
- package/dist/harness/registry.js +168 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/run-turn.d.ts +53 -0
- package/dist/harness/run-turn.js +528 -0
- package/dist/harness/run-turn.js.map +1 -0
- package/dist/harness/stop-controller.d.ts +50 -0
- package/dist/harness/stop-controller.js +62 -0
- package/dist/harness/stop-controller.js.map +1 -0
- package/dist/harness/terminal-table.d.ts +141 -0
- package/dist/harness/terminal-table.js +239 -0
- package/dist/harness/terminal-table.js.map +1 -0
- package/dist/harness/turn-context.d.ts +523 -0
- package/dist/harness/turn-context.js +739 -0
- package/dist/harness/turn-context.js.map +1 -0
- package/dist/harness/types.d.ts +448 -0
- package/dist/harness/types.js +64 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/usage-accumulator.d.ts +68 -0
- package/dist/harness/usage-accumulator.js +101 -0
- package/dist/harness/usage-accumulator.js.map +1 -0
- package/dist/harness-adapters.d.ts +27 -0
- package/dist/harness-adapters.js +28 -0
- package/dist/harness-adapters.js.map +1 -0
- package/dist/main.js.map +1 -1
- package/dist/runner-manager.js +64 -48
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.d.ts +5 -4
- package/dist/runner.js +65 -64
- package/dist/runner.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +1 -1
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/{activities/execute-cursor → shared}/attachment-resolver.d.ts +2 -2
- package/dist/{activities/execute-cursor → shared}/attachment-resolver.js +5 -5
- package/dist/shared/attachment-resolver.js.map +1 -0
- package/dist/{activities/execute-cursor → shared}/blueprint-resolver.d.ts +7 -5
- package/dist/{activities/execute-cursor → shared}/blueprint-resolver.js +3 -63
- package/dist/shared/blueprint-resolver.js.map +1 -0
- package/dist/shared/channel-attachment.d.ts +1 -1
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/{activities/execute-cursor → shared}/cost-guard.d.ts +5 -1
- package/dist/{activities/execute-cursor → shared}/cost-guard.js +5 -1
- package/dist/shared/cost-guard.js.map +1 -0
- package/dist/{activities/execute-cursor → shared}/env-resolver.d.ts +1 -1
- package/dist/shared/env-resolver.js.map +1 -0
- package/dist/{activities/execute-cursor → shared}/exact-apply.d.ts +1 -1
- package/dist/{activities/execute-cursor → shared}/exact-apply.js +6 -6
- package/dist/shared/exact-apply.js.map +1 -0
- package/dist/shared/execution-context.d.ts +35 -0
- package/dist/shared/execution-context.js +38 -0
- package/dist/shared/execution-context.js.map +1 -0
- package/dist/{activities/execute-cursor → shared}/extract-structured-output.d.ts +5 -2
- package/dist/{activities/execute-cursor → shared}/extract-structured-output.js +9 -6
- package/dist/shared/extract-structured-output.js.map +1 -0
- package/dist/shared/filereview/capture.d.ts +1 -1
- package/dist/shared/filereview/capture.js +1 -1
- package/dist/shared/heartbeat.d.ts +13 -11
- package/dist/shared/heartbeat.js +14 -29
- package/dist/shared/heartbeat.js.map +1 -1
- package/dist/{activities/execute-cursor → shared}/persist-decision.d.ts +5 -2
- package/dist/{activities/execute-cursor → shared}/persist-decision.js +4 -1
- package/dist/shared/persist-decision.js.map +1 -0
- package/dist/{activities/execute-cursor → shared}/skill-resolver.d.ts +16 -3
- package/dist/{activities/execute-cursor → shared}/skill-resolver.js +12 -4
- package/dist/shared/skill-resolver.js.map +1 -0
- package/dist/shared/subagent-rows.d.ts +21 -0
- package/dist/shared/subagent-rows.js +34 -0
- package/dist/shared/subagent-rows.js.map +1 -0
- package/dist/shared/worker-shutdown.d.ts +43 -16
- package/dist/shared/worker-shutdown.js +28 -13
- package/dist/shared/worker-shutdown.js.map +1 -1
- package/dist/shared/workspace/platform-dir.d.ts +1 -1
- package/dist/shared/workspace/platform-dir.js +1 -1
- package/dist/shared/workspace/session-provision.d.ts +54 -0
- package/dist/{activities/execute-cursor/workspace-provision.js → shared/workspace/session-provision.js} +15 -12
- package/dist/shared/workspace/session-provision.js.map +1 -0
- package/package.json +4 -4
- package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +325 -0
- package/src/__test-utils__/config-fixture.ts +63 -0
- package/src/__test-utils__/execution-record-fixture.ts +104 -0
- package/src/__test-utils__/harness-boot-order-child.ts +58 -0
- package/src/__test-utils__/harness-contract/contract.ts +581 -0
- package/src/__test-utils__/harness-contract/recording-sink.ts +107 -0
- package/src/__test-utils__/harness-contract/runtime-contract.ts +666 -0
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +349 -0
- package/src/__test-utils__/harness-contract/types.ts +176 -0
- package/src/__test-utils__/hermetic-activity.ts +502 -0
- package/src/__test-utils__/model-registry-fixture.ts +72 -0
- package/src/__test-utils__/module-specifiers.ts +82 -0
- package/src/__test-utils__/proto-helpers.ts +25 -0
- package/src/__test-utils__/turn-input-fixture.ts +155 -0
- package/src/__tests__/harness-boot-order.test.ts +77 -0
- package/src/__tests__/harness-contract.test.ts +27 -0
- package/src/__tests__/runner-manager.test.ts +18 -0
- package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +102 -0
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +396 -0
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +66 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +2 -43
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +342 -0
- package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +368 -0
- package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +254 -0
- package/src/activities/execute-cursor/__tests__/adapter-is-temporal-free.test.ts +45 -0
- package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +90 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +7 -8
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +28 -1
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +64 -6
- package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +228 -0
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +180 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/cost-cap.status.json +53 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/deny-and-retry.turn1.status.json +55 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/deny-and-retry.turn2.status.json +77 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +126 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/pause.status.json +40 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/plain-turn.status.json +48 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/platform-stop.status.json +56 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/recovery-fresh-agent.status.json +53 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/recovery-transport-timeout.status.json +48 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-cancelled.status.json +36 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/run-error-non-retryable.status.json +37 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +38 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +59 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-call.status.json +68 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +79 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/worker-shutdown.status.json +42 -0
- package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +170 -0
- package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +205 -0
- package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +171 -0
- package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +156 -0
- package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +286 -0
- package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +332 -0
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +193 -0
- package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +137 -0
- package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +185 -0
- package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +151 -0
- package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +65 -35
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -40
- package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +171 -0
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +118 -243
- package/src/activities/execute-cursor/__tests__/usage-pricing.test.ts +84 -0
- package/src/activities/execute-cursor/adapter.ts +138 -0
- package/src/activities/execute-cursor/approval-state.ts +2 -2
- package/src/activities/execute-cursor/capture-flow.ts +18 -41
- package/src/activities/execute-cursor/cursor-capabilities.ts +35 -0
- package/src/activities/execute-cursor/cursor-mcp-config.ts +2 -2
- package/src/activities/execute-cursor/fetch-interceptor.ts +48 -45
- package/src/activities/execute-cursor/http2-interceptor.ts +47 -34
- package/src/activities/execute-cursor/message-translator.ts +90 -45
- package/src/activities/execute-cursor/prompt-builder.ts +327 -12
- package/src/activities/execute-cursor/rejection-capture.ts +5 -12
- package/src/activities/execute-cursor/session-lifecycle.ts +1 -5
- package/src/activities/execute-cursor/turn-boundary.ts +1 -1
- package/src/activities/execute-cursor/turn-settle.ts +601 -0
- package/src/activities/execute-cursor/turn-setup.ts +645 -0
- package/src/activities/execute-cursor/turn-stream.ts +84 -210
- package/src/activities/execute-cursor/turn.ts +240 -0
- package/src/activities/execute-cursor/usage-pricing.ts +67 -0
- package/src/activities/execute-deep-agent/approval-file-change.ts +1 -1
- package/src/activities/execute-deep-agent/environment.ts +1 -1
- package/src/activities/execute-deep-agent/setup.ts +3 -2
- package/src/activities/hydrate-workflow-execution.ts +1 -1
- package/src/config.ts +13 -0
- package/src/harness/__tests__/import-direction.test.ts +168 -0
- package/src/harness/__tests__/persist-chokepoint.test.ts +146 -0
- package/src/harness/__tests__/registry.test.ts +168 -0
- package/src/harness/__tests__/run-turn.test.ts +165 -0
- package/src/harness/__tests__/stop-controller.test.ts +49 -0
- package/src/harness/__tests__/turn-context.test.ts +178 -0
- package/src/harness/__tests__/usage-accumulator.test.ts +83 -0
- package/src/harness/capabilities.ts +92 -0
- package/src/harness/persist-chokepoint.ts +112 -0
- package/src/harness/registry.ts +197 -0
- package/src/harness/run-turn.ts +618 -0
- package/src/harness/stop-controller.ts +83 -0
- package/src/harness/terminal-table.ts +276 -0
- package/src/harness/turn-context.ts +1028 -0
- package/src/harness/types.ts +473 -0
- package/src/harness/usage-accumulator.ts +122 -0
- package/src/harness-adapters.ts +30 -0
- package/src/main.ts +1 -1
- package/src/runner-manager.ts +68 -61
- package/src/runner.ts +67 -78
- package/src/shared/__tests__/attachment-naming.test.ts +1 -1
- package/src/{activities/execute-cursor → shared}/__tests__/attachment-resolver.test.ts +6 -4
- package/src/{activities/execute-cursor → shared}/__tests__/cost-guard.test.ts +2 -1
- package/src/{activities/execute-cursor → shared}/__tests__/exact-apply.test.ts +3 -3
- package/src/{activities/execute-cursor → shared}/__tests__/extract-structured-output.test.ts +8 -8
- package/src/{activities/execute-cursor → shared}/__tests__/persist-decision.test.ts +4 -2
- package/src/{activities/execute-cursor → shared}/__tests__/skill-resolver.test.ts +1 -1
- package/src/shared/__tests__/subagent-rows.test.ts +34 -0
- package/src/shared/__tests__/worker-shutdown.test.ts +44 -39
- package/src/shared/approval-policy.ts +1 -1
- package/src/{activities/execute-cursor → shared}/attachment-resolver.ts +6 -6
- package/src/{activities/execute-cursor → shared}/blueprint-resolver.ts +7 -78
- package/src/shared/channel-attachment.ts +1 -1
- package/src/{activities/execute-cursor → shared}/cost-guard.ts +5 -1
- package/src/{activities/execute-cursor → shared}/env-resolver.ts +1 -1
- package/src/{activities/execute-cursor → shared}/exact-apply.ts +7 -7
- package/src/shared/execution-context.ts +45 -0
- package/src/{activities/execute-cursor → shared}/extract-structured-output.ts +10 -7
- package/src/shared/filereview/capture.ts +1 -1
- package/src/shared/heartbeat.ts +13 -33
- package/src/{activities/execute-cursor → shared}/persist-decision.ts +5 -2
- package/src/{activities/execute-cursor → shared}/skill-resolver.ts +20 -6
- package/src/shared/subagent-rows.ts +39 -0
- package/src/shared/worker-shutdown.ts +55 -25
- package/src/{activities/execute-cursor/__tests__/workspace-provision.test.ts → shared/workspace/__tests__/session-provision.test.ts} +25 -28
- package/src/shared/workspace/platform-dir.ts +1 -1
- package/src/{activities/execute-cursor/workspace-provision.ts → shared/workspace/session-provision.ts} +29 -16
- package/dist/activities/execute-cursor/attachment-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/blueprint-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/cost-guard.js.map +0 -1
- package/dist/activities/execute-cursor/env-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/exact-apply.js.map +0 -1
- package/dist/activities/execute-cursor/extract-structured-output.js.map +0 -1
- package/dist/activities/execute-cursor/index.d.ts +0 -209
- package/dist/activities/execute-cursor/index.js +0 -2345
- package/dist/activities/execute-cursor/index.js.map +0 -1
- package/dist/activities/execute-cursor/persist-decision.js.map +0 -1
- package/dist/activities/execute-cursor/skill-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/usage-accumulator.d.ts +0 -89
- package/dist/activities/execute-cursor/usage-accumulator.js +0 -121
- package/dist/activities/execute-cursor/usage-accumulator.js.map +0 -1
- package/dist/activities/execute-cursor/workspace-provision.d.ts +0 -42
- package/dist/activities/execute-cursor/workspace-provision.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/usage-accumulator.test.ts +0 -164
- package/src/activities/execute-cursor/index.ts +0 -2876
- package/src/activities/execute-cursor/usage-accumulator.ts +0 -169
- /package/dist/{activities/execute-cursor → shared}/env-resolver.js +0 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripted `@cursor/sdk` agent — the double the hermetic `ExecuteCursor` runs
|
|
3
|
+
* drive instead of a live Cursor agent.
|
|
4
|
+
*
|
|
5
|
+
* The Cursor harness delegates the agent loop to the SDK: `agent.send()`
|
|
6
|
+
* returns a `Run` whose `stream()` yields `SDKMessage`s, whose `onDelta`
|
|
7
|
+
* callback carries usage, and whose `wait()` resolves the `RunResult`. Between
|
|
8
|
+
* those events the REAL SDK also performs side effects the harness observes
|
|
9
|
+
* only indirectly — it executes the tool (writing a file into the workspace) and
|
|
10
|
+
* it spawns the workspace's `.cursor/hooks.json` hook before a gated tool (the
|
|
11
|
+
* runner's own bash script, which appends to the denial ledger). A double that
|
|
12
|
+
* replays events alone would leave those effects out and the harness's
|
|
13
|
+
* deny-and-retry and file-review paths untested.
|
|
14
|
+
*
|
|
15
|
+
* So a turn is a SCRIPT of ordered steps, one of four kinds:
|
|
16
|
+
*
|
|
17
|
+
* - `event(SDKMessage)` — yielded from `stream()`
|
|
18
|
+
* - `delta(update)` — fired through `onDelta` (usage, `turn-ended`)
|
|
19
|
+
* - `effect(fn)` — the side effect the SDK would have performed
|
|
20
|
+
* (run the real hook, write a workspace file,
|
|
21
|
+
* cancel the activity from the outside)
|
|
22
|
+
* - `result(RunResult)` — what `wait()` resolves; ends the run
|
|
23
|
+
*
|
|
24
|
+
* The same shape as the native harness's `__test-utils__/scripted-model.ts`
|
|
25
|
+
* (`ScriptStep`, driven in order), applied to the SDK's event surface.
|
|
26
|
+
*
|
|
27
|
+
* Typing: `SDKMessage`, `Run`, `RunResult` and `SDKAgent` are the SDK's own
|
|
28
|
+
* types (`@cursor/sdk` 1.0.13, concrete in `messages.d.ts` / `run.d.ts` /
|
|
29
|
+
* `agent.d.ts`), so a script that drifts from the real event shape fails
|
|
30
|
+
* `tsc`. The delta channel is the exception: `InteractionUpdate` re-exports
|
|
31
|
+
* from `@anysphere/cursor-sdk-shared`, a workspace package the published SDK
|
|
32
|
+
* does not ship, so under `skipLibCheck` it resolves to `any` — in production
|
|
33
|
+
* code too (`turn-stream.ts`, `delta-enricher.ts` read it untyped). The one
|
|
34
|
+
* delta this double emits, the `turn-ended` usage, is therefore typed HERE
|
|
35
|
+
* ({@link TurnEndedUsageDelta}) against what `turn-stream.ts` reads from it,
|
|
36
|
+
* and that gap is recorded in the entry's findings rather than papered over.
|
|
37
|
+
*
|
|
38
|
+
* Cancellation: `run.cancel()` is how the harness stops a turn (first denial,
|
|
39
|
+
* cost cap, stall). The double honours it the way the SDK does — the stream
|
|
40
|
+
* ends at the next step, `wait()` resolves `{ status: "cancelled" }` — because
|
|
41
|
+
* the deny-and-retry path depends on exactly that ordering.
|
|
42
|
+
*
|
|
43
|
+
* Every `send()` consumes the NEXT script in the agent's queue: turn 1, then
|
|
44
|
+
* the resumed turn 2 on the same parked agent. A `send()` with no script left
|
|
45
|
+
* is a test bug and throws. A golden scenario declares every script up front
|
|
46
|
+
* (`turns`); a driver that learns what the next turn plays only just before
|
|
47
|
+
* it runs — the harness contract kit's Cursor subject — arranges it with
|
|
48
|
+
* {@link ScriptedCursorAgent.arrangeNextTurn}, which REPLACES the slot the
|
|
49
|
+
* next `send()` will consume.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import type {
|
|
53
|
+
ModelSelection,
|
|
54
|
+
Run,
|
|
55
|
+
RunOperation,
|
|
56
|
+
RunResult,
|
|
57
|
+
RunStatus,
|
|
58
|
+
SDKAgent,
|
|
59
|
+
SDKMessage,
|
|
60
|
+
SDKUserMessage,
|
|
61
|
+
SendOptions,
|
|
62
|
+
} from "@cursor/sdk";
|
|
63
|
+
import type { ConversationTurn } from "@cursor/sdk";
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Script steps
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The `turn-ended` delta as `turn-stream.ts` reads it (`update.type ===
|
|
71
|
+
* "turn-ended" && update.usage` -> `usageAccumulator.addTurn(update.usage)`).
|
|
72
|
+
* See the module header for why this is typed locally.
|
|
73
|
+
*/
|
|
74
|
+
export interface TurnEndedUsageDelta {
|
|
75
|
+
readonly type: "turn-ended";
|
|
76
|
+
readonly usage: {
|
|
77
|
+
readonly inputTokens?: number;
|
|
78
|
+
readonly outputTokens?: number;
|
|
79
|
+
readonly cacheWriteTokens?: number;
|
|
80
|
+
readonly cacheReadTokens?: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** What an `effect` step can reach: the run it is part of. */
|
|
85
|
+
export interface EffectContext {
|
|
86
|
+
readonly run: ScriptedRun;
|
|
87
|
+
readonly agent: ScriptedCursorAgent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type ScriptStep =
|
|
91
|
+
| { readonly kind: "event"; readonly event: SDKMessage }
|
|
92
|
+
| { readonly kind: "delta"; readonly update: TurnEndedUsageDelta }
|
|
93
|
+
| { readonly kind: "effect"; readonly label: string; readonly run: (ctx: EffectContext) => void | Promise<void> }
|
|
94
|
+
| { readonly kind: "result"; readonly result: Omit<RunResult, "id"> };
|
|
95
|
+
|
|
96
|
+
/** One `send()`'s worth of behaviour. */
|
|
97
|
+
export type TurnScript = readonly ScriptStep[];
|
|
98
|
+
|
|
99
|
+
/** Step builders — the vocabulary a scenario file reads as. */
|
|
100
|
+
export const step = {
|
|
101
|
+
event(event: SDKMessage): ScriptStep {
|
|
102
|
+
return { kind: "event", event };
|
|
103
|
+
},
|
|
104
|
+
turnEnded(usage: TurnEndedUsageDelta["usage"]): ScriptStep {
|
|
105
|
+
return { kind: "delta", update: { type: "turn-ended", usage } };
|
|
106
|
+
},
|
|
107
|
+
effect(label: string, run: (ctx: EffectContext) => void | Promise<void>): ScriptStep {
|
|
108
|
+
return { kind: "effect", label, run };
|
|
109
|
+
},
|
|
110
|
+
finished(result?: Partial<Omit<RunResult, "id" | "status">>): ScriptStep {
|
|
111
|
+
return { kind: "result", result: { status: "finished", ...result } };
|
|
112
|
+
},
|
|
113
|
+
errored(result?: Partial<Omit<RunResult, "id" | "status">>): ScriptStep {
|
|
114
|
+
return { kind: "result", result: { status: "error", ...result } };
|
|
115
|
+
},
|
|
116
|
+
} as const;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `SDKMessage` builders bound to one (agent_id, run_id) pair, so a scenario
|
|
120
|
+
* reads as the conversation it scripts. Shapes are the SDK's `messages.d.ts`;
|
|
121
|
+
* tool names are the STREAM taxonomy (`edit`, `shell`, `read` — lowercase; the
|
|
122
|
+
* hook sees `Write`, `Shell`, `Read`; see `approval-gate.test.ts`).
|
|
123
|
+
*/
|
|
124
|
+
export function sdkEvents(agentId: string, runId: string) {
|
|
125
|
+
const base = { agent_id: agentId, run_id: runId } as const;
|
|
126
|
+
return {
|
|
127
|
+
init(): SDKMessage {
|
|
128
|
+
return { type: "system", subtype: "init", ...base };
|
|
129
|
+
},
|
|
130
|
+
assistant(text: string): SDKMessage {
|
|
131
|
+
return { type: "assistant", ...base, message: { role: "assistant", content: [{ type: "text", text }] } };
|
|
132
|
+
},
|
|
133
|
+
thinking(text: string): SDKMessage {
|
|
134
|
+
return { type: "thinking", ...base, text };
|
|
135
|
+
},
|
|
136
|
+
toolCall(
|
|
137
|
+
callId: string,
|
|
138
|
+
name: string,
|
|
139
|
+
status: "running" | "completed" | "error",
|
|
140
|
+
args?: unknown,
|
|
141
|
+
result?: unknown,
|
|
142
|
+
): SDKMessage {
|
|
143
|
+
return { type: "tool_call", ...base, call_id: callId, name, status, args, result };
|
|
144
|
+
},
|
|
145
|
+
status(status: "CREATING" | "RUNNING" | "FINISHED" | "ERROR" | "CANCELLED" | "EXPIRED", message?: string): SDKMessage {
|
|
146
|
+
return { type: "status", ...base, status, message };
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// The run
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/** Called once per step so the driver's clock can tick. */
|
|
156
|
+
export type StepObserver = (step: ScriptStep, index: number) => void;
|
|
157
|
+
|
|
158
|
+
export class ScriptedRun implements Run {
|
|
159
|
+
readonly id: string;
|
|
160
|
+
readonly agentId: string;
|
|
161
|
+
private _status: RunStatus = "running";
|
|
162
|
+
private cancelled = false;
|
|
163
|
+
private scriptedResult: Omit<RunResult, "id"> | undefined;
|
|
164
|
+
private streamed = false;
|
|
165
|
+
private readonly statusListeners = new Set<(status: RunStatus) => void>();
|
|
166
|
+
/** `run.cancel()` calls, for assertions. */
|
|
167
|
+
readonly cancelCalls: number[] = [];
|
|
168
|
+
|
|
169
|
+
constructor(
|
|
170
|
+
readonly agent: ScriptedCursorAgent,
|
|
171
|
+
id: string,
|
|
172
|
+
private readonly script: TurnScript,
|
|
173
|
+
private readonly onDelta: SendOptions["onDelta"],
|
|
174
|
+
private readonly observeStep: StepObserver | undefined,
|
|
175
|
+
/** What `conversation()` answers; the error classifier reads it on a failed run. */
|
|
176
|
+
private readonly conversationTurns: ConversationTurn[] = [],
|
|
177
|
+
) {
|
|
178
|
+
this.id = id;
|
|
179
|
+
this.agentId = agent.agentId;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get status(): RunStatus {
|
|
183
|
+
return this._status;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
get result(): string | undefined {
|
|
187
|
+
return this.scriptedResult?.result;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
get model(): ModelSelection | undefined {
|
|
191
|
+
return this.scriptedResult?.model ?? this.agent.model;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
supports(_operation: RunOperation): boolean {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
unsupportedReason(_operation: RunOperation): string | undefined {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
onDidChangeStatus(listener: (status: RunStatus) => void): () => void {
|
|
203
|
+
this.statusListeners.add(listener);
|
|
204
|
+
return () => this.statusListeners.delete(listener);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async conversation(): Promise<ConversationTurn[]> {
|
|
208
|
+
return this.conversationTurns;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async *stream(): AsyncGenerator<SDKMessage, void> {
|
|
212
|
+
if (this.streamed) {
|
|
213
|
+
throw new Error(`ScriptedRun ${this.id}: stream() consumed twice`);
|
|
214
|
+
}
|
|
215
|
+
this.streamed = true;
|
|
216
|
+
for (let i = 0; i < this.script.length; i++) {
|
|
217
|
+
// A cancel lands between steps, exactly where the SDK's own stream would
|
|
218
|
+
// stop delivering.
|
|
219
|
+
if (this.cancelled) break;
|
|
220
|
+
const s = this.script[i];
|
|
221
|
+
this.observeStep?.(s, i);
|
|
222
|
+
switch (s.kind) {
|
|
223
|
+
case "event":
|
|
224
|
+
yield s.event;
|
|
225
|
+
break;
|
|
226
|
+
case "delta":
|
|
227
|
+
await this.onDelta?.({ update: s.update as never });
|
|
228
|
+
break;
|
|
229
|
+
case "effect":
|
|
230
|
+
await s.run({ run: this, agent: this.agent });
|
|
231
|
+
break;
|
|
232
|
+
case "result":
|
|
233
|
+
this.scriptedResult = s.result;
|
|
234
|
+
break;
|
|
235
|
+
default: {
|
|
236
|
+
const exhaustive: never = s;
|
|
237
|
+
throw new Error(`ScriptedRun: unknown step ${String(exhaustive)}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (!this.cancelled) this.setStatus(this.scriptedResult?.status ?? "finished");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async wait(): Promise<RunResult> {
|
|
245
|
+
if (this.cancelled) {
|
|
246
|
+
return { id: this.id, status: "cancelled" };
|
|
247
|
+
}
|
|
248
|
+
if (!this.scriptedResult) {
|
|
249
|
+
// A script that never declared its result is a test bug — surface it
|
|
250
|
+
// instead of inventing a "finished" the golden would then pin.
|
|
251
|
+
throw new Error(
|
|
252
|
+
`ScriptedRun ${this.id}: wait() called but the script has no result step ` +
|
|
253
|
+
`(add step.finished() or step.errored())`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return { id: this.id, ...this.scriptedResult };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async cancel(): Promise<void> {
|
|
260
|
+
this.cancelCalls.push(Date.now());
|
|
261
|
+
this.cancelled = true;
|
|
262
|
+
this.setStatus("cancelled");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private setStatus(status: RunStatus): void {
|
|
266
|
+
this._status = status;
|
|
267
|
+
for (const l of this.statusListeners) l(status);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// The agent
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
|
|
275
|
+
export interface ScriptedAgentOptions {
|
|
276
|
+
readonly agentId: string;
|
|
277
|
+
/** One script per `send()`, consumed in order. */
|
|
278
|
+
readonly turns: readonly TurnScript[];
|
|
279
|
+
/** Run ids, one per turn, so goldens carry stable `run_id`s. */
|
|
280
|
+
readonly runIds?: readonly string[];
|
|
281
|
+
readonly observeStep?: StepObserver;
|
|
282
|
+
readonly conversationTurns?: ConversationTurn[];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** What the harness passed to `send()`, kept for assertions on the prompt. */
|
|
286
|
+
export interface RecordedSend {
|
|
287
|
+
readonly message: string | SDKUserMessage;
|
|
288
|
+
readonly hasOnDelta: boolean;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** The run id a send is given when the scenario named none: `run-<agentId>-<n>`, `n` counting sends from 1. */
|
|
292
|
+
export function defaultRunId(agentId: string, sendIndex: number): string {
|
|
293
|
+
return `run-${agentId}-${sendIndex + 1}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export class ScriptedCursorAgent implements SDKAgent {
|
|
297
|
+
readonly agentId: string;
|
|
298
|
+
model: ModelSelection | undefined = undefined;
|
|
299
|
+
readonly sends: RecordedSend[] = [];
|
|
300
|
+
readonly runs: ScriptedRun[] = [];
|
|
301
|
+
closeCalls = 0;
|
|
302
|
+
private nextTurn = 0;
|
|
303
|
+
/** The scenario's scripts, one slot per send; `arrangeNextTurn` writes the slot the next send consumes. */
|
|
304
|
+
private readonly turns: TurnScript[];
|
|
305
|
+
|
|
306
|
+
constructor(private readonly options: ScriptedAgentOptions) {
|
|
307
|
+
this.agentId = options.agentId;
|
|
308
|
+
this.turns = [...options.turns];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** The run id the NEXT `send()` will be given — so a script can be built with matching event ids before the send happens. */
|
|
312
|
+
get nextRunId(): string {
|
|
313
|
+
return this.options.runIds?.[this.nextTurn] ?? defaultRunId(this.agentId, this.nextTurn);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Set (or replace) what the NEXT `send()` plays. Scripts already consumed
|
|
318
|
+
* are untouched; a slot declared up front for a later send stays where it
|
|
319
|
+
* is.
|
|
320
|
+
*/
|
|
321
|
+
arrangeNextTurn(script: TurnScript): void {
|
|
322
|
+
this.turns[this.nextTurn] = script;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async send(message: string | SDKUserMessage, options?: SendOptions): Promise<Run> {
|
|
326
|
+
const script = this.turns[this.nextTurn];
|
|
327
|
+
if (!script) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`ScriptedCursorAgent ${this.agentId}: send() #${this.nextTurn + 1} has no script ` +
|
|
330
|
+
`(the scenario declared ${this.turns.length})`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
const runId = this.nextRunId;
|
|
334
|
+
this.nextTurn++;
|
|
335
|
+
this.sends.push({ message, hasOnDelta: !!options?.onDelta });
|
|
336
|
+
if (options?.model) this.model = options.model;
|
|
337
|
+
const run = new ScriptedRun(
|
|
338
|
+
this,
|
|
339
|
+
runId,
|
|
340
|
+
script,
|
|
341
|
+
options?.onDelta,
|
|
342
|
+
this.options.observeStep,
|
|
343
|
+
this.options.conversationTurns,
|
|
344
|
+
);
|
|
345
|
+
this.runs.push(run);
|
|
346
|
+
return run;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
close(): void {
|
|
350
|
+
this.closeCalls++;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async reload(): Promise<void> {
|
|
354
|
+
// No persisted state to reload in the double.
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
358
|
+
this.close();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async listArtifacts(): Promise<never[]> {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async downloadArtifact(_path: string): Promise<Buffer> {
|
|
366
|
+
throw new Error("ScriptedCursorAgent: artifacts are not part of the scripted surface");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@cursor/sdk` module a hermetic `ExecuteCursor` run imports — every
|
|
3
|
+
* runtime surface the activity path touches, backed by scripted agents.
|
|
4
|
+
*
|
|
5
|
+
* The activity reaches the SDK at three runtime sites and nowhere else:
|
|
6
|
+
*
|
|
7
|
+
* - `session-lifecycle.ts`: `Agent.create(options)`, `Agent.resume(id, options)`,
|
|
8
|
+
* `Agent.archive(id, ...)` — the agent handle.
|
|
9
|
+
* - `service-tier.ts`: `Cursor.models.list({ apiKey })` — the catalog the
|
|
10
|
+
* variant params (`fast`, `thinking`) are pinned from.
|
|
11
|
+
* - `index.ts` outer catch: `import("@cursor/sdk")` for `CursorSdkError`, the
|
|
12
|
+
* `instanceof` the error classifier keys on.
|
|
13
|
+
*
|
|
14
|
+
* Neither the SDK nor the client is injectable into the activity (the real
|
|
15
|
+
* seam arrives with the harness contract; parent Q1/Q2), so — exactly as the
|
|
16
|
+
* native activity tests already do for their boundary modules — the module is
|
|
17
|
+
* substituted with `vi.mock`. Three existing tests each mock ONE of these
|
|
18
|
+
* surfaces (`session-lifecycle.test.ts`, `service-tier.test.ts`,
|
|
19
|
+
* `sdk-warmup.test.ts`); a whole-activity run needs all three at once, which is
|
|
20
|
+
* what this module is.
|
|
21
|
+
*
|
|
22
|
+
* `vi.mock` is hoisted and must appear in the test file; the factory can
|
|
23
|
+
* `await import()` this module and return {@link scriptedCursorSdkModule}. The
|
|
24
|
+
* module's statics read the {@link ScriptedCursorSdk} bound for the current
|
|
25
|
+
* scenario ({@link bindScriptedSdk}), so one test file can run several
|
|
26
|
+
* scenarios with different agents against the same mocked module.
|
|
27
|
+
*
|
|
28
|
+
* Resolution rules a scenario declares, mirroring what the real SDK does:
|
|
29
|
+
* - `Agent.create` hands out the agent declared for the session it is asked
|
|
30
|
+
* for (`agentForSession`, when the scenario resolves agents per session —
|
|
31
|
+
* the harness contract kit runs several sessions concurrently on one
|
|
32
|
+
* adapter, and the order their creates arrive in is real I/O timing), else
|
|
33
|
+
* the NEXT unclaimed agent in `agents` (turn 1 of a fresh session; the fresh
|
|
34
|
+
* agent of a poisoned-handle recovery) — unless the scenario declared a
|
|
35
|
+
* `createFailure`, which the FIRST create throws instead (the SDK refusing
|
|
36
|
+
* to mint an agent: a 401 on the key, a validation error).
|
|
37
|
+
* - `Agent.resume(id)` hands back the agent with that id if the scenario
|
|
38
|
+
* declared it (in `agents` or `resumableAgents`, or handed out for a
|
|
39
|
+
* session), else throws — `resolveAgent` then falls back to `create`, which
|
|
40
|
+
* is the production shape of a lost handle.
|
|
41
|
+
* - `Cursor.models.list` answers the scenario's catalog.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import type { AgentOptions, ModelListItem, SDKAgent } from "@cursor/sdk";
|
|
45
|
+
import type { ScriptedCursorAgent } from "./scripted-agent.js";
|
|
46
|
+
|
|
47
|
+
export interface ScriptedSdkOptions {
|
|
48
|
+
/** Agents handed out by `Agent.create`, in order. Also resumable by id. */
|
|
49
|
+
readonly agents: readonly ScriptedCursorAgent[];
|
|
50
|
+
/**
|
|
51
|
+
* The agent `Agent.create` hands out for the session the create names,
|
|
52
|
+
* consulted before the ordered list. The SDK's create options carry the
|
|
53
|
+
* session as `platform.workspaceRef` (`session-lifecycle.ts`
|
|
54
|
+
* `resolvePlatformOptions`: `stigmer-session:<sessionId>`), so the resolver
|
|
55
|
+
* is keyed by that ref — a scenario computes the ref the same way the
|
|
56
|
+
* adapter does. `undefined` falls through to the list. Consulted live on
|
|
57
|
+
* every create, so a scenario may mint the session's agent only when it
|
|
58
|
+
* learns of the session.
|
|
59
|
+
*/
|
|
60
|
+
readonly agentForWorkspaceRef?: (workspaceRef: string) => ScriptedCursorAgent | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Agents `Agent.resume` finds by id but `Agent.create` never hands out — a
|
|
63
|
+
* previous turn's agent the session knows only by `harness_state_id`.
|
|
64
|
+
*/
|
|
65
|
+
readonly resumableAgents?: readonly ScriptedCursorAgent[];
|
|
66
|
+
/**
|
|
67
|
+
* Thrown by the FIRST `Agent.create` instead of handing out an agent; later
|
|
68
|
+
* creates hand out `agents` as usual. A {@link ScriptedCursorSdkError} here is
|
|
69
|
+
* the SDK's own refusal (the activity's outer catch keys on `instanceof`);
|
|
70
|
+
* a plain `Error` is anything else that can escape the SDK boundary.
|
|
71
|
+
*/
|
|
72
|
+
readonly createFailure?: Error;
|
|
73
|
+
/** What `Cursor.models.list` answers. */
|
|
74
|
+
readonly catalog: readonly ModelListItem[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** One `Agent.create` / `Agent.resume` call as the activity made it. */
|
|
78
|
+
export interface RecordedResolution {
|
|
79
|
+
readonly kind: "create" | "resume";
|
|
80
|
+
readonly agentId: string | undefined;
|
|
81
|
+
readonly options: Partial<AgentOptions> | undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The `platform.workspaceRef` a create names, read defensively: the platform
|
|
86
|
+
* option's type re-exports from the unshipped `@anysphere/cursor-sdk-local-runtime`
|
|
87
|
+
* and resolves to `any` under `skipLibCheck` (the same gap `scripted-agent.ts`
|
|
88
|
+
* records for the delta channel), so the shape is checked here, not assumed.
|
|
89
|
+
*/
|
|
90
|
+
function workspaceRefOf(options: AgentOptions): string | undefined {
|
|
91
|
+
const platform: unknown = (options as { platform?: unknown }).platform;
|
|
92
|
+
if (typeof platform !== "object" || platform === null) return undefined;
|
|
93
|
+
const ref = (platform as { workspaceRef?: unknown }).workspaceRef;
|
|
94
|
+
return typeof ref === "string" ? ref : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class ScriptedCursorSdk {
|
|
98
|
+
readonly resolutions: RecordedResolution[] = [];
|
|
99
|
+
readonly archived: string[] = [];
|
|
100
|
+
private nextCreate = 0;
|
|
101
|
+
private createFailurePending: boolean;
|
|
102
|
+
private readonly byId = new Map<string, ScriptedCursorAgent>();
|
|
103
|
+
|
|
104
|
+
constructor(private readonly options: ScriptedSdkOptions) {
|
|
105
|
+
for (const a of [...options.agents, ...(options.resumableAgents ?? [])]) {
|
|
106
|
+
this.byId.set(a.agentId, a);
|
|
107
|
+
}
|
|
108
|
+
this.createFailurePending = options.createFailure !== undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
create(options: AgentOptions): SDKAgent {
|
|
112
|
+
if (this.createFailurePending && this.options.createFailure) {
|
|
113
|
+
this.createFailurePending = false;
|
|
114
|
+
this.resolutions.push({ kind: "create", agentId: undefined, options });
|
|
115
|
+
throw this.options.createFailure;
|
|
116
|
+
}
|
|
117
|
+
const workspaceRef = workspaceRefOf(options);
|
|
118
|
+
const forSession = workspaceRef !== undefined ? this.options.agentForWorkspaceRef?.(workspaceRef) : undefined;
|
|
119
|
+
if (forSession) {
|
|
120
|
+
this.byId.set(forSession.agentId, forSession);
|
|
121
|
+
forSession.model = options.model;
|
|
122
|
+
this.resolutions.push({ kind: "create", agentId: forSession.agentId, options });
|
|
123
|
+
return forSession;
|
|
124
|
+
}
|
|
125
|
+
const agent = this.options.agents[this.nextCreate];
|
|
126
|
+
if (!agent) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`ScriptedCursorSdk: Agent.create() #${this.nextCreate + 1} has no agent ` +
|
|
129
|
+
`(the scenario declared ${this.options.agents.length})`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
this.nextCreate++;
|
|
133
|
+
agent.model = options.model;
|
|
134
|
+
this.resolutions.push({ kind: "create", agentId: agent.agentId, options });
|
|
135
|
+
return agent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
resume(agentId: string, options: Partial<AgentOptions> | undefined): SDKAgent {
|
|
139
|
+
this.resolutions.push({ kind: "resume", agentId, options });
|
|
140
|
+
const agent = this.byId.get(agentId);
|
|
141
|
+
if (!agent) {
|
|
142
|
+
throw new ScriptedCursorSdkError(`agent ${agentId} not found`, { code: "agent_not_found" });
|
|
143
|
+
}
|
|
144
|
+
if (options?.model) agent.model = options.model;
|
|
145
|
+
return agent;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
archive(agentId: string): void {
|
|
149
|
+
this.archived.push(agentId);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
listModels(): readonly ModelListItem[] {
|
|
153
|
+
return this.options.catalog;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The construction options of the SDK's `CursorSdkError` (`errors.d.ts`), minus `cause`. */
|
|
158
|
+
export interface ScriptedCursorSdkErrorOptions {
|
|
159
|
+
readonly isRetryable?: boolean;
|
|
160
|
+
readonly code?: string;
|
|
161
|
+
readonly status?: number;
|
|
162
|
+
readonly endpoint?: string;
|
|
163
|
+
readonly requestId?: string;
|
|
164
|
+
readonly operation?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The `CursorSdkError` the mocked module exports. A real `Error` subclass with
|
|
169
|
+
* the SDK class's public surface (`@cursor/sdk` `errors.d.ts`: `isRetryable`,
|
|
170
|
+
* `code`, `status`, `endpoint`, `requestId`, `operation`, `toJSON()`), under
|
|
171
|
+
* the SDK's class name so `err.constructor.name` and `instanceof` (against
|
|
172
|
+
* THIS module's export, which is what the activity's dynamic import resolves
|
|
173
|
+
* to) both hold. The activity's outer catch reads `code`, `status` and
|
|
174
|
+
* `message` for the classifier and logs `toJSON()`; the double must carry the
|
|
175
|
+
* whole surface or the catch arm itself throws instead of mapping the error.
|
|
176
|
+
*/
|
|
177
|
+
export class ScriptedCursorSdkError extends Error {
|
|
178
|
+
readonly isRetryable: boolean;
|
|
179
|
+
readonly code: string | undefined;
|
|
180
|
+
readonly status: number | undefined;
|
|
181
|
+
readonly endpoint: string | undefined;
|
|
182
|
+
readonly requestId: string | undefined;
|
|
183
|
+
readonly operation: string | undefined;
|
|
184
|
+
constructor(message: string, opts: ScriptedCursorSdkErrorOptions = {}) {
|
|
185
|
+
super(message);
|
|
186
|
+
this.name = "CursorSdkError";
|
|
187
|
+
this.isRetryable = opts.isRetryable ?? false;
|
|
188
|
+
this.code = opts.code;
|
|
189
|
+
this.status = opts.status;
|
|
190
|
+
this.endpoint = opts.endpoint;
|
|
191
|
+
this.requestId = opts.requestId;
|
|
192
|
+
this.operation = opts.operation;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
toJSON(): Record<string, unknown> {
|
|
196
|
+
return {
|
|
197
|
+
name: this.name,
|
|
198
|
+
message: this.message,
|
|
199
|
+
isRetryable: this.isRetryable,
|
|
200
|
+
code: this.code,
|
|
201
|
+
status: this.status,
|
|
202
|
+
endpoint: this.endpoint,
|
|
203
|
+
requestId: this.requestId,
|
|
204
|
+
operation: this.operation,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let bound: ScriptedCursorSdk | undefined;
|
|
210
|
+
|
|
211
|
+
/** Bind the SDK the mocked module serves for the current scenario. */
|
|
212
|
+
export function bindScriptedSdk(sdk: ScriptedCursorSdk): void {
|
|
213
|
+
bound = sdk;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function current(): ScriptedCursorSdk {
|
|
217
|
+
if (!bound) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
"scripted-sdk: no ScriptedCursorSdk bound — call bindScriptedSdk(sdk) before running the activity",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
return bound;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The factory a test passes to `vi.mock("@cursor/sdk", ...)`:
|
|
227
|
+
*
|
|
228
|
+
* ```ts
|
|
229
|
+
* vi.mock("@cursor/sdk", async () =>
|
|
230
|
+
* (await import("../../__test-utils__/scripted-sdk.js")).scriptedCursorSdkModule(),
|
|
231
|
+
* );
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* Only the runtime surface the activity path uses is provided. Any other
|
|
235
|
+
* import from the mocked module is `undefined` and fails loudly at the call
|
|
236
|
+
* site — deliberately, so a new SDK dependency in production code is noticed
|
|
237
|
+
* here rather than silently stubbed.
|
|
238
|
+
*/
|
|
239
|
+
export function scriptedCursorSdkModule(): Record<string, unknown> {
|
|
240
|
+
return {
|
|
241
|
+
Agent: {
|
|
242
|
+
create: async (options: AgentOptions): Promise<SDKAgent> => current().create(options),
|
|
243
|
+
resume: async (agentId: string, options?: Partial<AgentOptions>): Promise<SDKAgent> =>
|
|
244
|
+
current().resume(agentId, options),
|
|
245
|
+
archive: async (agentId: string): Promise<void> => current().archive(agentId),
|
|
246
|
+
},
|
|
247
|
+
Cursor: {
|
|
248
|
+
models: {
|
|
249
|
+
list: async (): Promise<readonly ModelListItem[]> => current().listModels(),
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
CursorSdkError: ScriptedCursorSdkError,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins that the Cursor adapter is an SDK slice and nothing more: no
|
|
3
|
+
* production module under `activities/execute-cursor/` imports
|
|
4
|
+
* `@temporalio/*`.
|
|
5
|
+
*
|
|
6
|
+
* Why it matters: every Temporal fact a turn needs — the heartbeat, the
|
|
7
|
+
* cancellation, the throw the workflow sees — is the turn runtime's
|
|
8
|
+
* (`harness/run-turn.ts`) and reaches the adapter through the sink. The
|
|
9
|
+
* contract kit runs an adapter OUTSIDE any activity context
|
|
10
|
+
* (`__test-utils__/harness-contract/`), so a `Context.current()` anywhere
|
|
11
|
+
* in the adapter would throw there; and an adapter that threw a
|
|
12
|
+
* `CancelledFailure` would be deciding pause-vs-shutdown, which is the
|
|
13
|
+
* runtime's table. A static fence names the file in seconds where the kit
|
|
14
|
+
* would name it after a stack trace.
|
|
15
|
+
*
|
|
16
|
+
* Read off the syntax tree with the same primitive the import-direction
|
|
17
|
+
* fence uses (`__test-utils__/module-specifiers.ts`), so a package name in a
|
|
18
|
+
* comment cannot trip it and no import form can slip past it.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { describe, it, expect } from "vitest";
|
|
22
|
+
import { join, relative } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
|
|
25
|
+
import { packageImports, readSource, typeScriptFilesUnder } from "../../../__test-utils__/module-specifiers.js";
|
|
26
|
+
|
|
27
|
+
const SRC_ROOT = fileURLToPath(new URL("../../../", import.meta.url));
|
|
28
|
+
const ADAPTER_ROOT = join(SRC_ROOT, "activities", "execute-cursor");
|
|
29
|
+
|
|
30
|
+
describe("the Cursor adapter never imports @temporalio", () => {
|
|
31
|
+
const files = typeScriptFilesUnder(ADAPTER_ROOT, new Set(["__tests__", "__test-utils__"]));
|
|
32
|
+
const offences = files.flatMap((file) =>
|
|
33
|
+
packageImports(file, readSource(file), "@temporalio").map((specifier) => ` ${relative(SRC_ROOT, file)} imports "${specifier}"`),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
it("inspected the adapter's own files (the walk is rooted where it claims)", () => {
|
|
37
|
+
expect(files.map((f) => relative(ADAPTER_ROOT, f))).toEqual(
|
|
38
|
+
expect.arrayContaining(["adapter.ts", "turn.ts", "turn-setup.ts", "turn-stream.ts", "turn-settle.ts"]),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("finds no @temporalio import in any production module", () => {
|
|
43
|
+
expect(offences, `the Cursor adapter must not import @temporalio/*:\n${offences.join("\n")}`).toEqual([]);
|
|
44
|
+
});
|
|
45
|
+
});
|