@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,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One Cursor engine turn — the SDK slice of the Cursor harness, behind the
|
|
3
|
+
* turn runtime's contract (`harness/types.ts`).
|
|
4
|
+
*
|
|
5
|
+
* `runCursorTurn` is what `adapter.ts`'s `runTurn` delegates to: the HITL
|
|
6
|
+
* gate, the agent (parked, created or resumed), the prompt, the stream, the
|
|
7
|
+
* turn boundary, `run.wait()` and its recovery spines, and its own teardown
|
|
8
|
+
* (the denial watcher, the gate, the OTel span, and what becomes of the agent
|
|
9
|
+
* handle). Everything else about a turn is the runtime's
|
|
10
|
+
* (`harness/run-turn.ts`): the resolution phases, the persist chokepoint, the
|
|
11
|
+
* watchdog, the cost cap, the interruption table, the completion epilogue,
|
|
12
|
+
* the lock.
|
|
13
|
+
*
|
|
14
|
+
* This module is where `@cursor/sdk` enters the adapter's graph (through
|
|
15
|
+
* `turn-setup.ts` and its siblings), which is WHY it is a separate module
|
|
16
|
+
* from `adapter.ts` and is loaded there with a dynamic import inside `boot`:
|
|
17
|
+
* the adapter's HTTP/2 interceptor must patch `node:http2` before the SDK
|
|
18
|
+
* (and, through it, `@connectrpc/connect-node`) is first imported, and the
|
|
19
|
+
* composition roots import `adapter.ts` before they boot it. Nothing outside
|
|
20
|
+
* `adapter.ts` imports this module.
|
|
21
|
+
*
|
|
22
|
+
* Never throws out of `runCursorTurn` (an SDK failure is `failed` with the
|
|
23
|
+
* classifier's sentence; an unexpected exception is `failed` on the
|
|
24
|
+
* `internal` surface; a `CancelledFailure` never exists here), never branches
|
|
25
|
+
* on why `stopSignal` aborted, never writes a phase or a terminal copy, never
|
|
26
|
+
* imports `@temporalio/*` (`__tests__/adapter-is-temporal-free.test.ts`).
|
|
27
|
+
*
|
|
28
|
+
* Engine disposition, one rule per outcome (Q-S2-6): the agent is PARKED
|
|
29
|
+
* for the session's next turn on every exit but a failure — `completed`,
|
|
30
|
+
* `cancelled`, `awaiting_approval` and `interrupted` alike (a wedged handle
|
|
31
|
+
* after a stall is caught by the poisoned-handle recovery on the next turn;
|
|
32
|
+
* before S2 a pause, a shutdown and a stall dropped the handle, leaking the
|
|
33
|
+
* executor lease). A `failed` turn keeps the orchestrator's three
|
|
34
|
+
* dispositions: an engine-reported failure parks (a bad key is not a bad
|
|
35
|
+
* handle), a user-actionable failure closes, an internal failure drops.
|
|
36
|
+
*
|
|
37
|
+
* Extracted from `index.ts` `executeCursorInner` at S2 M3b and moved out of
|
|
38
|
+
* `adapter.ts` at S2 M5; the seventeen hermetic goldens under `__tests__/hermetic/`
|
|
39
|
+
* pin the result byte for byte.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import type { TurnInput, TurnOutcome, TurnSink } from "../../harness/types.js";
|
|
43
|
+
import { describeExecutionError } from "../../shared/model-error.js";
|
|
44
|
+
import { cacheSessionAgent } from "./agent-session-cache.js";
|
|
45
|
+
import { formatClassifiedError, synthesizeError } from "./error-classifier.js";
|
|
46
|
+
import { setInterceptorExecutionId } from "./fetch-interceptor.js";
|
|
47
|
+
import { closeProxySessions } from "./http2-interceptor.js";
|
|
48
|
+
import { clearCapturedRejection, getCapturedRejection } from "./rejection-capture.js";
|
|
49
|
+
import { newTurnStreamState } from "./turn-stream.js";
|
|
50
|
+
import { streamAndSettle, usageSnapshotFor } from "./turn-settle.js";
|
|
51
|
+
import {
|
|
52
|
+
buildTurnPrompt,
|
|
53
|
+
installGate,
|
|
54
|
+
readAdjudicatedRows,
|
|
55
|
+
resolveCursorMode,
|
|
56
|
+
resolveEngine,
|
|
57
|
+
type CursorAdapterConfig,
|
|
58
|
+
type CursorEngine,
|
|
59
|
+
type CursorGate,
|
|
60
|
+
} from "./turn-setup.js";
|
|
61
|
+
|
|
62
|
+
export async function runCursorTurn(input: TurnInput, sink: TurnSink, config: CursorAdapterConfig): Promise<TurnOutcome> {
|
|
63
|
+
const { executionId, sessionId } = input;
|
|
64
|
+
|
|
65
|
+
// Ensure fresh HTTP/2 transport — prevents a degraded session from a
|
|
66
|
+
// prior workflow task from poisoning this execution's agent stream.
|
|
67
|
+
closeProxySessions();
|
|
68
|
+
// The module-level fallback the interceptors read when no execution
|
|
69
|
+
// context is entered (the runtime enters one around the whole activity;
|
|
70
|
+
// the SDK warm-up in main.ts has none). Deprecated; an S5 footprint item.
|
|
71
|
+
setInterceptorExecutionId(executionId);
|
|
72
|
+
|
|
73
|
+
if (sink.stopSignal.aborted) return { kind: "interrupted" };
|
|
74
|
+
|
|
75
|
+
let engine: CursorEngine | undefined;
|
|
76
|
+
let gate: CursorGate | undefined;
|
|
77
|
+
let finishTurnTelemetry: (() => Promise<void>) | undefined;
|
|
78
|
+
let outcome: TurnOutcome | undefined;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// The flags the stream produces that outlive the loop: created before the
|
|
82
|
+
// gate so the denial watcher can write into them.
|
|
83
|
+
const streamState = newTurnStreamState();
|
|
84
|
+
const mode = resolveCursorMode(input, config);
|
|
85
|
+
const rows = readAdjudicatedRows(input);
|
|
86
|
+
gate = await installGate(input, sink, rows, streamState);
|
|
87
|
+
if (sink.stopSignal.aborted) return (outcome = { kind: "interrupted" });
|
|
88
|
+
|
|
89
|
+
engine = await resolveEngine(input, sink, config, mode);
|
|
90
|
+
sink.recordActivity();
|
|
91
|
+
if (sink.stopSignal.aborted) return (outcome = { kind: "interrupted" });
|
|
92
|
+
|
|
93
|
+
const prompt = await buildTurnPrompt(input, sink, engine, rows);
|
|
94
|
+
|
|
95
|
+
// The OTel turn span. Coarse-grained — spans the whole turn (agent.send +
|
|
96
|
+
// stream + any recovery retry + the turn boundary), ended once from the
|
|
97
|
+
// finally so it never leaks on a non-happy path and includes any recovery
|
|
98
|
+
// retry's tokens (read from the runtime's usage summary at end time).
|
|
99
|
+
const { startCursorTurnSpan } = await import("../../otel.js");
|
|
100
|
+
const turnSpan = await startCursorTurnSpan({ model: engine.validatedModel, mode: engine.agentMode, sessionId });
|
|
101
|
+
let turnTelemetryFinished = false;
|
|
102
|
+
const resolvedEngine = engine;
|
|
103
|
+
finishTurnTelemetry = async () => {
|
|
104
|
+
if (turnTelemetryFinished) return;
|
|
105
|
+
turnTelemetryFinished = true;
|
|
106
|
+
const usage = usageSnapshotFor(sink);
|
|
107
|
+
turnSpan.setTokens(Number(usage.inputTokens), Number(usage.outputTokens));
|
|
108
|
+
turnSpan.end();
|
|
109
|
+
try {
|
|
110
|
+
const { recordTurnMetrics } = await import("../../otel.js");
|
|
111
|
+
const startedAt = sink.status.startedAt ? new Date(sink.status.startedAt).getTime() : Date.now();
|
|
112
|
+
await recordTurnMetrics({
|
|
113
|
+
durationMs: Date.now() - startedAt,
|
|
114
|
+
inputTokens: Number(usage.inputTokens),
|
|
115
|
+
outputTokens: Number(usage.outputTokens),
|
|
116
|
+
model: resolvedEngine.validatedModel,
|
|
117
|
+
mode: resolvedEngine.agentMode,
|
|
118
|
+
});
|
|
119
|
+
} catch {
|
|
120
|
+
// Metrics not initialized — silently skip.
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
outcome = await streamAndSettle({ input, sink, config, engine, gate, prompt, rows, streamState });
|
|
125
|
+
return outcome;
|
|
126
|
+
} catch (err) {
|
|
127
|
+
outcome = await classifyThrown(err, input, config, engine);
|
|
128
|
+
return outcome;
|
|
129
|
+
} finally {
|
|
130
|
+
// End the OTel turn span + record metrics on EVERY exit path (idempotent).
|
|
131
|
+
await finishTurnTelemetry?.();
|
|
132
|
+
|
|
133
|
+
// Close the denial-ledger watcher on EVERY exit path (idempotent) so no
|
|
134
|
+
// orphaned fs.watch handle survives the turn.
|
|
135
|
+
gate?.stopDenialWatcher();
|
|
136
|
+
|
|
137
|
+
// Tear down the HITL gate on EVERY exit path (success, error, approval
|
|
138
|
+
// pause, stop) so attaching a real repo leaves the user's .cursor/hooks.json
|
|
139
|
+
// untouched between turns (issue #173). Best-effort: a leftover hooks.json
|
|
140
|
+
// is inert because the scope guard allows all invocations once this
|
|
141
|
+
// runner PID is gone. The `.stigmer` link is the runtime's to remove,
|
|
142
|
+
// after this.
|
|
143
|
+
if (gate) {
|
|
144
|
+
try {
|
|
145
|
+
await gate.removeGate();
|
|
146
|
+
} catch (cleanupErr) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`ExecuteCursor HITL gate teardown failed (non-fatal): ` +
|
|
149
|
+
`execution=${executionId}, error=${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (engine) disposeEngine(engine, sessionId, outcome);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* What becomes of the agent handle (see the header). Parking keeps the
|
|
160
|
+
* executor lease (and its MCP subprocesses) warm for the session's next
|
|
161
|
+
* turn; the idle TTL / shutdown hooks in agent-session-cache own the
|
|
162
|
+
* eventual release, so cache buildup across sessions stays bounded (#215).
|
|
163
|
+
*/
|
|
164
|
+
function disposeEngine(engine: CursorEngine, sessionId: string, outcome: TurnOutcome | undefined): void {
|
|
165
|
+
const park = (): void => cacheSessionAgent(sessionId, engine.resolution.agent, engine.agentFingerprint);
|
|
166
|
+
const close = (): void => {
|
|
167
|
+
try {
|
|
168
|
+
engine.resolution.agent.close();
|
|
169
|
+
} catch {
|
|
170
|
+
/* best effort */
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
if (outcome === undefined) {
|
|
174
|
+
// No outcome means the catch itself threw; nothing sane to do with the handle.
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
switch (outcome.kind) {
|
|
178
|
+
case "completed":
|
|
179
|
+
case "cancelled":
|
|
180
|
+
case "awaiting_approval":
|
|
181
|
+
case "interrupted":
|
|
182
|
+
park();
|
|
183
|
+
return;
|
|
184
|
+
case "failed":
|
|
185
|
+
switch (outcome.surface) {
|
|
186
|
+
case "engine":
|
|
187
|
+
park();
|
|
188
|
+
return;
|
|
189
|
+
case "actionable":
|
|
190
|
+
close();
|
|
191
|
+
return;
|
|
192
|
+
case "internal":
|
|
193
|
+
return;
|
|
194
|
+
default: {
|
|
195
|
+
const exhaustive: never = outcome.surface;
|
|
196
|
+
throw new Error(`cursor adapter: unknown failure surface ${String(exhaustive)}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
default: {
|
|
200
|
+
const exhaustive: never = outcome;
|
|
201
|
+
throw new Error(`cursor adapter: unknown outcome ${JSON.stringify(exhaustive)}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* An exception that escaped the turn, classified: a thrown `CursorSdkError`
|
|
208
|
+
* carries structured fields (code/status/endpoint/requestId) the generic
|
|
209
|
+
* description would flatten, so it goes through the same classifier as the
|
|
210
|
+
* `run.wait()` error path; anything else is described by
|
|
211
|
+
* `describeExecutionError` (a model error arrives MiddlewareError-wrapped
|
|
212
|
+
* with raw provider prose; non-model errors keep the root error's identity).
|
|
213
|
+
* Both are the `internal` surface: the runner or its transport broke.
|
|
214
|
+
*/
|
|
215
|
+
async function classifyThrown(err: unknown, input: TurnInput, config: CursorAdapterConfig, engine: CursorEngine | undefined): Promise<TurnOutcome> {
|
|
216
|
+
const { executionId } = input;
|
|
217
|
+
const fallbackContext = engine
|
|
218
|
+
? { model: engine.validatedModel, mode: engine.agentMode, agentId: engine.resolution.agentId }
|
|
219
|
+
: { model: input.model.requested, mode: "local", agentId: "" };
|
|
220
|
+
|
|
221
|
+
const { CursorSdkError } = await import("@cursor/sdk");
|
|
222
|
+
if (err instanceof CursorSdkError) {
|
|
223
|
+
console.error(`ExecuteCursor SDK error: execution=${executionId}, sdkError=${JSON.stringify(err.toJSON())}`);
|
|
224
|
+
const classified = synthesizeError({
|
|
225
|
+
sdkError: { code: err.code, status: err.status, message: err.message },
|
|
226
|
+
sdkResultFields: undefined,
|
|
227
|
+
streamErrorMessage: undefined,
|
|
228
|
+
capturedRejection: getCapturedRejection(executionId),
|
|
229
|
+
isResumedHandle: false,
|
|
230
|
+
fallbackContext,
|
|
231
|
+
proxyMode: !!config.proxyEndpoint,
|
|
232
|
+
});
|
|
233
|
+
clearCapturedRejection(executionId);
|
|
234
|
+
return { kind: "failed", surface: "internal", message: formatClassifiedError(classified), cause: err };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const { errorType, errorMessage } = describeExecutionError(err, { proxyMode: !!config.proxyEndpoint });
|
|
238
|
+
console.error(`ExecuteCursor failed: execution=${executionId}, [${errorType}] ${errorMessage}`);
|
|
239
|
+
return { kind: "failed", surface: "internal", message: `[${errorType}] ${errorMessage}`, cause: err };
|
|
240
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cursor harness's half of usage accounting: pricing one `turn-ended`
|
|
3
|
+
* delta at the rates of the variant this turn requested, and naming that
|
|
4
|
+
* basis on the priced delta the runtime accumulates.
|
|
5
|
+
*
|
|
6
|
+
* The runtime cannot price without the vendor's table and variant semantics,
|
|
7
|
+
* and it must not import them (Q-S2-12), so the adapter hands
|
|
8
|
+
* `TurnSink.reportUsage` a delta that already carries `estimatedCostUsd`
|
|
9
|
+
* and the basis (`model`, the catalog-validated id; `requestedModelParams`,
|
|
10
|
+
* the JSON of the params sent with every create/resume) the runtime writes
|
|
11
|
+
* into `streaming_usage`. Split out of the accumulator at S2 M3
|
|
12
|
+
* (`harness/usage-accumulator.ts` keeps the sums); the arithmetic and the
|
|
13
|
+
* rounding are unchanged, so the estimate is byte-for-byte the old one.
|
|
14
|
+
*
|
|
15
|
+
* Estimate at the rates of the variant we explicitly requested: a FAST run
|
|
16
|
+
* priced at base rates would understate the display estimate ~4x relative
|
|
17
|
+
* to the authoritative bill (#357). Thinking is price-neutral (#772) and
|
|
18
|
+
* never enters the estimate.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { ModelParameterValue } from "@cursor/sdk";
|
|
22
|
+
import { ServiceTier } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
23
|
+
|
|
24
|
+
import type { UsageDelta } from "../../harness/types.js";
|
|
25
|
+
import { getCursorModelPricingForVariant, computeTurnCost } from "./model-pricing.js";
|
|
26
|
+
|
|
27
|
+
/** The token counts of one `turn-ended` delta as the Cursor SDK reports them. */
|
|
28
|
+
export interface TurnUsage {
|
|
29
|
+
readonly inputTokens?: number;
|
|
30
|
+
readonly outputTokens?: number;
|
|
31
|
+
readonly cacheReadTokens?: number;
|
|
32
|
+
readonly cacheWriteTokens?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class CursorUsagePricer {
|
|
36
|
+
/** JSON-encoded params the runner sent with the model selection (#357); "" when none. */
|
|
37
|
+
private readonly requestedModelParams: string;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private readonly model: string,
|
|
41
|
+
private readonly requestedServiceTier: ServiceTier,
|
|
42
|
+
requestedModelParams: readonly ModelParameterValue[] = [],
|
|
43
|
+
) {
|
|
44
|
+
this.requestedModelParams = requestedModelParams.length > 0 ? JSON.stringify(requestedModelParams) : "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One SDK usage delta, priced and attributed, ready for `TurnSink.reportUsage`. */
|
|
48
|
+
price(usage: TurnUsage): UsageDelta {
|
|
49
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
50
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
51
|
+
const cacheReadTokens = usage.cacheReadTokens ?? 0;
|
|
52
|
+
const cacheWriteTokens = usage.cacheWriteTokens ?? 0;
|
|
53
|
+
const pricing = getCursorModelPricingForVariant(
|
|
54
|
+
this.model,
|
|
55
|
+
this.requestedServiceTier === ServiceTier.FAST ? "fast" : null,
|
|
56
|
+
);
|
|
57
|
+
return {
|
|
58
|
+
inputTokens,
|
|
59
|
+
outputTokens,
|
|
60
|
+
cacheReadTokens,
|
|
61
|
+
cacheWriteTokens,
|
|
62
|
+
estimatedCostUsd: computeTurnCost(pricing, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens),
|
|
63
|
+
model: this.model,
|
|
64
|
+
requestedModelParams: this.requestedModelParams,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* few fields). The approval card renders the proposed write/edit content from
|
|
10
10
|
* these args; there is no separate captured `file_changes` (removed in Phase 5
|
|
11
11
|
* Slice 4 — the args are the single source, and the Cursor deny-gate's
|
|
12
|
-
* exact-apply reads the same args on resume; see
|
|
12
|
+
* exact-apply reads the same args on resume; see shared/exact-apply.ts).
|
|
13
13
|
*
|
|
14
14
|
* @since First-Class Diff Review (#186), approval-gate phase
|
|
15
15
|
*/
|
|
@@ -42,7 +42,7 @@ export async function resolveEnvironment(
|
|
|
42
42
|
execCtx = await client.getExecutionContextByExecutionId(executionId, scopedToken);
|
|
43
43
|
} catch (err: unknown) {
|
|
44
44
|
// ConnectError uses numeric Code.NotFound (5); match the pattern
|
|
45
|
-
// from
|
|
45
|
+
// from shared/env-resolver.ts.
|
|
46
46
|
const code = (err as { code?: number | string })?.code;
|
|
47
47
|
if (code === 5 || code === "not_found" || code === "NOT_FOUND") {
|
|
48
48
|
console.log(
|
|
@@ -1006,8 +1006,9 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
1006
1006
|
*
|
|
1007
1007
|
* A session with no entries gets its own empty per-session directory (see
|
|
1008
1008
|
* shared/workspace/session-root.ts) — never the shared root, which would
|
|
1009
|
-
* leak other sessions' files into it. Mirrors
|
|
1010
|
-
*
|
|
1009
|
+
* leak other sessions' files into it. Mirrors shared/workspace/
|
|
1010
|
+
* session-provision.ts `provisionSessionWorkspace` exactly; S3 of the
|
|
1011
|
+
* harness runtime program retires this twin in favor of that one.
|
|
1011
1012
|
*/
|
|
1012
1013
|
async function provisionWorkspace(
|
|
1013
1014
|
config: Config,
|
|
@@ -282,7 +282,7 @@ async function fetchAndFlattenEnv(
|
|
|
282
282
|
execCtx = await client.getExecutionContextByExecutionId(executionId, scopedToken);
|
|
283
283
|
} catch (err: unknown) {
|
|
284
284
|
// ConnectError uses numeric Code.NotFound (5); match the pattern
|
|
285
|
-
// from
|
|
285
|
+
// from shared/env-resolver.ts.
|
|
286
286
|
const code = (err as { code?: number | string })?.code;
|
|
287
287
|
if (code === 5 || code === "not_found" || code === "NOT_FOUND") {
|
|
288
288
|
console.log(
|
package/src/config.ts
CHANGED
|
@@ -143,6 +143,19 @@ export interface Config {
|
|
|
143
143
|
* desktop runner (see stigmer-client.ts).
|
|
144
144
|
*/
|
|
145
145
|
readonly stigmerRunnerTokenRef?: { current: string | null };
|
|
146
|
+
/**
|
|
147
|
+
* Shared mutable reference to the credential the Cursor proxy transport
|
|
148
|
+
* sends as `x-stigmer-auth` (the fetch and HTTP/2 interceptors read it per
|
|
149
|
+
* request). Each composition root binds its own credential here, because
|
|
150
|
+
* the two differ: the static root binds the control-plane token (its host
|
|
151
|
+
* provides the proxy credential and mints no other, so
|
|
152
|
+
* `proxyTokenRef === stigmerTokenRef`), the manager binds the minted runner
|
|
153
|
+
* token (`proxyTokenRef === stigmerRunnerTokenRef`). Reading either sibling
|
|
154
|
+
* directly would send the wrong credential from one of the two roots
|
|
155
|
+
* (Q-S2-7). Required whenever `proxyEndpoint` is set; the Cursor adapter's
|
|
156
|
+
* boot refuses a proxy without it.
|
|
157
|
+
*/
|
|
158
|
+
readonly proxyTokenRef?: { current: string | null };
|
|
146
159
|
}
|
|
147
160
|
|
|
148
161
|
export function loadConfig(): Config {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the import direction the turn runtime stands on: nothing under
|
|
3
|
+
* `src/harness/` imports from `src/activities/`, and no production module
|
|
4
|
+
* under `src/shared/` does either.
|
|
5
|
+
*
|
|
6
|
+
* Why it matters (the extraction plan, section 3): the runtime is meant to
|
|
7
|
+
* be written once and call harness-specific code only through the adapter
|
|
8
|
+
* contract. If `harness/` could reach into `activities/execute-cursor/`, the
|
|
9
|
+
* runtime would be Cursor's orchestrator in a new directory and the "SDK
|
|
10
|
+
* slice plus a registry row" promise for the next harness would be false.
|
|
11
|
+
* The `shared/` half is what makes the `harness/` half hold: `harness/`
|
|
12
|
+
* imports `shared/` freely, so a `shared/` module that imported
|
|
13
|
+
* `activities/` would open the same door one hop away.
|
|
14
|
+
*
|
|
15
|
+
* The rule is read off the TypeScript syntax tree, not a regex
|
|
16
|
+
* (`__test-utils__/module-specifiers.ts`, shared with the adapter's
|
|
17
|
+
* Temporal-free fence), so a path quoted in a header comment (several
|
|
18
|
+
* `shared/` modules cite `activities/...` in prose) cannot trip it, and no
|
|
19
|
+
* import form can slip past it: static, type-only, side-effect, re-export
|
|
20
|
+
* and dynamic `import()` are all module specifiers to the parser. Ruled at
|
|
21
|
+
* the S2 gate (Q-S2-9); the `shared/` half ruled at the M1 gate (Q-M1-5).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { describe, it, expect } from "vitest";
|
|
25
|
+
import { join, relative, sep } from "node:path";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
|
|
28
|
+
import { forbiddenImports, readSource, typeScriptFilesUnder } from "../../__test-utils__/module-specifiers.js";
|
|
29
|
+
|
|
30
|
+
const SRC_ROOT = fileURLToPath(new URL("../../", import.meta.url));
|
|
31
|
+
const FORBIDDEN_ROOT = join(SRC_ROOT, "activities");
|
|
32
|
+
|
|
33
|
+
interface Offence {
|
|
34
|
+
readonly file: string;
|
|
35
|
+
readonly specifier: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface Sweep {
|
|
39
|
+
/** Files inspected, relative to `src/`; the liveness guards read this. */
|
|
40
|
+
readonly visited: string[];
|
|
41
|
+
readonly offences: Offence[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sweep(root: string, skipDirectories: ReadonlySet<string>): Sweep {
|
|
45
|
+
const visited: string[] = [];
|
|
46
|
+
const offences: Offence[] = [];
|
|
47
|
+
for (const file of typeScriptFilesUnder(root, skipDirectories)) {
|
|
48
|
+
const relativeFile = relative(SRC_ROOT, file);
|
|
49
|
+
visited.push(relativeFile);
|
|
50
|
+
for (const specifier of forbiddenImports(file, readSource(file), FORBIDDEN_ROOT)) {
|
|
51
|
+
offences.push({ file: relativeFile, specifier });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { visited, offences };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function describeOffences(offences: readonly Offence[]): string {
|
|
58
|
+
return offences.map((o) => ` ${o.file} imports "${o.specifier}"`).join("\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe("forbiddenImports (the checker itself)", () => {
|
|
62
|
+
const file = join(SRC_ROOT, "harness", "run-turn.ts");
|
|
63
|
+
|
|
64
|
+
it.each([
|
|
65
|
+
[
|
|
66
|
+
"a static import",
|
|
67
|
+
'import { a } from "../activities/execute-cursor/x.js";',
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"a type-only import",
|
|
71
|
+
'import type { A } from "../activities/execute-cursor/x.js";',
|
|
72
|
+
],
|
|
73
|
+
["a side-effect import", 'import "../activities/execute-cursor/x.js";'],
|
|
74
|
+
["a re-export", 'export { a } from "../activities/execute-cursor/x.js";'],
|
|
75
|
+
["a star re-export", 'export * from "../activities/x.js";'],
|
|
76
|
+
[
|
|
77
|
+
"a dynamic import",
|
|
78
|
+
'const m = await import("../activities/execute-cursor/x.js");',
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
"a path that leaves and re-enters",
|
|
82
|
+
'import { a } from "../shared/../activities/x.js";',
|
|
83
|
+
],
|
|
84
|
+
])("flags %s", (_label, source) => {
|
|
85
|
+
expect(forbiddenImports(file, source, FORBIDDEN_ROOT)).toHaveLength(1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it.each([
|
|
89
|
+
["a shared import", 'import { a } from "../shared/status.js";'],
|
|
90
|
+
["a sibling import", 'import { a } from "./types.js";'],
|
|
91
|
+
["a package import", 'import { create } from "@bufbuild/protobuf";'],
|
|
92
|
+
["a node builtin", 'import { join } from "node:path";'],
|
|
93
|
+
[
|
|
94
|
+
"a forbidden path quoted in a comment",
|
|
95
|
+
'// see: import { a } from "../activities/execute-cursor/x.js"\nimport { b } from "./types.js";',
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"a forbidden path in a plain string",
|
|
99
|
+
'const hint = "../activities/execute-cursor/x.js";',
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"a dynamic import whose specifier is not a literal",
|
|
103
|
+
"const m = await import(pathFromConfig);",
|
|
104
|
+
],
|
|
105
|
+
])("does not flag %s", (_label, source) => {
|
|
106
|
+
expect(forbiddenImports(file, source, FORBIDDEN_ROOT)).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("names every offending specifier when a file has several", () => {
|
|
110
|
+
const source = [
|
|
111
|
+
'import { a } from "../activities/a.js";',
|
|
112
|
+
'import { b } from "../shared/b.js";',
|
|
113
|
+
'export { c } from "../activities/c.js";',
|
|
114
|
+
].join("\n");
|
|
115
|
+
expect(forbiddenImports(file, source, FORBIDDEN_ROOT)).toEqual([
|
|
116
|
+
"../activities/a.js",
|
|
117
|
+
"../activities/c.js",
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("src/harness never imports src/activities", () => {
|
|
123
|
+
const result = sweep(join(SRC_ROOT, "harness"), new Set());
|
|
124
|
+
|
|
125
|
+
it("inspected the runtime's own files (the walk is rooted where it claims)", () => {
|
|
126
|
+
expect(result.visited).toEqual(
|
|
127
|
+
expect.arrayContaining([
|
|
128
|
+
join("harness", "registry.ts"),
|
|
129
|
+
join("harness", "types.ts"),
|
|
130
|
+
join("harness", "capabilities.ts"),
|
|
131
|
+
]),
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("finds no import into src/activities, tests included", () => {
|
|
136
|
+
expect(
|
|
137
|
+
result.offences,
|
|
138
|
+
`src/harness/ must not import from src/activities/:\n${describeOffences(result.offences)}`,
|
|
139
|
+
).toEqual([]);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("src/shared production modules never import src/activities", () => {
|
|
144
|
+
// Tests under shared/__tests__ may legitimately import both harnesses'
|
|
145
|
+
// implementations to compare them (attachment-naming.test.ts does); the
|
|
146
|
+
// rule is about what production code can reach.
|
|
147
|
+
const result = sweep(join(SRC_ROOT, "shared"), new Set(["__tests__"]));
|
|
148
|
+
|
|
149
|
+
it("inspected the shared modules (the walk is rooted where it claims)", () => {
|
|
150
|
+
expect(result.visited).toEqual(
|
|
151
|
+
expect.arrayContaining([
|
|
152
|
+
join("shared", "status.ts"),
|
|
153
|
+
join("shared", "workspace", "session-provision.ts"),
|
|
154
|
+
]),
|
|
155
|
+
);
|
|
156
|
+
expect(
|
|
157
|
+
result.visited.some((f) => f.includes(`${sep}__tests__${sep}`)),
|
|
158
|
+
"the production sweep must skip __tests__ directories",
|
|
159
|
+
).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("finds no import into src/activities", () => {
|
|
163
|
+
expect(
|
|
164
|
+
result.offences,
|
|
165
|
+
`src/shared/ production code must not import from src/activities/:\n${describeOffences(result.offences)}`,
|
|
166
|
+
).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
});
|