@stigmer/runner 3.0.9-dev.20260615153829 → 3.1.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__/approval-contract/types.d.ts +174 -0
- package/dist/__test-utils__/approval-contract/types.js +24 -0
- package/dist/__test-utils__/approval-contract/types.js.map +1 -0
- package/dist/activities/call-agent-status.d.ts +19 -1
- package/dist/activities/call-agent-status.js +64 -5
- package/dist/activities/call-agent-status.js.map +1 -1
- package/dist/activities/call-llm.js +19 -53
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +24 -0
- package/dist/activities/classify-tool-approvals.js +69 -17
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -0
- package/dist/activities/discover-mcp-server.js +11 -1
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +140 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +130 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.d.ts +18 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +123 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-cursor/approval-policy.d.ts +17 -61
- package/dist/activities/execute-cursor/approval-policy.js +8 -120
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +219 -9
- package/dist/activities/execute-cursor/approval-state.js +233 -17
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +127 -0
- package/dist/activities/execute-cursor/capture-flow.js +234 -0
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -0
- package/dist/activities/execute-cursor/cas-observations.d.ts +96 -0
- package/dist/activities/execute-cursor/cas-observations.js +184 -0
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -0
- package/dist/activities/execute-cursor/command-provenance.d.ts +62 -0
- package/dist/activities/execute-cursor/command-provenance.js +128 -0
- package/dist/activities/execute-cursor/command-provenance.js.map +1 -0
- package/dist/activities/execute-cursor/exact-apply.d.ts +110 -0
- package/dist/activities/execute-cursor/exact-apply.js +204 -0
- package/dist/activities/execute-cursor/exact-apply.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +53 -24
- package/dist/activities/execute-cursor/hook-script.js +310 -47
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +8 -1
- package/dist/activities/execute-cursor/index.js +500 -64
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +258 -11
- package/dist/activities/execute-cursor/message-translator.js +836 -90
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +20 -23
- package/dist/activities/execute-cursor/prompt-builder.js +74 -15
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/workspace-setup.d.ts +17 -0
- package/dist/activities/execute-cursor/workspace-setup.js +212 -33
- package/dist/activities/execute-cursor/workspace-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.d.ts +19 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js +143 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +88 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +81 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -0
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +47 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +68 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -0
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +8 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +7 -7
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +42 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +47 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +79 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +112 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -0
- package/dist/activities/execute-deep-agent/hitl.d.ts +16 -3
- package/dist/activities/execute-deep-agent/hitl.js +11 -3
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.d.ts +2 -1
- package/dist/activities/execute-deep-agent/index.js +393 -33
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +7 -1
- package/dist/activities/execute-deep-agent/inline-publisher.js +23 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +53 -2
- package/dist/activities/execute-deep-agent/setup.js +149 -92
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +36 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +56 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +1 -0
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +34 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.js +26 -25
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +17 -5
- package/dist/activities/execute-deep-agent/status-builder.js +44 -3
- package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +2 -19
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming.js +3 -15
- package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js +23 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +30 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js +29 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js +32 -2
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +9 -5
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +2 -0
- package/dist/client/stigmer-client.js +2 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +85 -4
- package/dist/middleware/approval-gate.js +165 -38
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/types.d.ts +2 -5
- package/dist/shared/activity-input.d.ts +43 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.js.map +1 -0
- package/dist/shared/approval-canonicalize.d.ts +19 -0
- package/dist/shared/approval-canonicalize.js +119 -0
- package/dist/shared/approval-canonicalize.js.map +1 -0
- package/dist/shared/approval-fingerprint.d.ts +106 -0
- package/dist/shared/approval-fingerprint.js +113 -0
- package/dist/shared/approval-fingerprint.js.map +1 -0
- package/dist/shared/approval-policy.d.ts +182 -12
- package/dist/shared/approval-policy.js +213 -27
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +52 -0
- package/dist/shared/args-preview.js +93 -0
- package/dist/shared/args-preview.js.map +1 -0
- package/dist/shared/artifact-storage.d.ts +19 -1
- package/dist/shared/artifact-storage.js +48 -11
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/file-change.d.ts +44 -0
- package/dist/shared/file-change.js +57 -0
- package/dist/shared/file-change.js.map +1 -0
- package/dist/shared/file-tools.d.ts +107 -0
- package/dist/shared/file-tools.js +168 -0
- package/dist/shared/file-tools.js.map +1 -0
- package/dist/shared/filereview/capture.d.ts +202 -0
- package/dist/shared/filereview/capture.js +498 -0
- package/dist/shared/filereview/capture.js.map +1 -0
- package/dist/shared/filereview/cas-substrate.d.ts +190 -0
- package/dist/shared/filereview/cas-substrate.js +284 -0
- package/dist/shared/filereview/cas-substrate.js.map +1 -0
- package/dist/shared/filereview/digest.d.ts +40 -0
- package/dist/shared/filereview/digest.js +66 -0
- package/dist/shared/filereview/digest.js.map +1 -0
- package/dist/shared/filereview/events.d.ts +170 -0
- package/dist/shared/filereview/events.js +298 -0
- package/dist/shared/filereview/events.js.map +1 -0
- package/dist/shared/filereview/git-substrate.d.ts +175 -0
- package/dist/shared/filereview/git-substrate.js +439 -0
- package/dist/shared/filereview/git-substrate.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +11 -0
- package/dist/shared/filereview/index.js +12 -0
- package/dist/shared/filereview/index.js.map +1 -0
- package/dist/shared/filereview/secret-paths.d.ts +63 -0
- package/dist/shared/filereview/secret-paths.js +105 -0
- package/dist/shared/filereview/secret-paths.js.map +1 -0
- package/dist/shared/fingerprint-secret.d.ts +26 -0
- package/dist/shared/fingerprint-secret.js +47 -0
- package/dist/shared/fingerprint-secret.js.map +1 -0
- package/dist/shared/model-client.d.ts +51 -0
- package/dist/shared/model-client.js +77 -0
- package/dist/shared/model-client.js.map +1 -0
- package/dist/shared/plan-artifact.js +0 -2
- package/dist/shared/plan-artifact.js.map +1 -1
- package/dist/shared/status-offload.d.ts +83 -9
- package/dist/shared/status-offload.js +399 -79
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/status.js +14 -1
- package/dist/shared/status.js.map +1 -1
- package/dist/shared/tool-kind.d.ts +19 -0
- package/dist/shared/tool-kind.js +13 -0
- package/dist/shared/tool-kind.js.map +1 -1
- package/dist/shared/tool-row.d.ts +88 -0
- package/dist/shared/tool-row.js +127 -0
- package/dist/shared/tool-row.js.map +1 -0
- package/dist/shared/workspace/platform-dir.d.ts +25 -0
- package/dist/shared/workspace/platform-dir.js +38 -2
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.js +56 -7
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.d.ts +50 -0
- package/dist/workflows/connect-mcp-server.js +136 -15
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/types.d.ts +8 -0
- package/package.json +2 -2
- package/src/__test-utils__/approval-contract/contract.ts +224 -0
- package/src/__test-utils__/approval-contract/types.ts +179 -0
- package/src/__test-utils__/fake-artifact-storage.ts +72 -0
- package/src/__tests__/approval-gateway-contract.test.ts +29 -0
- package/src/__tests__/claimcheck-codec.test.ts +16 -53
- package/src/__tests__/golden-e2e.test.ts +2 -0
- package/src/__tests__/runner-token-coordinator.test.ts +3 -3
- package/src/activities/__tests__/call-agent-status.test.ts +135 -0
- package/src/activities/__tests__/call-llm.test.ts +1 -1
- package/src/activities/__tests__/classify-tool-approvals.test.ts +208 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +30 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +2 -1
- package/src/activities/call-agent-status.ts +74 -4
- package/src/activities/call-llm.ts +18 -63
- package/src/activities/classify-tool-approvals.ts +101 -19
- package/src/activities/discover-mcp-server.ts +29 -1
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +216 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +148 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +41 -9
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +292 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +68 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +1005 -0
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +187 -0
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +97 -0
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +240 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +203 -0
- package/src/activities/execute-cursor/__tests__/exact-apply.test.ts +375 -0
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +1294 -24
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +446 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +384 -110
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +171 -25
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +189 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +44 -23
- package/src/activities/execute-cursor/__tests__/workspace-setup.test.ts +190 -10
- package/src/activities/execute-cursor/approval-policy.ts +28 -159
- package/src/activities/execute-cursor/approval-state.ts +366 -18
- package/src/activities/execute-cursor/capture-flow.ts +323 -0
- package/src/activities/execute-cursor/cas-observations.ts +204 -0
- package/src/activities/execute-cursor/command-provenance.ts +168 -0
- package/src/activities/execute-cursor/exact-apply.ts +253 -0
- package/src/activities/execute-cursor/hook-script.ts +317 -51
- package/src/activities/execute-cursor/index.ts +575 -67
- package/src/activities/execute-cursor/message-translator.ts +963 -89
- package/src/activities/execute-cursor/prompt-builder.ts +80 -14
- package/src/activities/execute-cursor/workspace-setup.ts +257 -42
- package/src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts +180 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +134 -0
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +11 -24
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +64 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-observer.test.ts +163 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +8 -5
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +342 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +284 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +14 -36
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +31 -13
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +349 -0
- package/src/activities/execute-deep-agent/__tests__/stamp-flowed-rows.test.ts +119 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +91 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +9 -9
- package/src/activities/execute-deep-agent/__tests__/subagent-approval-propagation.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +213 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +3 -6
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +84 -1
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +4 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +80 -0
- package/src/activities/execute-deep-agent/attachment-injector.ts +20 -11
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +66 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +125 -0
- package/src/activities/execute-deep-agent/hitl.ts +23 -7
- package/src/activities/execute-deep-agent/index.ts +463 -40
- package/src/activities/execute-deep-agent/inline-publisher.ts +27 -4
- package/src/activities/execute-deep-agent/setup.ts +223 -125
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +64 -0
- package/src/activities/execute-deep-agent/status-builder-shared.ts +62 -23
- package/src/activities/execute-deep-agent/status-builder.ts +63 -8
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +2 -16
- package/src/activities/execute-deep-agent/streaming.ts +3 -13
- package/src/activities/execute-deep-agent/subagent-transformer.ts +53 -13
- package/src/activities/execute-deep-agent/subagent-wiring.ts +50 -3
- package/src/activities/execute-deep-agent/v3-status-builder.ts +36 -2
- package/src/claimcheck/payload-codec.ts +8 -8
- package/src/client/stigmer-client.ts +9 -1
- package/src/middleware/__tests__/approval-gate.test.ts +488 -4
- package/src/middleware/approval-gate.ts +247 -38
- package/src/middleware/types.ts +5 -5
- package/src/shared/__tests__/activity-input.test.ts +78 -0
- package/src/shared/__tests__/approval-canonicalize.test.ts +106 -0
- package/src/shared/__tests__/approval-fingerprint.test.ts +115 -0
- package/src/shared/__tests__/approval-policy.test.ts +274 -40
- package/src/shared/__tests__/args-preview.test.ts +78 -0
- package/src/shared/__tests__/artifact-storage-extended.test.ts +62 -10
- package/src/shared/__tests__/artifact-storage.test.ts +123 -11
- package/src/shared/__tests__/file-change.test.ts +85 -0
- package/src/shared/__tests__/file-tools.test.ts +90 -0
- package/src/shared/__tests__/fingerprint-secret.test.ts +51 -0
- package/src/shared/__tests__/lease-scope-corpus.test.ts +56 -0
- package/src/shared/__tests__/model-client.test.ts +162 -0
- package/src/shared/__tests__/plan-artifact.test.ts +11 -26
- package/src/shared/__tests__/policy-source-corpus.test.ts +58 -0
- package/src/shared/__tests__/status-offload.test.ts +573 -16
- package/src/shared/__tests__/status.test.ts +4 -5
- package/src/shared/__tests__/tool-kind.test.ts +24 -1
- package/src/shared/__tests__/tool-row.test.ts +221 -0
- package/src/shared/activity-input.ts +57 -0
- package/src/shared/approval-canonicalize.ts +159 -0
- package/src/shared/approval-fingerprint.ts +148 -0
- package/src/shared/approval-policy.ts +303 -27
- package/src/shared/args-preview.ts +98 -0
- package/src/shared/artifact-storage.ts +62 -11
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +1 -2
- package/src/shared/file-change.ts +64 -0
- package/src/shared/file-tools.ts +169 -0
- package/src/shared/filereview/__tests__/capture.test.ts +856 -0
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +404 -0
- package/src/shared/filereview/__tests__/digest.test.ts +100 -0
- package/src/shared/filereview/__tests__/events.test.ts +245 -0
- package/src/shared/filereview/__tests__/git-substrate.test.ts +362 -0
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +286 -0
- package/src/shared/filereview/__tests__/secret-paths.test.ts +121 -0
- package/src/shared/filereview/capture.ts +727 -0
- package/src/shared/filereview/cas-substrate.ts +401 -0
- package/src/shared/filereview/digest.ts +83 -0
- package/src/shared/filereview/events.ts +449 -0
- package/src/shared/filereview/git-substrate.ts +555 -0
- package/src/shared/filereview/index.ts +60 -0
- package/src/shared/filereview/secret-paths.ts +121 -0
- package/src/shared/fingerprint-secret.ts +53 -0
- package/src/shared/model-client.ts +122 -0
- package/src/shared/plan-artifact.ts +0 -2
- package/src/shared/status-offload.ts +433 -77
- package/src/shared/status.ts +13 -0
- package/src/shared/tool-kind.ts +33 -0
- package/src/shared/tool-row.ts +135 -0
- package/src/shared/workspace/platform-dir.ts +41 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +35 -18
- package/src/workflow-engine/__tests__/tasks/try.test.ts +1 -1
- package/src/workflows/__tests__/connect-mcp-server.test.ts +304 -29
- package/src/workflows/call-agent-orchestrator.ts +53 -6
- package/src/workflows/connect-mcp-server.ts +179 -24
- package/src/workflows/types.ts +8 -0
|
@@ -30,13 +30,26 @@
|
|
|
30
30
|
* hooks.json is inert: the scope guard allows every invocation once the runner
|
|
31
31
|
* PID is gone, and the relocated artifacts are not in the repo.
|
|
32
32
|
*/
|
|
33
|
-
import { writeFile, readFile, mkdir, chmod, rm } from "node:fs/promises";
|
|
34
|
-
import { join } from "node:path";
|
|
33
|
+
import { writeFile, readFile, mkdir, chmod, rm, rmdir, readdir } from "node:fs/promises";
|
|
34
|
+
import { basename, dirname, join } from "node:path";
|
|
35
35
|
import { generateHookScript } from "./hook-script.js";
|
|
36
|
-
import {
|
|
36
|
+
import { buildToolApprovalRuleFile } from "./prompt-builder.js";
|
|
37
|
+
import { writeApprovalStateFile, resetDenialLedger, activePointerPath, writeActiveTurnPointer, removeActiveTurnPointer, } from "./approval-state.js";
|
|
38
|
+
import { resetCasObservations } from "./cas-observations.js";
|
|
39
|
+
import { ensureHitlGateDir } from "../../shared/workspace/platform-dir.js";
|
|
37
40
|
const CURSOR_DIR = ".cursor";
|
|
38
41
|
const HOOKS_CONFIG_FILE = "hooks.json";
|
|
39
42
|
const HOOK_SCRIPT_FILE = "stigmer-approval.sh";
|
|
43
|
+
const RULES_DIR = "rules";
|
|
44
|
+
// Pre-#173 the gate wrote its script/state/ledger INTO the repo here, as
|
|
45
|
+
// `stigmer-*` files. Newer runs relocate them to the session HITL dir, but the
|
|
46
|
+
// old files (and their hooks.json entry) linger in already-touched repos — see
|
|
47
|
+
// removeLegacyWorkspaceHookArtifacts / isStigmerHookEntry.
|
|
48
|
+
const LEGACY_HOOKS_DIR = "hooks";
|
|
49
|
+
const RUNNER_OWNED_HOOK_FILE_PREFIX = "stigmer-";
|
|
50
|
+
// Unmistakably runner-owned filename so installing it never collides with a
|
|
51
|
+
// user's own project rules and restoring it never touches one.
|
|
52
|
+
const TOOL_APPROVAL_RULE_FILE = "stigmer-tool-approval.mdc";
|
|
40
53
|
/**
|
|
41
54
|
* Cursor hook events the gate registers, both pointing at the same script (which
|
|
42
55
|
* branches on `hook_event_name`). `preToolUse` gates built-in tools
|
|
@@ -56,14 +69,33 @@ const HOOK_TIMEOUT_SECONDS = 10;
|
|
|
56
69
|
*/
|
|
57
70
|
export async function installHitlGate(params) {
|
|
58
71
|
const { workspaceRoot, hitlDir, approvalState, runnerPid } = params;
|
|
59
|
-
|
|
72
|
+
// Heal any pre-#173 in-workspace gate leftovers before installing. Without
|
|
73
|
+
// this, a stale `.cursor/hooks/stigmer-approval.sh` (from a runner build that
|
|
74
|
+
// wrote the gate into the repo) keeps running alongside the current
|
|
75
|
+
// out-of-workspace gate and — reading an old, taskless approval-state with no
|
|
76
|
+
// grant for the current action — vetoes EVERY gated tool, including ones the
|
|
77
|
+
// user just approved ("blocked despite approval"). Its denials also land in a
|
|
78
|
+
// ledger this runner never reads, so the denial never surfaces as a pause and
|
|
79
|
+
// the run silently completes with the file never written. The merge below
|
|
80
|
+
// strips the stale hooks.json entry (see isStigmerHookEntry); this removes the
|
|
81
|
+
// orphaned script/state/ledger files so nothing can re-run them.
|
|
82
|
+
await removeLegacyWorkspaceHookArtifacts(workspaceRoot);
|
|
83
|
+
const { scriptPath: approvalScriptPath, gateDir } = await writeHitlArtifacts(workspaceRoot, hitlDir, approvalState, runnerPid);
|
|
60
84
|
// One script, two events: preToolUse gates built-ins; beforeMCPExecution is
|
|
61
85
|
// the only event Cursor enforces for MCP tools. The script branches internally
|
|
62
86
|
// on hook_event_name so MCP is gated in exactly one place.
|
|
63
|
-
|
|
87
|
+
const hookHandle = await installWorkspaceHook(workspaceRoot, [
|
|
64
88
|
{ event: PRE_TOOL_USE_EVENT, scriptPath: approvalScriptPath },
|
|
65
89
|
{ event: BEFORE_MCP_EVENT, scriptPath: approvalScriptPath },
|
|
66
90
|
]);
|
|
91
|
+
// Install the always-applied tool-approval rule. The deny-based gate surfaces
|
|
92
|
+
// an approval pause to the model as a tool failure (Cursor's generic "blocked
|
|
93
|
+
// by a hook" text), and the SDK exposes no non-leaky approval primitive — so
|
|
94
|
+
// this rule, which takes precedence over MCP-server instructions and persists
|
|
95
|
+
// across resumed turns, is the strongest available lever to stop the model
|
|
96
|
+
// from misreading the gate as a broken environment.
|
|
97
|
+
const rule = await installWorkspaceRule(workspaceRoot);
|
|
98
|
+
return { ...hookHandle, rule, gateDir };
|
|
67
99
|
}
|
|
68
100
|
/**
|
|
69
101
|
* Restore the workspace to its pre-turn state.
|
|
@@ -72,36 +104,138 @@ export async function installHitlGate(params) {
|
|
|
72
104
|
* and a leftover hooks.json is inert anyway (see the module doc).
|
|
73
105
|
*/
|
|
74
106
|
export async function removeHitlGate(handle) {
|
|
107
|
+
await restoreWorkspaceFile(handle.hooksJsonPath, handle.restoreTo);
|
|
108
|
+
// Drop the active-turn pointer so the gate is INERT between turns: a hook that
|
|
109
|
+
// fires while no turn is active (a cached hooks.json, the user's own IDE on the
|
|
110
|
+
// same repo) reads no pointer and allows immediately. The stable hook script
|
|
111
|
+
// itself is intentionally left in place — it is harmless without a pointer and
|
|
112
|
+
// is reused (and refreshed) by the next turn.
|
|
113
|
+
await removeActiveTurnPointer(handle.gateDir);
|
|
114
|
+
if (handle.rule) {
|
|
115
|
+
await restoreWorkspaceFile(handle.rule.path, handle.rule.restoreTo);
|
|
116
|
+
// When we deleted our own rule file (restoreTo === null) and the `.cursor/
|
|
117
|
+
// rules` dir is now empty, remove it too so a repo that had no rules before
|
|
118
|
+
// is left exactly as we found it. rmdir only removes an empty dir, so a
|
|
119
|
+
// workspace with the user's own rules is untouched.
|
|
120
|
+
if (handle.rule.restoreTo === null) {
|
|
121
|
+
try {
|
|
122
|
+
await rmdir(dirname(handle.rule.path));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Non-empty (user has other rules) or already gone — nothing to clean.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Restore a single gate-managed workspace file to its pre-turn state: delete it
|
|
132
|
+
* when nothing existed before (restoreTo === null), else write the snapshot
|
|
133
|
+
* bytes back. Best-effort and never throws — a teardown failure must not fail
|
|
134
|
+
* the execution, and a leftover artifact is inert (see the module doc).
|
|
135
|
+
*/
|
|
136
|
+
async function restoreWorkspaceFile(path, restoreTo) {
|
|
75
137
|
try {
|
|
76
|
-
if (
|
|
77
|
-
await rm(
|
|
138
|
+
if (restoreTo === null) {
|
|
139
|
+
await rm(path, { force: true });
|
|
78
140
|
}
|
|
79
141
|
else {
|
|
80
|
-
await writeFile(
|
|
142
|
+
await writeFile(path, restoreTo, "utf-8");
|
|
81
143
|
}
|
|
82
144
|
}
|
|
83
145
|
catch (err) {
|
|
84
|
-
console.warn(`removeHitlGate: failed to restore ${
|
|
146
|
+
console.warn(`removeHitlGate: failed to restore ${path} (non-fatal): ` +
|
|
85
147
|
`${err instanceof Error ? err.message : err}`);
|
|
86
148
|
}
|
|
87
149
|
}
|
|
88
150
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
151
|
+
* Delete pre-#173 in-workspace gate leftovers: the runner-owned `stigmer-*`
|
|
152
|
+
* files an older runner build wrote to `.cursor/hooks/` (the hook script, its
|
|
153
|
+
* approval-state, its denial ledger). These are the orphan that shadows the
|
|
154
|
+
* current gate (see installHitlGate). Best-effort and never throws — a cleanup
|
|
155
|
+
* failure must not fail the run.
|
|
156
|
+
*
|
|
157
|
+
* Only `stigmer-`-prefixed files are removed (the unmistakably runner-owned
|
|
158
|
+
* namespace, like the rule filename), so a user's own scripts in `.cursor/hooks`
|
|
159
|
+
* are untouched; the directory itself is removed only if it ends up empty.
|
|
160
|
+
*/
|
|
161
|
+
async function removeLegacyWorkspaceHookArtifacts(workspaceRoot) {
|
|
162
|
+
const hooksDir = join(workspaceRoot, CURSOR_DIR, LEGACY_HOOKS_DIR);
|
|
163
|
+
let entries;
|
|
164
|
+
try {
|
|
165
|
+
entries = await readdir(hooksDir);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return; // no legacy hooks dir — nothing to heal
|
|
169
|
+
}
|
|
170
|
+
let removedAny = false;
|
|
171
|
+
for (const name of entries) {
|
|
172
|
+
if (!name.startsWith(RUNNER_OWNED_HOOK_FILE_PREFIX))
|
|
173
|
+
continue;
|
|
174
|
+
try {
|
|
175
|
+
await rm(join(hooksDir, name), { force: true });
|
|
176
|
+
removedAny = true;
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
console.warn(`removeLegacyWorkspaceHookArtifacts: failed to remove ${name} (non-fatal): ` +
|
|
180
|
+
`${err instanceof Error ? err.message : err}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (removedAny) {
|
|
184
|
+
// rmdir only succeeds on an empty dir, so a user who keeps their own hooks
|
|
185
|
+
// here is left untouched; an empty (entirely-ours) dir is cleaned away.
|
|
186
|
+
try {
|
|
187
|
+
await rmdir(hooksDir);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Non-empty (user owns other hooks) or already gone — nothing to clean.
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Write this turn's gate artifacts and return the STABLE hook-script path plus
|
|
196
|
+
* the workspace gate directory.
|
|
197
|
+
*
|
|
198
|
+
* Two surfaces, deliberately split by scope:
|
|
199
|
+
*
|
|
200
|
+
* - Per-SESSION (in {@link hitlDir}): the approval-state file (hook input) and a
|
|
201
|
+
* freshly-reset denial ledger (hook output the runner reads). Reset every turn
|
|
202
|
+
* so the runner only ever reads denials from the current run, even across HITL
|
|
203
|
+
* reinvocations and Temporal activity retries.
|
|
204
|
+
*
|
|
205
|
+
* - Per-WORKSPACE (in the gate dir): the STABLE hook script and the active-turn
|
|
206
|
+
* pointer. The script path is stable across executions because the Cursor SDK
|
|
207
|
+
* caches `.cursor/hooks.json` (the script path) for the runner process — a
|
|
208
|
+
* per-session script would be cached at the first execution and reused for all
|
|
209
|
+
* later ones, sending their denials to the first session's ledger (the
|
|
210
|
+
* empty-ledger → COMPLETED regression). The script bakes NO per-session paths;
|
|
211
|
+
* it reads the pointer, which we repoint (atomically) every turn to THIS turn's
|
|
212
|
+
* state file, ledger, and runner PID. See generateHookScript / writeActiveTurnPointer.
|
|
93
213
|
*/
|
|
94
|
-
async function writeHitlArtifacts(hitlDir, approvalState, runnerPid) {
|
|
214
|
+
async function writeHitlArtifacts(workspaceRoot, hitlDir, approvalState, runnerPid) {
|
|
95
215
|
await mkdir(hitlDir, { recursive: true });
|
|
96
216
|
const stateFilePath = await writeApprovalStateFile(hitlDir, approvalState);
|
|
97
|
-
// Reset the denial ledger for this turn so the runner only reads denials
|
|
98
|
-
// produced by the current run, even across HITL reinvocations on the durable
|
|
99
|
-
// HITL directory and Temporal activity retries.
|
|
100
217
|
const ledgerFilePath = await resetDenialLedger(hitlDir);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
218
|
+
// Reset the cas-observations sidecar too, so the turn boundary only ever reads
|
|
219
|
+
// gitignored observations the hook staged this run (deterministic across HITL
|
|
220
|
+
// reinvocations and Temporal retries, exactly like the denial ledger).
|
|
221
|
+
await resetCasObservations(hitlDir);
|
|
222
|
+
const gateDir = await ensureHitlGateDir(workspaceRoot);
|
|
223
|
+
const pointerPath = activePointerPath(gateDir);
|
|
224
|
+
const scriptPath = join(gateDir, HOOK_SCRIPT_FILE);
|
|
225
|
+
// Bake the workspace root so capture mode's `git check-ignore` runs against the
|
|
226
|
+
// right repo regardless of the hook's cwd. The root is stable per workspace
|
|
227
|
+
// (the gate dir is keyed by it), so the script stays stable across executions.
|
|
228
|
+
await writeFile(scriptPath, generateHookScript(pointerPath, workspaceRoot), "utf-8");
|
|
229
|
+
await chmod(scriptPath, 0o755);
|
|
230
|
+
// Point the stable hook at THIS turn's per-session artifacts (atomic, last
|
|
231
|
+
// write wins) — the single indirection that makes a process-cached hook resolve
|
|
232
|
+
// the current execution instead of the first one's.
|
|
233
|
+
await writeActiveTurnPointer(gateDir, {
|
|
234
|
+
stateFile: stateFilePath,
|
|
235
|
+
ledgerFile: ledgerFilePath,
|
|
236
|
+
runnerPid,
|
|
237
|
+
});
|
|
238
|
+
return { scriptPath, gateDir };
|
|
105
239
|
}
|
|
106
240
|
/**
|
|
107
241
|
* Snapshot the workspace's existing `.cursor/hooks.json`, write a merged config
|
|
@@ -123,6 +257,28 @@ async function installWorkspaceHook(workspaceRoot, registrations) {
|
|
|
123
257
|
await writeFile(hooksJsonPath, merged, "utf-8");
|
|
124
258
|
return { hooksJsonPath, restoreTo };
|
|
125
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Snapshot and install the always-applied tool-approval rule into
|
|
262
|
+
* `.cursor/rules/stigmer-tool-approval.mdc`, returning the path + restore
|
|
263
|
+
* target. The filename is runner-owned, so restoreTo is normally null (teardown
|
|
264
|
+
* deletes it); if a same-named file somehow pre-exists (e.g. a crash-leftover
|
|
265
|
+
* from a prior turn), its bytes are captured and restored so we never clobber a
|
|
266
|
+
* file we did not create.
|
|
267
|
+
*/
|
|
268
|
+
async function installWorkspaceRule(workspaceRoot) {
|
|
269
|
+
const rulesDir = join(workspaceRoot, CURSOR_DIR, RULES_DIR);
|
|
270
|
+
const rulePath = join(rulesDir, TOOL_APPROVAL_RULE_FILE);
|
|
271
|
+
let restoreTo = null;
|
|
272
|
+
try {
|
|
273
|
+
restoreTo = await readFile(rulePath, "utf-8");
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
restoreTo = null;
|
|
277
|
+
}
|
|
278
|
+
await mkdir(rulesDir, { recursive: true });
|
|
279
|
+
await writeFile(rulePath, buildToolApprovalRuleFile(), "utf-8");
|
|
280
|
+
return { path: rulePath, restoreTo };
|
|
281
|
+
}
|
|
126
282
|
/**
|
|
127
283
|
* A hook entry the gate installs. Absolute `command` so the hook is found
|
|
128
284
|
* regardless of which workspace root a multi-root IDE resolves against.
|
|
@@ -131,18 +287,31 @@ function buildHookEntry(scriptPath) {
|
|
|
131
287
|
return { command: scriptPath, timeout: HOOK_TIMEOUT_SECONDS, failClosed: true };
|
|
132
288
|
}
|
|
133
289
|
/**
|
|
134
|
-
* Identify a hook entry the gate itself wrote
|
|
135
|
-
* turn
|
|
136
|
-
*
|
|
137
|
-
*
|
|
290
|
+
* Identify a hook entry the gate itself wrote — in this turn, a prior
|
|
291
|
+
* crash-leftover turn, or a pre-#173 runner build — so a re-install never
|
|
292
|
+
* duplicates it and a restore strips it. Matches two runner-owned shapes, never
|
|
293
|
+
* a user's own hook:
|
|
294
|
+
*
|
|
295
|
+
* 1. Current: any script under the runner-owned `~/.stigmer/` tree (the session
|
|
296
|
+
* HITL dir or the workspace gate dir, `~/.stigmer/.../*.sh`) — location-based,
|
|
297
|
+
* so it covers every gate script regardless of name.
|
|
298
|
+
* 2. Legacy: a `stigmer-*.sh` script (historically the in-workspace
|
|
299
|
+
* `.cursor/hooks/stigmer-approval.sh`) — recognized by its unmistakably
|
|
300
|
+
* runner-owned basename. Without this, the old in-workspace hook is treated
|
|
301
|
+
* as a user hook and preserved by the merge, so it keeps running alongside
|
|
302
|
+
* (and vetoing) the current gate. The `stigmer-` prefix is the same
|
|
303
|
+
* runner-owned namespace convention as the rule filename.
|
|
138
304
|
*/
|
|
139
305
|
function isStigmerHookEntry(entry) {
|
|
140
306
|
if (!entry || typeof entry !== "object")
|
|
141
307
|
return false;
|
|
142
308
|
const command = entry.command;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
309
|
+
if (typeof command !== "string" || command.length === 0)
|
|
310
|
+
return false;
|
|
311
|
+
if (command.includes("/.stigmer/") && command.endsWith(".sh"))
|
|
312
|
+
return true;
|
|
313
|
+
const file = basename(command);
|
|
314
|
+
return file.startsWith(RUNNER_OWNED_HOOK_FILE_PREFIX) && file.endsWith(".sh");
|
|
146
315
|
}
|
|
147
316
|
const STANDALONE_CONFIG = (registrations) => JSON.stringify({ version: 1, hooks: mergeHooks({}, registrations).hooks }, null, 2);
|
|
148
317
|
/**
|
|
@@ -203,11 +372,21 @@ export function buildMergedConfig(originalRaw, registrations) {
|
|
|
203
372
|
const version = typeof root.version === "number" ? root.version : 1;
|
|
204
373
|
const { hooks: mergedHooks, cleaned, strippedStale } = mergeHooks(hooks, registrations);
|
|
205
374
|
const merged = JSON.stringify({ ...root, version, hooks: mergedHooks }, null, 2);
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
375
|
+
// No stale Stigmer entry → restore the user's exact original bytes.
|
|
376
|
+
if (!strippedStale) {
|
|
377
|
+
return { merged, restoreTo: originalRaw };
|
|
378
|
+
}
|
|
379
|
+
// We stripped a stale Stigmer entry, so never restore the original bytes (that
|
|
380
|
+
// would re-plant our leftover). If stripping leaves NO user hooks at all and
|
|
381
|
+
// the file carried nothing but `version`/`hooks`, the entire hooks.json was our
|
|
382
|
+
// own leftover (the pre-#173 design wrote the whole file) — delete it on
|
|
383
|
+
// teardown so a polluted repo is left pristine. Otherwise restore the cleaned,
|
|
384
|
+
// Stigmer-free form, preserving the user's other hooks and root fields.
|
|
385
|
+
const onlyVersionAndHooks = Object.keys(root).every((k) => k === "version" || k === "hooks");
|
|
386
|
+
const noUserHooksRemain = Object.values(cleaned).every((v) => Array.isArray(v) && v.length === 0);
|
|
387
|
+
const restoreTo = onlyVersionAndHooks && noUserHooksRemain
|
|
388
|
+
? null
|
|
389
|
+
: JSON.stringify({ ...root, version, hooks: cleaned }, null, 2);
|
|
211
390
|
return { merged, restoreTo };
|
|
212
391
|
}
|
|
213
392
|
//# sourceMappingURL=workspace-setup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-setup.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/workspace-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-setup.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/workspace-setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep-agent adapter for the HITL gateway Contract Test Kit.
|
|
3
|
+
*
|
|
4
|
+
* Drives the REAL in-process gateway: a `createDeepAgent` graph carrying the
|
|
5
|
+
* production {@link createApprovalGateMiddleware}, a `MemorySaver` checkpointer,
|
|
6
|
+
* and a scripted model that proposes the action under test. `authorize` runs the
|
|
7
|
+
* graph to its first pause and, for an approving/non-approving decision, resumes
|
|
8
|
+
* via `Command(resume=…)` — exactly the production pause/resume cycle proven in
|
|
9
|
+
* `__tests__/subagent-approval-propagation.test.ts`. There is no mocked
|
|
10
|
+
* `interrupt()`: the gate's own `interrupt()` fires, so the contract exercises the
|
|
11
|
+
* gateway end-to-end.
|
|
12
|
+
*
|
|
13
|
+
* Capabilities: this substrate IS the side effect, so it observes (and counts)
|
|
14
|
+
* execution; resource-exact leasing is not a property of this gate (sameness comes
|
|
15
|
+
* from checkpoint replay), so `enforcesExactResource` is false and
|
|
16
|
+
* `authorizeAfterGrant` is intentionally not implemented.
|
|
17
|
+
*/
|
|
18
|
+
import type { GatewaySubstrate } from "../../../__test-utils__/approval-contract/types.js";
|
|
19
|
+
export declare function createDeepAgentSubstrate(): GatewaySubstrate;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep-agent adapter for the HITL gateway Contract Test Kit.
|
|
3
|
+
*
|
|
4
|
+
* Drives the REAL in-process gateway: a `createDeepAgent` graph carrying the
|
|
5
|
+
* production {@link createApprovalGateMiddleware}, a `MemorySaver` checkpointer,
|
|
6
|
+
* and a scripted model that proposes the action under test. `authorize` runs the
|
|
7
|
+
* graph to its first pause and, for an approving/non-approving decision, resumes
|
|
8
|
+
* via `Command(resume=…)` — exactly the production pause/resume cycle proven in
|
|
9
|
+
* `__tests__/subagent-approval-propagation.test.ts`. There is no mocked
|
|
10
|
+
* `interrupt()`: the gate's own `interrupt()` fires, so the contract exercises the
|
|
11
|
+
* gateway end-to-end.
|
|
12
|
+
*
|
|
13
|
+
* Capabilities: this substrate IS the side effect, so it observes (and counts)
|
|
14
|
+
* execution; resource-exact leasing is not a property of this gate (sameness comes
|
|
15
|
+
* from checkpoint replay), so `enforcesExactResource` is false and
|
|
16
|
+
* `authorizeAfterGrant` is intentionally not implemented.
|
|
17
|
+
*/
|
|
18
|
+
import { HumanMessage } from "@langchain/core/messages";
|
|
19
|
+
import { tool } from "@langchain/core/tools";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { Command, MemorySaver } from "@langchain/langgraph";
|
|
22
|
+
import { createDeepAgent, StateBackend } from "deepagents";
|
|
23
|
+
import { createApprovalGateMiddleware } from "../../../middleware/approval-gate.js";
|
|
24
|
+
import { deriveLeaseScope } from "../../../shared/approval-policy.js";
|
|
25
|
+
import { ScriptedModel, readPendingInterrupts } from "./scripted-model.js";
|
|
26
|
+
/**
|
|
27
|
+
* Translate an abstract action into a deep-agent tool name + args + MCP server.
|
|
28
|
+
* The names are deliberately ALIASES, not deepagents built-ins, so we can attach
|
|
29
|
+
* a counting handler without shadowing an injected built-in, while still hitting
|
|
30
|
+
* the right approval category via the shared classifier:
|
|
31
|
+
* - write → `overwrite_file` (FILE_WRITE), delete → `remove_file` (FILE_DELETE),
|
|
32
|
+
* shell → `execute_command` (SHELL): each gated fail-closed by category.
|
|
33
|
+
* - read → an unknown, non-mutating name: auto-approved (fail-open), no gate.
|
|
34
|
+
* - mcp → the MCP tool name mapped to its server: governed by policy, absent
|
|
35
|
+
* policy ⇒ auto-approved.
|
|
36
|
+
*/
|
|
37
|
+
function toDeepAgentTool(action) {
|
|
38
|
+
switch (action.kind) {
|
|
39
|
+
case "write":
|
|
40
|
+
return { name: "overwrite_file", args: { path: action.resource, content: "x" }, serverSlug: "" };
|
|
41
|
+
case "shell":
|
|
42
|
+
return { name: "execute_command", args: { command: action.resource }, serverSlug: "" };
|
|
43
|
+
case "delete":
|
|
44
|
+
return { name: "remove_file", args: { path: action.resource }, serverSlug: "" };
|
|
45
|
+
case "read":
|
|
46
|
+
return { name: "inspect_resource", args: { path: action.resource }, serverSlug: "" };
|
|
47
|
+
case "mcp":
|
|
48
|
+
return { name: action.mcpToolName ?? "mcp_tool", args: {}, serverSlug: action.mcpServerSlug ?? "srv" };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Map a contract decision to the gate's resume verdict (anything unrecognized = unknown). */
|
|
52
|
+
function toResumeAction(decision) {
|
|
53
|
+
switch (decision) {
|
|
54
|
+
case "approve":
|
|
55
|
+
return "approve";
|
|
56
|
+
case "skip":
|
|
57
|
+
return "skip";
|
|
58
|
+
case "reject":
|
|
59
|
+
return "reject";
|
|
60
|
+
default:
|
|
61
|
+
return "unrecognized-verdict";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let threadSeq = 0;
|
|
65
|
+
/**
|
|
66
|
+
* Drive one probe action through a freshly built gate and report the outcome.
|
|
67
|
+
* `leasedCategories` pre-arms a run-lifetime class lease (empty for the plain
|
|
68
|
+
* authorize path); `decision` resumes a pause for the non-lease drives.
|
|
69
|
+
*/
|
|
70
|
+
async function runProbe(action, decision, leasedCategories) {
|
|
71
|
+
const { name, args, serverSlug } = toDeepAgentTool(action);
|
|
72
|
+
let executionCount = 0;
|
|
73
|
+
const countingTool = tool(async () => {
|
|
74
|
+
executionCount += 1;
|
|
75
|
+
return "ok";
|
|
76
|
+
}, {
|
|
77
|
+
name,
|
|
78
|
+
description: `contract probe tool ${name}`,
|
|
79
|
+
schema: z.object({}).passthrough(),
|
|
80
|
+
});
|
|
81
|
+
const toolServerMap = serverSlug ? new Map([[name, serverSlug]]) : new Map();
|
|
82
|
+
const gate = createApprovalGateMiddleware({
|
|
83
|
+
policies: new Map(),
|
|
84
|
+
toolServerMap,
|
|
85
|
+
leasedCategories,
|
|
86
|
+
});
|
|
87
|
+
const agent = await createDeepAgent({
|
|
88
|
+
model: new ScriptedModel(() => ({
|
|
89
|
+
toolCalls: [{ name, args, id: "call_1" }],
|
|
90
|
+
done: "done",
|
|
91
|
+
})),
|
|
92
|
+
checkpointer: new MemorySaver(),
|
|
93
|
+
backend: new StateBackend(),
|
|
94
|
+
tools: [countingTool],
|
|
95
|
+
middleware: [gate],
|
|
96
|
+
});
|
|
97
|
+
const config = { configurable: { thread_id: `contract-${threadSeq++}` }, recursionLimit: 50 };
|
|
98
|
+
// Run to the first pause. A gated tool interrupts before its handler runs.
|
|
99
|
+
await agent.invoke({ messages: [new HumanMessage({ content: "go" })] }, config);
|
|
100
|
+
const pending = readPendingInterrupts((await agent.getState(config)));
|
|
101
|
+
const gated = pending.length > 0;
|
|
102
|
+
// The gate stamps the authorization provenance on every interrupt it raises;
|
|
103
|
+
// surface it so the contract can assert no side effect is gated without one.
|
|
104
|
+
const policySource = gated ? pending[0].policySource : "";
|
|
105
|
+
// `none` is the "no decision yet" probe: leave the action withheld.
|
|
106
|
+
if (gated && decision !== "none") {
|
|
107
|
+
const resume = {};
|
|
108
|
+
for (const p of pending)
|
|
109
|
+
resume[p.interruptId] = { action: toResumeAction(decision) };
|
|
110
|
+
await agent.invoke(new Command({ resume }), config);
|
|
111
|
+
}
|
|
112
|
+
return { executed: executionCount > 0, gated, executionCount, policySource };
|
|
113
|
+
}
|
|
114
|
+
const NO_LEASED_CATEGORIES = new Set();
|
|
115
|
+
export function createDeepAgentSubstrate() {
|
|
116
|
+
return {
|
|
117
|
+
name: "deep-agent",
|
|
118
|
+
available: true,
|
|
119
|
+
capabilities: {
|
|
120
|
+
observesExecution: true,
|
|
121
|
+
enforcesExactResource: false,
|
|
122
|
+
// The deep-agent gate re-checks every distinct call and relies on
|
|
123
|
+
// checkpoint replay for sameness; content isolation is not a property of
|
|
124
|
+
// the gate itself, so the content-exact invariant does not apply here.
|
|
125
|
+
enforcesExactContent: false,
|
|
126
|
+
appliesRunLifetimeLease: true,
|
|
127
|
+
surfacesGatePolicySource: true,
|
|
128
|
+
},
|
|
129
|
+
async authorize(action, decision) {
|
|
130
|
+
return runProbe(action, decision, NO_LEASED_CATEGORIES);
|
|
131
|
+
},
|
|
132
|
+
async authorizeUnderClassLease(leased, probe) {
|
|
133
|
+
// Reduce the leased action to its class exactly as the runner does, then
|
|
134
|
+
// arm the gate with that category lease and run the probe with no fresh
|
|
135
|
+
// decision — so only the lease can clear it.
|
|
136
|
+
const leasedTool = toDeepAgentTool(leased);
|
|
137
|
+
const scope = deriveLeaseScope(leasedTool.name, leasedTool.serverSlug);
|
|
138
|
+
const leasedCategories = scope?.kind === "category" ? new Set([scope.category]) : NO_LEASED_CATEGORIES;
|
|
139
|
+
return runProbe(probe, "none", leasedCategories);
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=gateway-substrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-substrate.js","sourceRoot":"","sources":["../../../../src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripted deep-agent test harness: a deterministic chat model plus the
|
|
3
|
+
* parent-checkpoint interrupt reader.
|
|
4
|
+
*
|
|
5
|
+
* These two helpers are what every "drive a real deepagents/LangGraph graph
|
|
6
|
+
* through the approval gate" test needs, and they were previously copy-pasted
|
|
7
|
+
* inline. They are shared by:
|
|
8
|
+
* - `__tests__/subagent-approval-propagation.test.ts` (sub-agent interrupt
|
|
9
|
+
* propagation against the real runtime), and
|
|
10
|
+
* - `__test-utils__/gateway-substrate.ts` (the deep-agent adapter for the
|
|
11
|
+
* gateway Contract Test Kit).
|
|
12
|
+
*
|
|
13
|
+
* Keeping one copy means a change to how the runner reads pending interrupts (or
|
|
14
|
+
* how a scripted run is driven) updates every consumer at once.
|
|
15
|
+
*/
|
|
16
|
+
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
|
|
17
|
+
import { type BaseMessage } from "@langchain/core/messages";
|
|
18
|
+
import type { ChatResult } from "@langchain/core/outputs";
|
|
19
|
+
/** A single tool call the scripted model proposes on a non-terminal turn. */
|
|
20
|
+
export interface ScriptedToolCall {
|
|
21
|
+
name: string;
|
|
22
|
+
args: Record<string, unknown>;
|
|
23
|
+
id: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* What the scripted model does for a given role: propose `toolCalls` while no
|
|
27
|
+
* tool result is pending, then emit the `done` text once a tool result returns.
|
|
28
|
+
*/
|
|
29
|
+
export interface ScriptStep {
|
|
30
|
+
toolCalls: ScriptedToolCall[];
|
|
31
|
+
done: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Picks the {@link ScriptStep} for the currently-bound tool set. A single-agent
|
|
35
|
+
* test ignores the argument; a parent/sub-agent test keys the role on a tool
|
|
36
|
+
* unique to one role (e.g. the sub-agent's gated tool), because `createDeepAgent`
|
|
37
|
+
* injects the `task` tool into both the parent and its sub-agents.
|
|
38
|
+
*/
|
|
39
|
+
export type ScriptSelector = (boundToolNames: string[]) => ScriptStep;
|
|
40
|
+
/**
|
|
41
|
+
* A deterministic two-turn chat model: on the first turn it proposes the
|
|
42
|
+
* selected role's tool calls; once a tool result comes back it returns the
|
|
43
|
+
* role's terminating text. No LLM, no network — the role is keyed on the bound
|
|
44
|
+
* tool names so the same model instance drives a parent and its sub-agents.
|
|
45
|
+
*/
|
|
46
|
+
export declare class ScriptedModel extends BaseChatModel {
|
|
47
|
+
toolNames: string[];
|
|
48
|
+
private readonly select;
|
|
49
|
+
constructor(select: ScriptSelector);
|
|
50
|
+
_llmType(): string;
|
|
51
|
+
bindTools(tools: unknown[]): this;
|
|
52
|
+
_generate(messages: BaseMessage[]): Promise<ChatResult>;
|
|
53
|
+
}
|
|
54
|
+
/** A pending approval interrupt surfaced at the parent checkpoint. */
|
|
55
|
+
export interface PendingInterrupt {
|
|
56
|
+
taskId: string;
|
|
57
|
+
interruptId: string;
|
|
58
|
+
toolCallId: string;
|
|
59
|
+
toolName: string;
|
|
60
|
+
message: string;
|
|
61
|
+
/**
|
|
62
|
+
* Authorization provenance the gate attached to the interrupt
|
|
63
|
+
* (approval-gate.ts `policy_source`) — the PolicySource union string, or "" for
|
|
64
|
+
* legacy. Lets the gateway contract assert every gated side effect is
|
|
65
|
+
* provenance-tagged.
|
|
66
|
+
*/
|
|
67
|
+
policySource: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read the pending approval interrupts from a graph state, exactly as the
|
|
71
|
+
* production resume path does (`hitl.ts` / `index.ts`): scan the parent's
|
|
72
|
+
* top-level `tasks[].interrupts`, skipping any already carrying a resume value.
|
|
73
|
+
*
|
|
74
|
+
* Crucially `interruptId` is the interrupt's OWN id (`interrupts[].id`), not the
|
|
75
|
+
* owning `task.id` — for a nested sub-agent interrupt the two differ, and only
|
|
76
|
+
* the interrupt id routes a `Command(resume=...)` value back into the nested
|
|
77
|
+
* `interrupt()`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readPendingInterrupts(state: {
|
|
80
|
+
tasks?: ReadonlyArray<{
|
|
81
|
+
id: string;
|
|
82
|
+
interrupts?: ReadonlyArray<{
|
|
83
|
+
id?: string;
|
|
84
|
+
value?: unknown;
|
|
85
|
+
resumeValue?: unknown;
|
|
86
|
+
}>;
|
|
87
|
+
}>;
|
|
88
|
+
}): PendingInterrupt[];
|