@stigmer/runner 3.14.1 → 3.15.1
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 +28 -5
- package/dist/__test-utils__/hermetic-activity.js +23 -7
- package/dist/__test-utils__/hermetic-activity.js.map +1 -1
- 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 +38 -62
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +47 -121
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +17 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -4
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +52 -11
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +66 -7
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
- 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 +16 -0
- package/dist/harness/capabilities.js +1 -0
- package/dist/harness/capabilities.js.map +1 -1
- 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 +44 -5
- package/dist/harness/registry.js +61 -5
- package/dist/harness/registry.js.map +1 -1
- 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 +232 -52
- package/dist/harness/types.js +17 -8
- package/dist/harness/types.js.map +1 -1
- 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 +110 -14
- 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 +128 -83
- package/src/__test-utils__/harness-contract/recording-sink.ts +15 -4
- package/src/__test-utils__/harness-contract/runtime-contract.ts +666 -0
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +92 -32
- package/src/__test-utils__/harness-contract/types.ts +85 -9
- package/src/__test-utils__/hermetic-activity.ts +33 -8
- package/src/__test-utils__/model-registry-fixture.ts +72 -0
- package/src/__test-utils__/module-specifiers.ts +82 -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 +7 -5
- 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 +65 -145
- package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +30 -4
- package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +96 -8
- 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 +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/cost-cap.status.json +53 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/pause.status.json +1 -6
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/platform-stop.status.json +56 -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/unattributed-hook-block.status.json +79 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/worker-shutdown.status.json +2 -7
- 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 +22 -18
- package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +1 -1
- 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 +1 -1
- 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 +1 -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 +17 -0
- package/src/harness/persist-chokepoint.ts +112 -0
- package/src/harness/registry.ts +79 -5
- 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 +248 -53
- 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,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cursor harness as a `HarnessAdapter` — the worker- and session-lifetime
|
|
3
|
+
* half of its SDK slice, behind the turn runtime's contract
|
|
4
|
+
* (`harness/types.ts`). One engine turn is `turn.ts`'s `runCursorTurn`; this
|
|
5
|
+
* module boots, releases, and delegates.
|
|
6
|
+
*
|
|
7
|
+
* What this adapter owns: the proxy interceptors and the parked-agent cache
|
|
8
|
+
* (worker lifetime), the parked agent per session (session lifetime), and one
|
|
9
|
+
* engine turn (`runTurn`, through `turn.ts`). Everything else about a turn is
|
|
10
|
+
* the runtime's (`harness/run-turn.ts`).
|
|
11
|
+
*
|
|
12
|
+
* THIS MODULE'S STATIC GRAPH IS SDK-FREE, ON PURPOSE. The composition roots
|
|
13
|
+
* import it (through `src/harness-adapters.ts`) BEFORE they boot it, and
|
|
14
|
+
* `boot` is where the fetch and HTTP/2 interceptors are installed. The HTTP/2
|
|
15
|
+
* one patches `node:http2`, whose ESM facade is snapshotted the first time
|
|
16
|
+
* `@connectrpc/connect-node` is imported — and `@cursor/sdk` imports
|
|
17
|
+
* connect-node. So the SDK-touching half of this harness (`turn.ts` and,
|
|
18
|
+
* through it, `turn-setup.ts` and its siblings) is loaded with a dynamic
|
|
19
|
+
* import INSIDE `boot`, after the installs and after
|
|
20
|
+
* `assertHttp2ConnectPatched` has proven the facade sees the patch. This is
|
|
21
|
+
* the contract's own rule ("vendor SDKs are imported lazily inside" boot,
|
|
22
|
+
* `types.ts`), and the runner's dynamic-import-for-order convention
|
|
23
|
+
* (`bootstrap.ts`). `src/__tests__/harness-boot-order.test.ts` boots a fresh
|
|
24
|
+
* process through this path and fails if the graph regresses; the modules
|
|
25
|
+
* this file imports statically must each stay connect-free.
|
|
26
|
+
*
|
|
27
|
+
* The interceptors read the proxy credential from `Config.proxyTokenRef` per
|
|
28
|
+
* request; which credential that is (the static root's control-plane token,
|
|
29
|
+
* the manager's minted runner token) is the root's decision, not this
|
|
30
|
+
* module's (Q-S2-7).
|
|
31
|
+
*
|
|
32
|
+
* What this adapter never does: throw out of `runTurn` (an SDK failure is
|
|
33
|
+
* `failed` with the classifier's sentence; an unexpected exception is
|
|
34
|
+
* `failed` on the `internal` surface; a `CancelledFailure` never exists
|
|
35
|
+
* here), branch on why `stopSignal` aborted, write a phase or a terminal
|
|
36
|
+
* copy, or import `@temporalio/*` (`__tests__/adapter-is-temporal-free.test.ts`
|
|
37
|
+
* pins it, which is what lets the contract kit run this adapter outside any
|
|
38
|
+
* activity context).
|
|
39
|
+
*
|
|
40
|
+
* The file-review boundary (`turn-boundary.ts`) runs whole inside `turn.ts`
|
|
41
|
+
* for S2 (Q-S2-1): five of its six steps read the denial ledger, this
|
|
42
|
+
* harness's deny-and-retry primitive. S3 lifts the capture into the runtime
|
|
43
|
+
* with both harnesses' captures in view; `types.ts`'s "the file-review
|
|
44
|
+
* boundary is the runtime's" becomes true then.
|
|
45
|
+
*
|
|
46
|
+
* Extracted from `index.ts` `createCursorActivities` at S2 M3b; split from
|
|
47
|
+
* the turn body at S2 M5. The seventeen hermetic goldens under
|
|
48
|
+
* `__tests__/hermetic/` pin the result byte for byte.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
import type { Config } from "../../config.js";
|
|
52
|
+
import type { HarnessAdapter, TurnInput, TurnOutcome, TurnSink } from "../../harness/types.js";
|
|
53
|
+
import { markBoot } from "../../shared/cold-start-timing.js";
|
|
54
|
+
import { closeAllCachedAgents, evictSessionAgent } from "./agent-session-cache.js";
|
|
55
|
+
import { CURSOR_CAPABILITIES } from "./cursor-capabilities.js";
|
|
56
|
+
import { installFetchInterceptor } from "./fetch-interceptor.js";
|
|
57
|
+
import { assertHttp2ConnectPatched, installHttp2Interceptor } from "./http2-interceptor.js";
|
|
58
|
+
import type { CursorAdapterConfig } from "./turn-setup.js";
|
|
59
|
+
|
|
60
|
+
/** The config slice this adapter reads per turn, taken at `boot`; a whole `Config` satisfies it. */
|
|
61
|
+
export function resolveCursorConfig(config: Config): CursorAdapterConfig {
|
|
62
|
+
return {
|
|
63
|
+
proxyEndpoint: config.proxyEndpoint,
|
|
64
|
+
cursorApiKey: config.cursorApiKey,
|
|
65
|
+
stigmerToken: config.stigmerToken,
|
|
66
|
+
stigmerTokenRef: config.stigmerTokenRef,
|
|
67
|
+
workspaceRootDir: config.workspaceRootDir,
|
|
68
|
+
cloudModeEnabled: config.cloudModeEnabled,
|
|
69
|
+
agentResolveTimeoutMs: config.agentResolveTimeoutMs,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** What `boot` leaves behind for the turns: the config slice and the engine turn, loaded after the interceptors. */
|
|
74
|
+
interface BootedCursorHarness {
|
|
75
|
+
readonly config: CursorAdapterConfig;
|
|
76
|
+
readonly runCursorTurn: typeof import("./turn.js").runCursorTurn;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function createCursorAdapter(): HarnessAdapter {
|
|
80
|
+
let booted: BootedCursorHarness | undefined;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
name: "cursor",
|
|
84
|
+
capabilities: CURSOR_CAPABILITIES,
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Install the proxy transport, prove the HTTP/2 patch reached the facade,
|
|
88
|
+
* then load the SDK slice — in that order, for the reason in the header.
|
|
89
|
+
* Runs before bootstrap resolution, so `bootConfig` carries no Temporal
|
|
90
|
+
* coordinates yet; every field the slice reads is known at that point.
|
|
91
|
+
* Idempotent: a re-boot rebinds the interceptors and re-reads the slice.
|
|
92
|
+
*/
|
|
93
|
+
async boot(bootConfig: Config): Promise<void> {
|
|
94
|
+
if (bootConfig.proxyEndpoint && !bootConfig.proxyTokenRef) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"cursor adapter: a proxy endpoint needs Config.proxyTokenRef (the composition root binds its proxy credential there; see config.ts)",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
// The Cursor SDK's REST client resolves `fetch` at call time and its
|
|
100
|
+
// Connect transport opens native HTTP/2 sessions; the two interceptors
|
|
101
|
+
// route the first through the proxy and stamp the second with the proxy
|
|
102
|
+
// credential and the execution id. Both are no-ops without a proxy.
|
|
103
|
+
installFetchInterceptor({
|
|
104
|
+
proxyEndpoint: bootConfig.proxyEndpoint ?? undefined,
|
|
105
|
+
proxyTokenRef: bootConfig.proxyTokenRef,
|
|
106
|
+
});
|
|
107
|
+
installHttp2Interceptor({
|
|
108
|
+
proxyEndpoint: bootConfig.proxyEndpoint ?? undefined,
|
|
109
|
+
proxyTokenRef: bootConfig.proxyTokenRef,
|
|
110
|
+
});
|
|
111
|
+
markBoot("interceptors_installed");
|
|
112
|
+
// Fail loudly at boot if a load-order regression left the node:http2 ESM
|
|
113
|
+
// facade unpatched (otherwise BiDi streams would silently 401). No-op
|
|
114
|
+
// when the interceptor is unconfigured (no proxy).
|
|
115
|
+
await assertHttp2ConnectPatched();
|
|
116
|
+
|
|
117
|
+
const { runCursorTurn } = await import("./turn.js");
|
|
118
|
+
booted = { config: resolveCursorConfig(bootConfig), runCursorTurn };
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/** Close every parked agent (their executor leases and MCP subprocesses). */
|
|
122
|
+
async shutdown(): Promise<void> {
|
|
123
|
+
closeAllCachedAgents();
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/** The session is done on this host: close the agent parked for it, if any (#215). */
|
|
127
|
+
async releaseSession(sessionId: string): Promise<void> {
|
|
128
|
+
evictSessionAgent(sessionId);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
async runTurn(input: TurnInput, sink: TurnSink): Promise<TurnOutcome> {
|
|
132
|
+
if (booted === undefined) {
|
|
133
|
+
throw new Error("cursor adapter: runTurn before boot (the registry boots every adapter before the worker polls)");
|
|
134
|
+
}
|
|
135
|
+
return booted.runCursorTurn(input, sink, booted.config);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -346,7 +346,7 @@ export function emitCursorGrantReceipts(
|
|
|
346
346
|
*/
|
|
347
347
|
export function buildApprovalGrants(
|
|
348
348
|
pendingApprovals: PendingApproval[],
|
|
349
|
-
decisions:
|
|
349
|
+
decisions: ReadonlyMap<string, ApprovalAction>,
|
|
350
350
|
contentDigests?: Map<string, string>,
|
|
351
351
|
): ApprovalGrant[] {
|
|
352
352
|
const grants: ApprovalGrant[] = [];
|
|
@@ -406,7 +406,7 @@ function parseArgs(argsPreview: string): Record<string, unknown> | undefined {
|
|
|
406
406
|
* generated hook script (from approval-policy.ts), not carried here.
|
|
407
407
|
*/
|
|
408
408
|
export function buildApprovalState(
|
|
409
|
-
mergedPolicies:
|
|
409
|
+
mergedPolicies: ReadonlyMap<string, MergedToolPolicy>,
|
|
410
410
|
globalBypass: boolean,
|
|
411
411
|
leasedCategories: ReadonlySet<ApprovalCategory>,
|
|
412
412
|
grants?: ApprovalGrant[],
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
* observational/audit records at their transcript positions, while
|
|
15
15
|
* `file_change_sets` remains the single DECISION surface.
|
|
16
16
|
*
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
17
|
+
* The resume-time reconcile is NOT here since S2 M2: the turn runtime's
|
|
18
|
+
* `reconcileReinvocation` (`harness/turn-context.ts`) calls the shared
|
|
19
|
+
* reconcile directly under {@link CURSOR_FILE_REVIEW_IDENTITY}, the one
|
|
20
|
+
* export that carries the two Cursor facts it needs. The two capture
|
|
21
|
+
* functions keep their original signatures so the activity wiring (index.ts)
|
|
22
|
+
* and the cutover tests are unchanged.
|
|
19
23
|
*/
|
|
20
24
|
|
|
21
25
|
import { readFile } from "node:fs/promises";
|
|
@@ -30,10 +34,8 @@ import { readCasObservations } from "./cas-observations.js";
|
|
|
30
34
|
import { contentDigest } from "../../shared/file-tools.js";
|
|
31
35
|
import { isToolCallRowHidden, stampFileEditRow } from "../../shared/tool-row.js";
|
|
32
36
|
import {
|
|
33
|
-
applyCaptureDecisions as sharedApplyCaptureDecisions,
|
|
34
37
|
captureBaselineToLedger as sharedCaptureBaselineToLedger,
|
|
35
38
|
captureCandidateToLedger as sharedCaptureCandidateToLedger,
|
|
36
|
-
type CaptureResumeResult,
|
|
37
39
|
} from "../../shared/filereview/capture.js";
|
|
38
40
|
import {
|
|
39
41
|
createGitProgressSubstrate,
|
|
@@ -50,8 +52,7 @@ import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-pa
|
|
|
50
52
|
import { casBlobReader, type CasPathCapture } from "../../shared/filereview/cas-substrate.js";
|
|
51
53
|
import type { GitSubstrateChange as GitCapturedChange } from "../../shared/filereview/git-substrate.js";
|
|
52
54
|
import type { ArtifactStorage } from "../../shared/artifact-storage.js";
|
|
53
|
-
|
|
54
|
-
export type { CaptureResumeResult };
|
|
55
|
+
import type { FileReviewIdentity } from "../../harness/capabilities.js";
|
|
55
56
|
|
|
56
57
|
/** The harness id the projection reads from the BASELINE payload (load-bearing). */
|
|
57
58
|
const HARNESS_ID = "cursor";
|
|
@@ -67,6 +68,17 @@ const CURSOR_RUNNER_OWNED_PATHS: readonly string[] = [
|
|
|
67
68
|
".cursor/rules/stigmer-tool-approval.mdc",
|
|
68
69
|
];
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* The two facts the runtime's resume-time reconcile needs from this harness
|
|
73
|
+
* (`harness/turn-context.ts` `reconcileReinvocation`): the same id and
|
|
74
|
+
* exclude list the capture functions below stamp and apply, so the baseline a
|
|
75
|
+
* turn authors and the reconcile a resume runs agree by construction.
|
|
76
|
+
*/
|
|
77
|
+
export const CURSOR_FILE_REVIEW_IDENTITY: FileReviewIdentity = {
|
|
78
|
+
harnessId: HARNESS_ID,
|
|
79
|
+
excludePaths: CURSOR_RUNNER_OWNED_PATHS,
|
|
80
|
+
};
|
|
81
|
+
|
|
70
82
|
// `deriveCaptureMode` is the single capture-vs-deny-gate decision, now shared by
|
|
71
83
|
// BOTH harnesses from `shared/filereview/capture.ts`. Re-exported here so existing
|
|
72
84
|
// Cursor imports (index.ts, the deny-gate-exact-apply seam test) keep resolving
|
|
@@ -326,41 +338,6 @@ async function readFileOrNull(absolutePath: string): Promise<Uint8Array | null>
|
|
|
326
338
|
}
|
|
327
339
|
}
|
|
328
340
|
|
|
329
|
-
/**
|
|
330
|
-
* Resume: reconcile the working tree to the change set's DECIDED decisions
|
|
331
|
-
* (keep approved "after" bytes, snap rejected/undecided back to baseline,
|
|
332
|
-
* hash-verified), author RECONCILED / FAILED, and release the refs. Delegates to
|
|
333
|
-
* the shared orchestration with the Cursor harness id + exclude paths.
|
|
334
|
-
*
|
|
335
|
-
* `storage` threads the CAS blob store + reader so gitignored (CAS) files in the
|
|
336
|
-
* change set reconcile from the durable manifest — approved after-blobs written
|
|
337
|
-
* (hash-verified), rejected files snapped back to their before-blobs. Omit for a
|
|
338
|
-
* git-only harness/turn; the shared CAS branch is then skipped. The read path is
|
|
339
|
-
* the same one `exact-apply` already uses (`ArtifactStorage.download`).
|
|
340
|
-
*/
|
|
341
|
-
export function applyCaptureDecisions(opts: {
|
|
342
|
-
readonly status: AgentExecutionStatus;
|
|
343
|
-
readonly gitRoot: string;
|
|
344
|
-
readonly executionId: string;
|
|
345
|
-
readonly changeSet: FileChangeSet;
|
|
346
|
-
readonly storage?: ArtifactStorage;
|
|
347
|
-
/**
|
|
348
|
-
* True (default) for a git work tree; false for a CAS-only non-git workspace
|
|
349
|
-
* (Slice 2c). When false the reconcile never consults git refs — it is driven
|
|
350
|
-
* entirely by the durable CAS manifest.
|
|
351
|
-
*/
|
|
352
|
-
readonly gitWorkspace?: boolean;
|
|
353
|
-
}): Promise<CaptureResumeResult> {
|
|
354
|
-
const { storage, ...rest } = opts;
|
|
355
|
-
return sharedApplyCaptureDecisions({
|
|
356
|
-
...rest,
|
|
357
|
-
harnessId: HARNESS_ID,
|
|
358
|
-
excludePaths: CURSOR_RUNNER_OWNED_PATHS,
|
|
359
|
-
storage,
|
|
360
|
-
readBlob: storage ? casBlobReader(storage) : undefined,
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
341
|
// ---------------------------------------------------------------------------
|
|
365
342
|
// Cursor-specific presentation
|
|
366
343
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cursor harness's capability flags — the facts about `@cursor/sdk` the
|
|
3
|
+
* turn runtime branches on, declared once (`harness/capabilities.ts` has the
|
|
4
|
+
* matrix and the meaning of each flag).
|
|
5
|
+
*
|
|
6
|
+
* - `deny-and-retry`: an out-of-process hook denies a gated tool, the
|
|
7
|
+
* denial lands in the session ledger, the run is cancelled, and the next
|
|
8
|
+
* invocation re-runs with grants (`hook-script.ts`, `approval-state.ts`).
|
|
9
|
+
* - `engine-minted`: the agent id exists only once `Agent.create` returns;
|
|
10
|
+
* the adapter binds it through `TurnSink.bindHarnessState` before its
|
|
11
|
+
* first persist.
|
|
12
|
+
* - No system prompt: instructions ride the first user message
|
|
13
|
+
* (`prompt-builder.ts`).
|
|
14
|
+
* - Sub-agents through the SDK's `agents` option (`subagent-config.ts`).
|
|
15
|
+
* - No tool restriction in the SDK config: the hook's "disabled" arm
|
|
16
|
+
* enforces `enabledTools` at call time (issue #350).
|
|
17
|
+
* - PNG and JPEG inline; the transport re-sniffs (`shared/attachment-vision.ts`).
|
|
18
|
+
* - File review under the `cursor` harness id, with the transient
|
|
19
|
+
* `.cursor/hooks.json` the gate writes excluded from every diff
|
|
20
|
+
* (`capture-flow.ts`).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { HarnessCapabilities } from "../../harness/capabilities.js";
|
|
24
|
+
import { CURSOR_VISION_PROFILE } from "../../shared/attachment-vision.js";
|
|
25
|
+
import { CURSOR_FILE_REVIEW_IDENTITY } from "./capture-flow.js";
|
|
26
|
+
|
|
27
|
+
export const CURSOR_CAPABILITIES: HarnessCapabilities = {
|
|
28
|
+
pausePrimitive: "deny-and-retry",
|
|
29
|
+
stateIdSource: "engine-minted",
|
|
30
|
+
systemPrompt: false,
|
|
31
|
+
subAgents: true,
|
|
32
|
+
toolRestriction: false,
|
|
33
|
+
visionProfile: CURSOR_VISION_PROFILE,
|
|
34
|
+
fileReview: CURSOR_FILE_REVIEW_IDENTITY,
|
|
35
|
+
};
|
|
@@ -42,7 +42,7 @@ export type CursorMcpServerConfig =
|
|
|
42
42
|
* expressed here — the HITL hook enforces it (see ResolvedMcpServer.enabledTools).
|
|
43
43
|
*/
|
|
44
44
|
export function toCursorMcpConfig(
|
|
45
|
-
servers: ResolvedMcpServer[],
|
|
45
|
+
servers: readonly ResolvedMcpServer[],
|
|
46
46
|
): Record<string, CursorMcpServerConfig> {
|
|
47
47
|
const result: Record<string, CursorMcpServerConfig> = {};
|
|
48
48
|
|
|
@@ -75,7 +75,7 @@ export function toCursorMcpConfig(
|
|
|
75
75
|
* Used as a pre-flight check before agent.send() to surface config issues early.
|
|
76
76
|
*/
|
|
77
77
|
export function validateMcpServerEnv(
|
|
78
|
-
servers: ResolvedMcpServer[],
|
|
78
|
+
servers: readonly ResolvedMcpServer[],
|
|
79
79
|
usages: McpServerUsage[],
|
|
80
80
|
): string[] {
|
|
81
81
|
const warnings: string[] = [];
|
|
@@ -9,15 +9,17 @@
|
|
|
9
9
|
* This is the JavaScript-level equivalent of LangChain's base_url parameter
|
|
10
10
|
* that the Python agent-runner uses for the LLM proxy pattern.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Installed by the Cursor adapter's `boot` (adapter.ts), which the registry
|
|
13
|
+
* runs in both composition roots before Temporal coordinates are resolved and
|
|
14
|
+
* before the adapter loads `@cursor/sdk`, so the interceptor is in place
|
|
15
|
+
* before the SDK issues its first request. The proxy credential is read per
|
|
16
|
+
* request from the ref the root bound as `Config.proxyTokenRef`.
|
|
14
17
|
*
|
|
15
18
|
* When STIGMER_PROXY_ENDPOINT is not set, this module is a no-op.
|
|
16
19
|
*/
|
|
17
20
|
|
|
18
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
19
|
-
|
|
20
21
|
import { TimingRecorder, emitTimingLog } from "../../shared/cold-start-timing.js";
|
|
22
|
+
import { getExecutionContext } from "../../shared/execution-context.js";
|
|
21
23
|
|
|
22
24
|
/** One REST path's timing identity: the emitted timeline event and its
|
|
23
25
|
* single segment name. */
|
|
@@ -56,22 +58,37 @@ const CURSOR_DOMAINS = [
|
|
|
56
58
|
|
|
57
59
|
interface ProxyConfig {
|
|
58
60
|
proxyEndpoint: string;
|
|
59
|
-
|
|
61
|
+
/**
|
|
62
|
+
* The proxy credential, read on EVERY request (never copied): the
|
|
63
|
+
* composition root that installed this interceptor owns the ref and
|
|
64
|
+
* rotates it in place (the static root's sandbox-token renewal, the
|
|
65
|
+
* manager's token coordinator), so a rotation reaches the next request
|
|
66
|
+
* without a call into this module. Which credential it is differs per
|
|
67
|
+
* root — the static root's control-plane token, the manager's minted
|
|
68
|
+
* runner token (Q-S2-7) — and this module never knows which.
|
|
69
|
+
*/
|
|
70
|
+
proxyTokenRef: { readonly current: string | null };
|
|
60
71
|
executionId?: string;
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
/**
|
|
75
|
+
* The `Authorization`-style header value for the current proxy credential,
|
|
76
|
+
* or undefined when the ref is empty. Empty is unreachable by construction
|
|
77
|
+
* today (neither root ever writes null into the ref it bound), so the request
|
|
78
|
+
* goes out without the header and the proxy's 401 is the diagnostic; a
|
|
79
|
+
* runner-side throw here would surface inside the SDK's transport, a path
|
|
80
|
+
* nothing exercises.
|
|
81
|
+
*/
|
|
82
|
+
function bearer(config: ProxyConfig): string | undefined {
|
|
83
|
+
const token = config.proxyTokenRef.current;
|
|
84
|
+
return token === null ? undefined : `Bearer ${token}`;
|
|
68
85
|
}
|
|
69
86
|
|
|
70
|
-
|
|
87
|
+
let interceptorConfig: ProxyConfig | null = null;
|
|
88
|
+
const originalFetch = globalThis.fetch;
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
90
|
+
/** The execution-scoped store the headers below are stamped from (`shared/execution-context.ts`). */
|
|
91
|
+
const executionContext = getExecutionContext();
|
|
75
92
|
|
|
76
93
|
/**
|
|
77
94
|
* Connect RPC path prefixes used by the Cursor SDK. Most requests with
|
|
@@ -193,7 +210,11 @@ function rewriteUrl(originalUrl: string, proxyEndpoint: string): string {
|
|
|
193
210
|
*/
|
|
194
211
|
function replaceAuth(init: RequestInit | undefined, config: ProxyConfig): RequestInit {
|
|
195
212
|
const headers = new Headers(init?.headers);
|
|
196
|
-
|
|
213
|
+
const auth = bearer(config);
|
|
214
|
+
// Replace, never leak: with no proxy credential the SDK's own authorization
|
|
215
|
+
// (a Cursor credential) must still not reach the proxy.
|
|
216
|
+
if (auth !== undefined) headers.set("authorization", auth);
|
|
217
|
+
else headers.delete("authorization");
|
|
197
218
|
const ctx = executionContext.getStore();
|
|
198
219
|
const effectiveExecutionId = ctx?.executionId ?? config.executionId;
|
|
199
220
|
if (effectiveExecutionId) {
|
|
@@ -210,7 +231,8 @@ function replaceAuth(init: RequestInit | undefined, config: ProxyConfig): Reques
|
|
|
210
231
|
*/
|
|
211
232
|
function injectProxyAuth(init: RequestInit | undefined, config: ProxyConfig): RequestInit {
|
|
212
233
|
const headers = new Headers(init?.headers);
|
|
213
|
-
|
|
234
|
+
const auth = bearer(config);
|
|
235
|
+
if (auth !== undefined) headers.set("x-stigmer-auth", auth);
|
|
214
236
|
const ctx = executionContext.getStore();
|
|
215
237
|
const effectiveExecutionId = ctx?.executionId ?? config.executionId;
|
|
216
238
|
if (effectiveExecutionId) {
|
|
@@ -362,21 +384,26 @@ async function fetchWithProxyAuth(
|
|
|
362
384
|
}
|
|
363
385
|
|
|
364
386
|
/**
|
|
365
|
-
* Install the global fetch interceptor
|
|
366
|
-
*
|
|
387
|
+
* Install the global fetch interceptor, at boot, BEFORE `@cursor/sdk` is
|
|
388
|
+
* imported. Idempotent: a second install rebinds the config (a worker may
|
|
389
|
+
* re-boot its harnesses) and never captures the interceptor itself as the
|
|
390
|
+
* original fetch.
|
|
367
391
|
*
|
|
368
392
|
* When proxyEndpoint is empty or not provided, the interceptor is not
|
|
369
393
|
* installed and all fetch calls pass through to the original implementation.
|
|
394
|
+
* A proxy endpoint with no credential bound at install is a configuration
|
|
395
|
+
* defect and fails the boot here, the same failure as before the ref (the
|
|
396
|
+
* ref is read per request afterwards; only its presence is judged now).
|
|
370
397
|
*/
|
|
371
398
|
export function installFetchInterceptor(config: {
|
|
372
399
|
proxyEndpoint: string | undefined;
|
|
373
|
-
|
|
400
|
+
proxyTokenRef: { readonly current: string | null } | undefined;
|
|
374
401
|
}): void {
|
|
375
402
|
if (!config.proxyEndpoint) {
|
|
376
403
|
return;
|
|
377
404
|
}
|
|
378
405
|
|
|
379
|
-
if (!config.
|
|
406
|
+
if (!config.proxyTokenRef || config.proxyTokenRef.current === null) {
|
|
380
407
|
throw new Error(
|
|
381
408
|
"STIGMER_TOKEN is required when STIGMER_PROXY_ENDPOINT is set. " +
|
|
382
409
|
"In proxy mode, the runner authenticates with Stigmer's proxy using STIGMER_TOKEN.",
|
|
@@ -385,7 +412,7 @@ export function installFetchInterceptor(config: {
|
|
|
385
412
|
|
|
386
413
|
interceptorConfig = {
|
|
387
414
|
proxyEndpoint: config.proxyEndpoint,
|
|
388
|
-
|
|
415
|
+
proxyTokenRef: config.proxyTokenRef,
|
|
389
416
|
};
|
|
390
417
|
|
|
391
418
|
globalThis.fetch = interceptedFetch;
|
|
@@ -395,18 +422,6 @@ export function installFetchInterceptor(config: {
|
|
|
395
422
|
);
|
|
396
423
|
}
|
|
397
424
|
|
|
398
|
-
/**
|
|
399
|
-
* Update the auth token on the live interceptor config. Must be called
|
|
400
|
-
* whenever the Stigmer JWT is refreshed (e.g. via IPC updateToken) so
|
|
401
|
-
* that fetch-intercepted REST calls (token exchange, /v1/models) use the
|
|
402
|
-
* current token instead of the one frozen at install time.
|
|
403
|
-
*/
|
|
404
|
-
export function updateInterceptorToken(token: string): void {
|
|
405
|
-
if (interceptorConfig) {
|
|
406
|
-
interceptorConfig = { ...interceptorConfig, stigmerToken: token };
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
425
|
/**
|
|
411
426
|
* Update the execution ID on the active interceptor (legacy global path).
|
|
412
427
|
* Prefer {@link runWithExecutionContext} for concurrent-safe isolation.
|
|
@@ -419,18 +434,6 @@ export function setInterceptorExecutionId(executionId: string | undefined): void
|
|
|
419
434
|
}
|
|
420
435
|
}
|
|
421
436
|
|
|
422
|
-
/**
|
|
423
|
-
* Run an async function with execution-scoped context. The executionId is
|
|
424
|
-
* propagated through the async call chain via AsyncLocalStorage, ensuring
|
|
425
|
-
* concurrent activities on the same runner process don't overwrite each
|
|
426
|
-
* other's proxy headers.
|
|
427
|
-
*/
|
|
428
|
-
export function runWithExecutionContext<T>(
|
|
429
|
-
executionId: string,
|
|
430
|
-
fn: () => Promise<T>,
|
|
431
|
-
): Promise<T> {
|
|
432
|
-
return executionContext.run({ executionId }, fn);
|
|
433
|
-
}
|
|
434
437
|
|
|
435
438
|
/**
|
|
436
439
|
* Remove the interceptor and restore the original fetch. Primarily for
|
|
@@ -16,11 +16,14 @@
|
|
|
16
16
|
*
|
|
17
17
|
* LOAD ORDER IS THEREFORE LOAD-BEARING: this interceptor MUST be installed
|
|
18
18
|
* before the first connect-node import in the process. The runner enforces this
|
|
19
|
-
* by (a) keeping connect-node out of the pre-
|
|
20
|
-
* (bootstrap.ts
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* by (a) keeping connect-node out of the pre-boot static module graph
|
|
20
|
+
* (bootstrap.ts and harness/registry.ts load StigmerClient via dynamic import;
|
|
21
|
+
* adapter.ts loads the SDK inside boot) and (b) installing this interceptor in
|
|
22
|
+
* the Cursor adapter's `boot`, which the registry runs in both composition
|
|
23
|
+
* roots before Temporal coordinates are resolved. assertHttp2ConnectPatched()
|
|
24
|
+
* verifies the ESM-facade view right after the install so any future
|
|
25
|
+
* regression fails loudly instead of silently 401-ing;
|
|
26
|
+
* src/__tests__/harness-boot-order.test.ts runs that boot in a fresh process.
|
|
24
27
|
*
|
|
25
28
|
* This module patches `http2.connect()` to wrap returned sessions. The
|
|
26
29
|
* wrapped session's `request()` method reads the execution ID from the
|
|
@@ -42,7 +45,7 @@
|
|
|
42
45
|
|
|
43
46
|
import { createRequire } from "node:module";
|
|
44
47
|
import type http2Type from "node:http2";
|
|
45
|
-
import { getExecutionContext } from "
|
|
48
|
+
import { getExecutionContext } from "../../shared/execution-context.js";
|
|
46
49
|
|
|
47
50
|
// Use require() to get the ACTUAL CJS module singleton. Mutations here
|
|
48
51
|
// are visible to all importers including ESM namespace imports, because
|
|
@@ -56,7 +59,14 @@ const STIGMER_AUTH_HEADER = "x-stigmer-auth";
|
|
|
56
59
|
interface Http2InterceptorConfig {
|
|
57
60
|
proxyHostname: string;
|
|
58
61
|
proxyPort: string;
|
|
59
|
-
|
|
62
|
+
/**
|
|
63
|
+
* The proxy credential, read on EVERY stream (never copied): the root that
|
|
64
|
+
* installed this interceptor owns the ref and rotates it in place, so a
|
|
65
|
+
* rotation reaches the next `request()` on an already-open session without
|
|
66
|
+
* a call into this module (see fetch-interceptor.ts for the two roots'
|
|
67
|
+
* bindings, Q-S2-7).
|
|
68
|
+
*/
|
|
69
|
+
proxyTokenRef: { readonly current: string | null };
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
let config: Http2InterceptorConfig | null = null;
|
|
@@ -110,10 +120,15 @@ function wrapSession(session: http2Type.ClientHttp2Session): http2Type.ClientHtt
|
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
const ctx = getExecutionContext().getStore();
|
|
113
|
-
const augmented: http2Type.OutgoingHttpHeaders = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
const augmented: http2Type.OutgoingHttpHeaders = { ...headers };
|
|
124
|
+
// An empty ref is unreachable by construction today (no root writes null
|
|
125
|
+
// into the ref it bound); the stream then goes out without the header and
|
|
126
|
+
// the proxy's 401 is the diagnostic, rather than a throw inside the SDK's
|
|
127
|
+
// transport, a path nothing exercises.
|
|
128
|
+
const token = config.proxyTokenRef.current;
|
|
129
|
+
if (token !== null) {
|
|
130
|
+
augmented[STIGMER_AUTH_HEADER] = `Bearer ${token}`;
|
|
131
|
+
}
|
|
117
132
|
if (ctx?.executionId) {
|
|
118
133
|
augmented[EXECUTION_ID_HEADER] = ctx.executionId;
|
|
119
134
|
}
|
|
@@ -124,21 +139,27 @@ function wrapSession(session: http2Type.ClientHttp2Session): http2Type.ClientHtt
|
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
/**
|
|
127
|
-
* Install the HTTP/2 interceptor
|
|
128
|
-
*
|
|
142
|
+
* Install the HTTP/2 interceptor, at boot, BEFORE `@cursor/sdk` (and so
|
|
143
|
+
* connect-node) is imported. Idempotent: a second install rebinds the config
|
|
144
|
+
* (a worker may re-boot its harnesses) and leaves the one patch in place —
|
|
145
|
+
* re-capturing `http2.connect` here would take the patched function as the
|
|
146
|
+
* original and wrap every proxy session twice.
|
|
129
147
|
*
|
|
130
|
-
* When proxyEndpoint is not provided,
|
|
131
|
-
* and all http2.connect() calls pass through to
|
|
148
|
+
* When proxyEndpoint is not provided, or no credential ref is bound, the
|
|
149
|
+
* interceptor is not installed and all http2.connect() calls pass through to
|
|
150
|
+
* the original. (The fetch interceptor, installed first, throws on a proxy
|
|
151
|
+
* without a credential; this one stays a silent no-op as it always has, so a
|
|
152
|
+
* caller that installs only this half keeps its historical posture.)
|
|
132
153
|
*/
|
|
133
154
|
export function installHttp2Interceptor(opts: {
|
|
134
155
|
proxyEndpoint: string | undefined;
|
|
135
|
-
|
|
156
|
+
proxyTokenRef: { readonly current: string | null } | undefined;
|
|
136
157
|
}): void {
|
|
137
158
|
if (!opts.proxyEndpoint) {
|
|
138
159
|
return;
|
|
139
160
|
}
|
|
140
161
|
|
|
141
|
-
if (!opts.
|
|
162
|
+
if (!opts.proxyTokenRef || opts.proxyTokenRef.current === null) {
|
|
142
163
|
return;
|
|
143
164
|
}
|
|
144
165
|
|
|
@@ -150,7 +171,11 @@ export function installHttp2Interceptor(opts: {
|
|
|
150
171
|
return;
|
|
151
172
|
}
|
|
152
173
|
|
|
153
|
-
|
|
174
|
+
const alreadyPatched = config !== null;
|
|
175
|
+
config = { proxyHostname: parsed.hostname, proxyPort: parsed.port, proxyTokenRef: opts.proxyTokenRef };
|
|
176
|
+
if (alreadyPatched) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
154
179
|
originalConnect = http2.connect;
|
|
155
180
|
|
|
156
181
|
http2.connect = function patchedConnect(
|
|
@@ -205,26 +230,14 @@ export async function assertHttp2ConnectPatched(): Promise<void> {
|
|
|
205
230
|
"[http2-interceptor] node:http2 ESM facade is unpatched: connect-node imported " +
|
|
206
231
|
"node:http2 before installHttp2Interceptor() ran, so its frozen namespace still " +
|
|
207
232
|
"holds the original http2.connect. BiDi streams would omit x-stigmer-auth and 401. " +
|
|
208
|
-
"Fix the load order: keep @connectrpc/connect-node out of the pre-
|
|
209
|
-
"
|
|
210
|
-
"
|
|
211
|
-
"
|
|
233
|
+
"Fix the load order: keep @connectrpc/connect-node out of the pre-boot static module " +
|
|
234
|
+
"graph (load StigmerClient via dynamic import, as bootstrap.ts and harness/registry.ts " +
|
|
235
|
+
"do) and keep @cursor/sdk behind the Cursor adapter's boot (adapter.ts loads turn.ts " +
|
|
236
|
+
"after this install). See src/__tests__/harness-boot-order.test.ts.",
|
|
212
237
|
);
|
|
213
238
|
}
|
|
214
239
|
}
|
|
215
240
|
|
|
216
|
-
/**
|
|
217
|
-
* Update the auth token on the live interceptor config. Must be called
|
|
218
|
-
* whenever the Stigmer JWT is refreshed (e.g. via IPC updateToken) so
|
|
219
|
-
* that HTTP/2-intercepted streams use the current token instead of the
|
|
220
|
-
* one frozen at install time.
|
|
221
|
-
*/
|
|
222
|
-
export function updateHttp2InterceptorToken(token: string): void {
|
|
223
|
-
if (config) {
|
|
224
|
-
config = { ...config, stigmerToken: token };
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
241
|
/**
|
|
229
242
|
* Remove the interceptor and restore the original http2.connect.
|
|
230
243
|
* Primarily for testing.
|