@stigmer/runner 3.0.9-dev.20260616060535 → 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 +10 -0
- package/dist/activities/execute-deep-agent/hitl.js +5 -1
- 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 +370 -56
- 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 +11 -5
- package/dist/activities/execute-deep-agent/status-builder.js +6 -2
- 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 +6 -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 +2 -5
- 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 +12 -11
- 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 +15 -1
- package/src/activities/execute-deep-agent/index.ts +434 -64
- 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 +19 -7
- 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 +8 -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
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git snapshot/restore substrate for the apply-then-review HITL "capture mode".
|
|
3
|
+
*
|
|
4
|
+
* THE MODEL
|
|
5
|
+
* ---------
|
|
6
|
+
* Edits are applied to the working tree and reviewed AFTER the fact; rejecting a
|
|
7
|
+
* file snaps it back exactly. We use git as a NO-COMMIT snapshot/restore
|
|
8
|
+
* substrate at the turn boundary:
|
|
9
|
+
*
|
|
10
|
+
* 1. snapshotBaseline() at turn start — record the exact pre-turn working tree
|
|
11
|
+
* and pin it behind a ref so it survives a multi-day approval wait.
|
|
12
|
+
* 2. The agent edits files freely (the gate allows git-tracked write/edit/delete).
|
|
13
|
+
* 3. captureChangeSet() at turn end — diff the post-turn tree against the
|
|
14
|
+
* baseline into a per-file change set, and pin the post-turn ("after") tree
|
|
15
|
+
* behind a ref. The working tree is LEFT in its "after" state: the user
|
|
16
|
+
* reviews the real, applied change and the workspace browser shows it.
|
|
17
|
+
* 4. On resume, reconcile the working tree to the per-file decisions, sourced
|
|
18
|
+
* ENTIRELY from the two pinned refs (not the live tree, not the persisted
|
|
19
|
+
* bodies — the refs are the single source of truth):
|
|
20
|
+
* - approved -> applyApprovedPaths() ensures the "after" bytes.
|
|
21
|
+
* - rejected/undecided -> restoreToBaseline() snaps the file back to its
|
|
22
|
+
* exact pre-turn bytes.
|
|
23
|
+
* This is symmetric and idempotent: it converges to the correct end state
|
|
24
|
+
* regardless of the tree's current contents, so a Temporal retry or a tree
|
|
25
|
+
* reset is harmless.
|
|
26
|
+
* 5. dropCaptureRefs() releases the pinned objects.
|
|
27
|
+
*
|
|
28
|
+
* HARNESS-AGNOSTIC
|
|
29
|
+
* ----------------
|
|
30
|
+
* This is pure git plumbing keyed by `executionId`. It carries no Cursor- or
|
|
31
|
+
* deep-agent-specific knowledge: the runner-owned files a harness writes into the
|
|
32
|
+
* workspace (e.g. the Cursor gate's `.cursor/hooks.json`) are passed in as
|
|
33
|
+
* `excludePaths` so they never pollute the captured diff, rather than being
|
|
34
|
+
* hard-coded here. Both harnesses author IDENTICAL ledger entries through the
|
|
35
|
+
* shared producer (events.ts) on top of this substrate. A future CAS substrate
|
|
36
|
+
* (for ignored / non-git paths) sits beside this module.
|
|
37
|
+
*
|
|
38
|
+
* NOTHING IS PERMANENT UNTIL APPROVAL
|
|
39
|
+
* -----------------------------------
|
|
40
|
+
* Edits are visible during review, but nothing is committed and the next turn is
|
|
41
|
+
* blocked until the user decides; a reject snaps the file back byte-for-byte from
|
|
42
|
+
* the baseline ref. Shell/MCP and gitignored writes/deletes are NOT reversible by
|
|
43
|
+
* this substrate, so they stay on the approve-before-run gate (see each harness's
|
|
44
|
+
* gate: the Cursor hook and the deep-agent approval-gate middleware).
|
|
45
|
+
*
|
|
46
|
+
* WHY GIT, NO COMMITS
|
|
47
|
+
* -------------------
|
|
48
|
+
* Git is used ONLY as an exact, reversible capture/restore mechanism. We never
|
|
49
|
+
* move HEAD, never touch a branch, never push. Approved work stays uncommitted;
|
|
50
|
+
* a harness's own writeback path (if any) commits the reconciled tree afterwards.
|
|
51
|
+
*
|
|
52
|
+
* HARD INVARIANTS
|
|
53
|
+
* ---------------
|
|
54
|
+
* - NEVER `git clean -x`/`-a` or `git stash -a`: the git-excluded `.stigmer/`
|
|
55
|
+
* directory holds SDK resume state and MUST survive. `git clean -fd` (no `-x`)
|
|
56
|
+
* leaves ignored paths alone, so `.stigmer`/`lost+found` are safe.
|
|
57
|
+
* - Runner-owned files written into the workspace are EXCLUDED (via `excludePaths`)
|
|
58
|
+
* from both the baseline and the after tree, so they never pollute the captured
|
|
59
|
+
* diff regardless of gate-teardown ordering.
|
|
60
|
+
* - All staging uses a TEMP index (GIT_INDEX_FILE) so the repo's real index is
|
|
61
|
+
* never disturbed.
|
|
62
|
+
*/
|
|
63
|
+
import { execFile } from "node:child_process";
|
|
64
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
65
|
+
import { dirname, join } from "node:path";
|
|
66
|
+
import { promisify } from "node:util";
|
|
67
|
+
import { FileChangeType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
68
|
+
import { bytesLookBinary } from "../file-change.js";
|
|
69
|
+
import { sha256Bytes } from "./digest.js";
|
|
70
|
+
const execFileAsync = promisify(execFile);
|
|
71
|
+
/** Build `:(exclude)` pathspecs for the runner-owned files a harness passes in. */
|
|
72
|
+
function excludePathspecs(excludePaths) {
|
|
73
|
+
return excludePaths.map((p) => `:(exclude)${p}`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Refs that pin the per-execution baseline and after trees against GC. Exported
|
|
77
|
+
* so the file-review producer can stamp them onto the ledger's SnapshotRef
|
|
78
|
+
* (GitTreeRef.ref) without re-deriving the format. Keyed by executionId only:
|
|
79
|
+
* the unified gate guarantees at most one un-reconciled change set per execution
|
|
80
|
+
* at a time, and the reconcile drops these refs before the next turn re-pins.
|
|
81
|
+
*/
|
|
82
|
+
export function baselineRef(executionId) {
|
|
83
|
+
return `refs/stigmer/baseline/${executionId}`;
|
|
84
|
+
}
|
|
85
|
+
export function captureRef(executionId) {
|
|
86
|
+
return `refs/stigmer/capture/${executionId}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A non-interactive identity for the snapshot commit objects. These commits are
|
|
90
|
+
* never on a branch, never pushed, and exist only to anchor a tree against
|
|
91
|
+
* garbage collection — but `git commit-tree` still requires an author/committer,
|
|
92
|
+
* and the repo may have no user.name/email configured. Supplied via env so we
|
|
93
|
+
* never mutate the repo's config.
|
|
94
|
+
*/
|
|
95
|
+
const SNAPSHOT_IDENTITY_ENV = {
|
|
96
|
+
GIT_AUTHOR_NAME: "stigmer-runner",
|
|
97
|
+
GIT_AUTHOR_EMAIL: "runner@stigmer.local",
|
|
98
|
+
GIT_COMMITTER_NAME: "stigmer-runner",
|
|
99
|
+
GIT_COMMITTER_EMAIL: "runner@stigmer.local",
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Run a git command in `gitRoot`. Returns stdout as a UTF-8 string. Binary
|
|
103
|
+
* output (file bodies) must use {@link gitBuffer} instead so bytes are exact.
|
|
104
|
+
*/
|
|
105
|
+
async function git(gitRoot, args, env) {
|
|
106
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
107
|
+
cwd: gitRoot,
|
|
108
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
109
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
110
|
+
encoding: "utf-8",
|
|
111
|
+
});
|
|
112
|
+
return stdout;
|
|
113
|
+
}
|
|
114
|
+
/** Run a git command returning raw stdout BYTES (for exact file-body reads). */
|
|
115
|
+
async function gitBuffer(gitRoot, args) {
|
|
116
|
+
const { stdout } = await execFileAsync("git", args, {
|
|
117
|
+
cwd: gitRoot,
|
|
118
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
119
|
+
encoding: "buffer",
|
|
120
|
+
});
|
|
121
|
+
return stdout;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Whether `dir` is inside a git work tree (the capability switch: a real git
|
|
125
|
+
* workspace gets capture mode, anything else keeps the deny-gate fallback).
|
|
126
|
+
*/
|
|
127
|
+
export async function isGitWorkTree(dir) {
|
|
128
|
+
try {
|
|
129
|
+
const out = await git(dir, ["rev-parse", "--is-inside-work-tree"]);
|
|
130
|
+
return out.trim() === "true";
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Whether a repo-relative path is capturable by this substrate — i.e. it would
|
|
138
|
+
* appear in the `git add -A` snapshot. A gitignored path is NOT capturable: the
|
|
139
|
+
* snapshot cannot see it, so it can be neither reviewed nor reverted, and a
|
|
140
|
+
* capture-mode gate must keep gating it. Mirrors the Cursor hook's
|
|
141
|
+
* `__stigmer_is_gitignored` check: `git check-ignore -q` exits 0 when the path IS
|
|
142
|
+
* ignored, non-zero otherwise; a non-git context / error is treated as
|
|
143
|
+
* not-ignored (capturable), matching the hook's allow-on-error behavior.
|
|
144
|
+
*/
|
|
145
|
+
export async function isPathCapturable(gitRoot, path) {
|
|
146
|
+
if (!path)
|
|
147
|
+
return false;
|
|
148
|
+
try {
|
|
149
|
+
await git(gitRoot, ["check-ignore", "-q", "--", path]);
|
|
150
|
+
return false; // exit 0 -> ignored -> not capturable
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return true; // exit 1 (not ignored) or error -> capturable
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Whether the repo has a resolvable HEAD (false for a brand-new empty repo). */
|
|
157
|
+
async function headExists(gitRoot) {
|
|
158
|
+
try {
|
|
159
|
+
await git(gitRoot, ["rev-parse", "--verify", "--quiet", "HEAD"]);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Absolute path of the repo's `.git` dir (for temp index placement). */
|
|
167
|
+
async function resolveGitDir(gitRoot) {
|
|
168
|
+
const out = (await git(gitRoot, ["rev-parse", "--absolute-git-dir"])).trim();
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Stage the entire working tree (tracked + untracked, honoring `.gitignore` /
|
|
173
|
+
* `.git/info/exclude`, EXCLUDING the passed runner-owned paths) into a TEMP index
|
|
174
|
+
* and write it out as a tree object. The real index is never touched.
|
|
175
|
+
*
|
|
176
|
+
* Using a temp index with `git add -A` records every present file as the tree's
|
|
177
|
+
* content; files absent from the working tree are simply absent from the tree,
|
|
178
|
+
* so the result is an exact snapshot of "what files exist right now and their
|
|
179
|
+
* bytes". `.stigmer` and other ignored paths are never staged.
|
|
180
|
+
*/
|
|
181
|
+
async function writeWorkingTree(gitRoot, gitDir, label, executionId, excludePaths) {
|
|
182
|
+
const tmpIndex = join(gitDir, `stigmer-index-${label}-${executionId}`);
|
|
183
|
+
try {
|
|
184
|
+
await rm(tmpIndex, { force: true });
|
|
185
|
+
const env = { GIT_INDEX_FILE: tmpIndex };
|
|
186
|
+
await git(gitRoot, ["add", "-A", "--", ".", ...excludePathspecs(excludePaths)], env);
|
|
187
|
+
return (await git(gitRoot, ["write-tree"], env)).trim();
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
await rm(tmpIndex, { force: true });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Pin `tree` behind `ref` as a parent-less-or-HEAD-parented commit (anti-GC). */
|
|
194
|
+
async function pinTree(gitRoot, ref, tree, message) {
|
|
195
|
+
const args = ["commit-tree", tree, "-m", message];
|
|
196
|
+
if (await headExists(gitRoot)) {
|
|
197
|
+
args.push("-p", "HEAD");
|
|
198
|
+
}
|
|
199
|
+
const commit = (await git(gitRoot, args, SNAPSHOT_IDENTITY_ENV)).trim();
|
|
200
|
+
await git(gitRoot, ["update-ref", ref, commit]);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Snapshot the working tree at turn start and pin it behind the baseline ref.
|
|
204
|
+
* Returns the baseline tree sha (used by {@link captureChangeSet} and
|
|
205
|
+
* {@link restoreToBaseline} later in the SAME activity invocation). `excludePaths`
|
|
206
|
+
* are the runner-owned files the harness writes into the workspace (omit for none).
|
|
207
|
+
*/
|
|
208
|
+
export async function snapshotBaseline(gitRoot, executionId, excludePaths = []) {
|
|
209
|
+
const gitDir = await resolveGitDir(gitRoot);
|
|
210
|
+
const tree = await writeWorkingTree(gitRoot, gitDir, "baseline", executionId, excludePaths);
|
|
211
|
+
await pinTree(gitRoot, baselineRef(executionId), tree, `stigmer baseline ${executionId}`);
|
|
212
|
+
return tree;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Capture the change set the turn produced: write the post-turn working tree,
|
|
216
|
+
* pin it behind the capture ref (so it survives the approval wait), and diff it
|
|
217
|
+
* against `baselineTree` into a per-file {@link GitSubstrateChange} list.
|
|
218
|
+
*
|
|
219
|
+
* Renames are intentionally NOT detected (`--no-renames`): a rename surfaces as
|
|
220
|
+
* a delete + create, which restores and applies exactly and matches the per-file
|
|
221
|
+
* review model.
|
|
222
|
+
*/
|
|
223
|
+
export async function captureChangeSet(gitRoot, executionId, baselineTree, excludePaths = []) {
|
|
224
|
+
const gitDir = await resolveGitDir(gitRoot);
|
|
225
|
+
const afterTree = await writeWorkingTree(gitRoot, gitDir, "capture", executionId, excludePaths);
|
|
226
|
+
await pinTree(gitRoot, captureRef(executionId), afterTree, `stigmer capture ${executionId}`);
|
|
227
|
+
const raw = await git(gitRoot, [
|
|
228
|
+
"diff",
|
|
229
|
+
"--no-renames",
|
|
230
|
+
"--name-status",
|
|
231
|
+
"-z",
|
|
232
|
+
baselineTree,
|
|
233
|
+
afterTree,
|
|
234
|
+
]);
|
|
235
|
+
const changes = [];
|
|
236
|
+
for (const { status, path } of parseNameStatusZ(raw)) {
|
|
237
|
+
const change = await buildCapturedChange(gitRoot, baselineTree, afterTree, status, path);
|
|
238
|
+
if (change)
|
|
239
|
+
changes.push(change);
|
|
240
|
+
}
|
|
241
|
+
return { baselineTree, afterTree, changes };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Restore the given files to their `baselineTree` (pre-turn) bytes. On resume
|
|
245
|
+
* this reverts the REJECTED/undecided subset so a discarded change snaps back
|
|
246
|
+
* exactly; it is the generic "ensure baseline bytes" primitive. Surgical
|
|
247
|
+
* (bounded by the passed change set): an agent-created file is removed; an
|
|
248
|
+
* agent-modified or agent-deleted file is rewritten with its baseline bytes.
|
|
249
|
+
* `.stigmer` and other ignored paths are never touched (they are not in the
|
|
250
|
+
* change set). Idempotent — safe to re-run (e.g. on a Temporal retry).
|
|
251
|
+
*/
|
|
252
|
+
export async function restoreToBaseline(gitRoot, baselineTree, changes) {
|
|
253
|
+
for (const change of changes) {
|
|
254
|
+
const abs = join(gitRoot, change.path);
|
|
255
|
+
if (change.changeType === FileChangeType.CREATE) {
|
|
256
|
+
await rm(abs, { force: true });
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// MODIFY or DELETE: the baseline holds the file — restore its exact bytes.
|
|
260
|
+
await writeBlobToDisk(gitRoot, baselineTree, change.path, abs);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Ensure the APPROVED files hold their captured "after" bytes, as uncommitted
|
|
266
|
+
* working-tree changes (no commit). In capture mode the agent's edits are left
|
|
267
|
+
* applied through the review window, so on resume this is normally an idempotent
|
|
268
|
+
* re-assert of bytes already on disk; sourcing from the pinned "after" tree also
|
|
269
|
+
* makes it self-healing if the tree was reset (Temporal retry / sandbox
|
|
270
|
+
* recycle). An approved CREATE/MODIFY writes the after bytes; an approved DELETE
|
|
271
|
+
* removes the file. Rejected/undecided paths are not passed in (they go to
|
|
272
|
+
* {@link restoreToBaseline}).
|
|
273
|
+
*/
|
|
274
|
+
export async function applyApprovedPaths(gitRoot, afterTree, approved) {
|
|
275
|
+
for (const change of approved) {
|
|
276
|
+
const abs = join(gitRoot, change.path);
|
|
277
|
+
if (change.changeType === FileChangeType.DELETE) {
|
|
278
|
+
await rm(abs, { force: true });
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// CREATE or MODIFY: write the agent's approved bytes from the after tree.
|
|
282
|
+
await writeBlobToDisk(gitRoot, afterTree, change.path, abs);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Recompute the captured change set on a RESUME, from the pinned baseline and
|
|
288
|
+
* capture refs — the authoritative source that survived the approval wait,
|
|
289
|
+
* independent of any persisted fidelity. Returns `undefined` when this was not a
|
|
290
|
+
* capture turn (the capture ref is absent), so the caller can fall through to the
|
|
291
|
+
* non-capture path.
|
|
292
|
+
*/
|
|
293
|
+
export async function recomputeChangeSet(gitRoot, executionId) {
|
|
294
|
+
const baselineTree = await resolveRefTree(gitRoot, baselineRef(executionId));
|
|
295
|
+
const afterTree = await resolveRefTree(gitRoot, captureRef(executionId));
|
|
296
|
+
if (!baselineTree || !afterTree)
|
|
297
|
+
return undefined;
|
|
298
|
+
const raw = await git(gitRoot, [
|
|
299
|
+
"diff",
|
|
300
|
+
"--no-renames",
|
|
301
|
+
"--name-status",
|
|
302
|
+
"-z",
|
|
303
|
+
baselineTree,
|
|
304
|
+
afterTree,
|
|
305
|
+
]);
|
|
306
|
+
const changes = [];
|
|
307
|
+
for (const { status, path } of parseNameStatusZ(raw)) {
|
|
308
|
+
const change = await buildCapturedChange(gitRoot, baselineTree, afterTree, status, path);
|
|
309
|
+
if (change)
|
|
310
|
+
changes.push(change);
|
|
311
|
+
}
|
|
312
|
+
return { baselineTree, afterTree, changes };
|
|
313
|
+
}
|
|
314
|
+
/** Resolve a ref to its tree sha, or `undefined` when the ref does not exist. */
|
|
315
|
+
async function resolveRefTree(gitRoot, ref) {
|
|
316
|
+
try {
|
|
317
|
+
return (await git(gitRoot, ["rev-parse", "--verify", "--quiet", `${ref}^{tree}`])).trim();
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** The ref pinning the post-reconcile (approved) tree for the RECONCILED event. */
|
|
324
|
+
function approvedRef(executionId) {
|
|
325
|
+
return `refs/stigmer/approved/${executionId}`;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Snapshot the post-reconcile working tree (after approved bytes were applied and
|
|
329
|
+
* rejected files were reverted) and pin it behind the approved ref. Returns the
|
|
330
|
+
* approved tree sha for the RECONCILED event's `approved_snapshot`, plus the ref
|
|
331
|
+
* so the producer can stamp the SnapshotRef. Idempotent under a Temporal retry.
|
|
332
|
+
*/
|
|
333
|
+
export async function snapshotApproved(gitRoot, executionId, excludePaths = []) {
|
|
334
|
+
const gitDir = await resolveGitDir(gitRoot);
|
|
335
|
+
const tree = await writeWorkingTree(gitRoot, gitDir, "approved", executionId, excludePaths);
|
|
336
|
+
const ref = approvedRef(executionId);
|
|
337
|
+
await pinTree(gitRoot, ref, tree, `stigmer approved ${executionId}`);
|
|
338
|
+
return { treeOid: tree, ref };
|
|
339
|
+
}
|
|
340
|
+
/** Release the per-execution baseline, capture, and approved refs (idempotent). */
|
|
341
|
+
export async function dropCaptureRefs(gitRoot, executionId) {
|
|
342
|
+
for (const ref of [baselineRef(executionId), captureRef(executionId), approvedRef(executionId)]) {
|
|
343
|
+
try {
|
|
344
|
+
await git(gitRoot, ["update-ref", "-d", ref]);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// Already gone — nothing to release.
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Parse `git diff --name-status -z` output: a flat NUL-delimited stream of
|
|
353
|
+
* `STATUS\0PATH\0` pairs. NUL-delimiting avoids every quoting/escaping concern
|
|
354
|
+
* for paths with spaces, quotes, or unicode.
|
|
355
|
+
*/
|
|
356
|
+
function parseNameStatusZ(raw) {
|
|
357
|
+
const parts = raw.split("\u0000");
|
|
358
|
+
const entries = [];
|
|
359
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
360
|
+
const status = parts[i];
|
|
361
|
+
const path = parts[i + 1];
|
|
362
|
+
if (!status || !path)
|
|
363
|
+
continue;
|
|
364
|
+
entries.push({ status: status[0], path });
|
|
365
|
+
}
|
|
366
|
+
return entries;
|
|
367
|
+
}
|
|
368
|
+
/** Read a blob's exact BYTES from a tree, or `undefined` when absent. */
|
|
369
|
+
async function readBlobBytes(gitRoot, tree, path) {
|
|
370
|
+
try {
|
|
371
|
+
return await gitBuffer(gitRoot, ["cat-file", "-p", `${tree}:${path}`]);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Read one side of a change as {@link CapturedContent}, computing its identity
|
|
379
|
+
* from the RAW bytes (never a lossy UTF-8 decode): a binary blob carries only its
|
|
380
|
+
* byte-true content address (no body — it reconciles from the git ref), while a
|
|
381
|
+
* text blob is carried inline. Returns `undefined` only when the blob is absent
|
|
382
|
+
* from the tree (the side does not exist for this changeType).
|
|
383
|
+
*/
|
|
384
|
+
async function readSide(gitRoot, tree, path) {
|
|
385
|
+
const bytes = await readBlobBytes(gitRoot, tree, path);
|
|
386
|
+
if (bytes === undefined)
|
|
387
|
+
return undefined;
|
|
388
|
+
// Identity is always computed over the RAW bytes at the source — never a lossy
|
|
389
|
+
// UTF-8 re-encode — so the enforcement digest is byte-true for binary and text
|
|
390
|
+
// alike. A binary blob carries no body (it reconciles from the git ref); a text
|
|
391
|
+
// blob carries its inline body for the review diff.
|
|
392
|
+
const sha256 = sha256Bytes(bytes);
|
|
393
|
+
if (bytesLookBinary(bytes)) {
|
|
394
|
+
return { kind: "binary", sha256 };
|
|
395
|
+
}
|
|
396
|
+
return { kind: "inline", text: bytes.toString("utf8"), sha256 };
|
|
397
|
+
}
|
|
398
|
+
/** Write a blob's exact bytes from a tree to `abs` (creating parent dirs). */
|
|
399
|
+
async function writeBlobToDisk(gitRoot, tree, path, abs) {
|
|
400
|
+
const buf = await gitBuffer(gitRoot, ["cat-file", "-p", `${tree}:${path}`]);
|
|
401
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
402
|
+
await writeFile(abs, buf);
|
|
403
|
+
}
|
|
404
|
+
/** An empty inline side — the default when a MODIFY blob is unexpectedly absent. */
|
|
405
|
+
const EMPTY_INLINE = { kind: "inline", text: "" };
|
|
406
|
+
/**
|
|
407
|
+
* Build a {@link GitSubstrateChange} from a diff entry. WHOLE_FILE capture: the
|
|
408
|
+
* before/after content comes from the two trees, with identity computed over the
|
|
409
|
+
* exact bytes git holds (see {@link readSide}) — so the review shows the file's
|
|
410
|
+
* true net change and the enforcement digest is byte-true, binary or text alike.
|
|
411
|
+
* A binary side is carried as a content address only (no body); large text bodies
|
|
412
|
+
* are offloaded later at the persist chokepoint.
|
|
413
|
+
*/
|
|
414
|
+
async function buildCapturedChange(gitRoot, baselineTree, afterTree, status, path) {
|
|
415
|
+
if (status === "A") {
|
|
416
|
+
const after = await readSide(gitRoot, afterTree, path);
|
|
417
|
+
if (after === undefined)
|
|
418
|
+
return undefined;
|
|
419
|
+
return { path, changeType: FileChangeType.CREATE, after };
|
|
420
|
+
}
|
|
421
|
+
if (status === "D") {
|
|
422
|
+
const before = await readSide(gitRoot, baselineTree, path);
|
|
423
|
+
if (before === undefined)
|
|
424
|
+
return undefined;
|
|
425
|
+
return { path, changeType: FileChangeType.DELETE, before };
|
|
426
|
+
}
|
|
427
|
+
// Modified (M) — and any other status (T type-change, etc.) treated as MODIFY.
|
|
428
|
+
const before = await readSide(gitRoot, baselineTree, path);
|
|
429
|
+
const after = await readSide(gitRoot, afterTree, path);
|
|
430
|
+
if (before === undefined && after === undefined)
|
|
431
|
+
return undefined;
|
|
432
|
+
return {
|
|
433
|
+
path,
|
|
434
|
+
changeType: FileChangeType.MODIFY,
|
|
435
|
+
before: before ?? EMPTY_INLINE,
|
|
436
|
+
after: after ?? EMPTY_INLINE,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=git-substrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-substrate.js","sourceRoot":"","sources":["../../../src/shared/filereview/git-substrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runner's file-review producer: harness-agnostic digest + event authoring
|
|
3
|
+
* for the apply-then-review HITL subsystem. Consumed by both the Cursor and
|
|
4
|
+
* deep-agent harnesses to author identical ledger entries.
|
|
5
|
+
*/
|
|
6
|
+
export { aggregateDigest, fileChangeKindName, fileDigest, sha256Bytes, sha256Hex, type FileDigestInput, } from "./digest.js";
|
|
7
|
+
export { appendFileReviewEvents, buildBaselineCapturedEvent, buildCandidateCapturedEvent, buildCapturedFileChange, buildFailedEvent, buildReconciledEvent, eventId, hasCandidateCaptured, type CapturedChangeInput, type ChangeSetContext, } from "./events.js";
|
|
8
|
+
export { applyCaptureDecisions, captureBaselineToLedger, captureCandidateToLedger, type CaptureResumeResult, } from "./capture.js";
|
|
9
|
+
export { isGitWorkTree } from "./git-substrate.js";
|
|
10
|
+
export { applyCasApproved, casBlobKey, casBlobReader, casManifestKey, loadCasManifest, restoreCasToBaseline, snapshotCasChangeSet, type BlobReader, type CasBlobRef, type CasCapturedFile, type CasManifest, type CasPathCapture, type CasSnapshotRef, } from "./cas-substrate.js";
|
|
11
|
+
export { isSecretLikePath, partitionIgnoredPathsBySecret, SECRET_BASENAME_PATTERNS, SECRET_PATH_PATTERNS, type CasSecretPartition, } from "./secret-paths.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runner's file-review producer: harness-agnostic digest + event authoring
|
|
3
|
+
* for the apply-then-review HITL subsystem. Consumed by both the Cursor and
|
|
4
|
+
* deep-agent harnesses to author identical ledger entries.
|
|
5
|
+
*/
|
|
6
|
+
export { aggregateDigest, fileChangeKindName, fileDigest, sha256Bytes, sha256Hex, } from "./digest.js";
|
|
7
|
+
export { appendFileReviewEvents, buildBaselineCapturedEvent, buildCandidateCapturedEvent, buildCapturedFileChange, buildFailedEvent, buildReconciledEvent, eventId, hasCandidateCaptured, } from "./events.js";
|
|
8
|
+
export { applyCaptureDecisions, captureBaselineToLedger, captureCandidateToLedger, } from "./capture.js";
|
|
9
|
+
export { isGitWorkTree } from "./git-substrate.js";
|
|
10
|
+
export { applyCasApproved, casBlobKey, casBlobReader, casManifestKey, loadCasManifest, restoreCasToBaseline, snapshotCasChangeSet, } from "./cas-substrate.js";
|
|
11
|
+
export { isSecretLikePath, partitionIgnoredPathsBySecret, SECRET_BASENAME_PATTERNS, SECRET_PATH_PATTERNS, } from "./secret-paths.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/filereview/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,SAAS,GAEV,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,2BAA2B,EAC3B,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,OAAO,EACP,oBAAoB,GAGrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,GAEzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,oBAAoB,GAOrB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,gBAAgB,EAChB,6BAA6B,EAC7B,wBAAwB,EACxB,oBAAoB,GAErB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The secret-safety gate for CAS capture (design doc 12).
|
|
3
|
+
*
|
|
4
|
+
* The git substrate never captures gitignored paths, which was an accidental
|
|
5
|
+
* safety property: `.gitignore` is where projects keep secrets (`.env`, private
|
|
6
|
+
* keys, credentials). The CAS substrate's whole purpose is to start capturing
|
|
7
|
+
* ignored / non-git paths — so, done naively, it would write secret bytes into
|
|
8
|
+
* durable artifact storage. This module is the fail-closed gate that prevents
|
|
9
|
+
* that: a path classified secret-like is BLOCKED from capture (authored as
|
|
10
|
+
* DIFF_UNREVIEWABLE instead) and its bytes never leave the workspace.
|
|
11
|
+
*
|
|
12
|
+
* Scope discipline (design doc 12 D2): this is deliberately a small, explicit,
|
|
13
|
+
* deterministic matcher — NOT the Phase-4 `sensitivity` taxonomy, NOT ML. It is
|
|
14
|
+
* a pure function over the path, so the cross-edition corpus can lock it (a path
|
|
15
|
+
* either blocks or captures, identically everywhere). The matcher errs toward
|
|
16
|
+
* blocking: a path only needs to LOOK secret-like to be withheld, because the
|
|
17
|
+
* cost of withholding a non-secret (it stays on the deny-gate, as today) is far
|
|
18
|
+
* lower than the cost of persisting a real secret.
|
|
19
|
+
*
|
|
20
|
+
* @since File-Change HITL Redesign (Phase 3 — CAS / DD-E)
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Basename patterns that mark a file secret-like. Matched case-insensitively
|
|
24
|
+
* against the final path segment. Kept as an explicit, reviewable list — adding
|
|
25
|
+
* a pattern is a deliberate, corpus-locked change, never a heuristic guess.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SECRET_BASENAME_PATTERNS: readonly RegExp[];
|
|
28
|
+
/**
|
|
29
|
+
* Path-fragment patterns that mark a file secret-like by WHERE it lives, matched
|
|
30
|
+
* against the normalized (forward-slash) full path. Covers credential stores
|
|
31
|
+
* whose basenames alone are ambiguous.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SECRET_PATH_PATTERNS: readonly RegExp[];
|
|
34
|
+
/**
|
|
35
|
+
* Whether a path is secret-like and must NOT be captured into the durable CAS
|
|
36
|
+
* store. Fail-closed: an empty/unknown path is treated as secret-like (withheld)
|
|
37
|
+
* rather than captured.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isSecretLikePath(path: string): boolean;
|
|
40
|
+
/** How the turn's observed gitignored paths split under the secret gate. */
|
|
41
|
+
export interface CasSecretPartition {
|
|
42
|
+
/** Paths safe to capture into durable CAS (their bytes may be persisted). */
|
|
43
|
+
readonly capturablePaths: readonly string[];
|
|
44
|
+
/**
|
|
45
|
+
* Paths withheld from capture — authored as content-less DIFF_UNREVIEWABLE.
|
|
46
|
+
* The union of the gate's up-front blocks and any observed secret-like path.
|
|
47
|
+
*/
|
|
48
|
+
readonly unreviewablePaths: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Split a turn's observed gitignored paths into capturable vs withheld — the
|
|
52
|
+
* single source of truth for "which ignored bytes may reach durable storage".
|
|
53
|
+
*
|
|
54
|
+
* `unreviewablePaths = gateBlockedPaths ∪ { p ∈ observed : isSecretLikePath(p) }`
|
|
55
|
+
* and `capturablePaths = { p ∈ observed : ¬isSecretLikePath(p) }`. The
|
|
56
|
+
* `isSecretLikePath` re-check over `observed` is the fail-closed backstop that
|
|
57
|
+
* holds **even under the global bypass** (`spec.auto_approve_all`): there the
|
|
58
|
+
* approval gate is not installed, so `gateBlockedPaths` is empty and this
|
|
59
|
+
* re-check is the only thing keeping a secret's bytes out of CAS. Order is
|
|
60
|
+
* preserved (gate blocks first, then observed order) and duplicates are folded,
|
|
61
|
+
* so the result is deterministic for the cross-edition corpus.
|
|
62
|
+
*/
|
|
63
|
+
export declare function partitionIgnoredPathsBySecret(observedPaths: Iterable<string>, gateBlockedPaths: ReadonlySet<string>): CasSecretPartition;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The secret-safety gate for CAS capture (design doc 12).
|
|
3
|
+
*
|
|
4
|
+
* The git substrate never captures gitignored paths, which was an accidental
|
|
5
|
+
* safety property: `.gitignore` is where projects keep secrets (`.env`, private
|
|
6
|
+
* keys, credentials). The CAS substrate's whole purpose is to start capturing
|
|
7
|
+
* ignored / non-git paths — so, done naively, it would write secret bytes into
|
|
8
|
+
* durable artifact storage. This module is the fail-closed gate that prevents
|
|
9
|
+
* that: a path classified secret-like is BLOCKED from capture (authored as
|
|
10
|
+
* DIFF_UNREVIEWABLE instead) and its bytes never leave the workspace.
|
|
11
|
+
*
|
|
12
|
+
* Scope discipline (design doc 12 D2): this is deliberately a small, explicit,
|
|
13
|
+
* deterministic matcher — NOT the Phase-4 `sensitivity` taxonomy, NOT ML. It is
|
|
14
|
+
* a pure function over the path, so the cross-edition corpus can lock it (a path
|
|
15
|
+
* either blocks or captures, identically everywhere). The matcher errs toward
|
|
16
|
+
* blocking: a path only needs to LOOK secret-like to be withheld, because the
|
|
17
|
+
* cost of withholding a non-secret (it stays on the deny-gate, as today) is far
|
|
18
|
+
* lower than the cost of persisting a real secret.
|
|
19
|
+
*
|
|
20
|
+
* @since File-Change HITL Redesign (Phase 3 — CAS / DD-E)
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Basename patterns that mark a file secret-like. Matched case-insensitively
|
|
24
|
+
* against the final path segment. Kept as an explicit, reviewable list — adding
|
|
25
|
+
* a pattern is a deliberate, corpus-locked change, never a heuristic guess.
|
|
26
|
+
*/
|
|
27
|
+
export const SECRET_BASENAME_PATTERNS = [
|
|
28
|
+
/^\.env(\..+)?$/, // .env, .env.local, .env.production, ...
|
|
29
|
+
/^\.npmrc$/,
|
|
30
|
+
/^\.netrc$/,
|
|
31
|
+
/^\.pgpass$/,
|
|
32
|
+
/^\.htpasswd$/,
|
|
33
|
+
/^credentials(\.[A-Za-z0-9]+)?$/, // credentials, credentials.json, ...
|
|
34
|
+
/^secrets?(\.[A-Za-z0-9]+)?$/, // secret / secrets / secrets.yaml, ...
|
|
35
|
+
/^id_(rsa|dsa|ecdsa|ed25519)$/, // private SSH keys (no .pub)
|
|
36
|
+
/.*\.(pem|key|pfx|p12|keystore|jks|asc|ppk)$/,
|
|
37
|
+
/.*\.tfstate(\.backup)?$/, // terraform state embeds secrets
|
|
38
|
+
/.*\.tfvars$/,
|
|
39
|
+
/.*\.(kdbx)$/, // password databases
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Path-fragment patterns that mark a file secret-like by WHERE it lives, matched
|
|
43
|
+
* against the normalized (forward-slash) full path. Covers credential stores
|
|
44
|
+
* whose basenames alone are ambiguous.
|
|
45
|
+
*/
|
|
46
|
+
export const SECRET_PATH_PATTERNS = [
|
|
47
|
+
/(^|\/)\.ssh\//, // ~/.ssh/* and repo-local .ssh/*
|
|
48
|
+
/(^|\/)\.aws\/credentials$/,
|
|
49
|
+
/(^|\/)\.gnupg\//,
|
|
50
|
+
/(^|\/)\.docker\/config\.json$/,
|
|
51
|
+
/(^|\/)\.kube\/config$/,
|
|
52
|
+
];
|
|
53
|
+
/** The final path segment (basename), for both `/` and `\` separators. */
|
|
54
|
+
function basename(path) {
|
|
55
|
+
const normalized = path.replace(/\\/g, "/");
|
|
56
|
+
const idx = normalized.lastIndexOf("/");
|
|
57
|
+
return idx >= 0 ? normalized.slice(idx + 1) : normalized;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether a path is secret-like and must NOT be captured into the durable CAS
|
|
61
|
+
* store. Fail-closed: an empty/unknown path is treated as secret-like (withheld)
|
|
62
|
+
* rather than captured.
|
|
63
|
+
*/
|
|
64
|
+
export function isSecretLikePath(path) {
|
|
65
|
+
if (!path)
|
|
66
|
+
return true;
|
|
67
|
+
const normalized = path.replace(/\\/g, "/");
|
|
68
|
+
const base = basename(normalized).toLowerCase();
|
|
69
|
+
for (const re of SECRET_BASENAME_PATTERNS) {
|
|
70
|
+
if (re.test(base))
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
const lowerPath = normalized.toLowerCase();
|
|
74
|
+
for (const re of SECRET_PATH_PATTERNS) {
|
|
75
|
+
if (re.test(lowerPath))
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Split a turn's observed gitignored paths into capturable vs withheld — the
|
|
82
|
+
* single source of truth for "which ignored bytes may reach durable storage".
|
|
83
|
+
*
|
|
84
|
+
* `unreviewablePaths = gateBlockedPaths ∪ { p ∈ observed : isSecretLikePath(p) }`
|
|
85
|
+
* and `capturablePaths = { p ∈ observed : ¬isSecretLikePath(p) }`. The
|
|
86
|
+
* `isSecretLikePath` re-check over `observed` is the fail-closed backstop that
|
|
87
|
+
* holds **even under the global bypass** (`spec.auto_approve_all`): there the
|
|
88
|
+
* approval gate is not installed, so `gateBlockedPaths` is empty and this
|
|
89
|
+
* re-check is the only thing keeping a secret's bytes out of CAS. Order is
|
|
90
|
+
* preserved (gate blocks first, then observed order) and duplicates are folded,
|
|
91
|
+
* so the result is deterministic for the cross-edition corpus.
|
|
92
|
+
*/
|
|
93
|
+
export function partitionIgnoredPathsBySecret(observedPaths, gateBlockedPaths) {
|
|
94
|
+
const unreviewable = new Set(gateBlockedPaths);
|
|
95
|
+
const capturablePaths = [];
|
|
96
|
+
for (const path of observedPaths) {
|
|
97
|
+
if (isSecretLikePath(path)) {
|
|
98
|
+
unreviewable.add(path);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
capturablePaths.push(path);
|
|
102
|
+
}
|
|
103
|
+
return { capturablePaths, unreviewablePaths: [...unreviewable] };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=secret-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-paths.js","sourceRoot":"","sources":["../../../src/shared/filereview/secret-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,gBAAgB,EAAE,yCAAyC;IAC3D,WAAW;IACX,WAAW;IACX,YAAY;IACZ,cAAc;IACd,gCAAgC,EAAE,qCAAqC;IACvE,6BAA6B,EAAE,uCAAuC;IACtE,8BAA8B,EAAE,6BAA6B;IAC7D,6CAA6C;IAC7C,yBAAyB,EAAE,iCAAiC;IAC5D,aAAa;IACb,aAAa,EAAE,qBAAqB;CACrC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,eAAe,EAAE,iCAAiC;IAClD,2BAA2B;IAC3B,iBAAiB;IACjB,+BAA+B;IAC/B,uBAAuB;CACxB,CAAC;AAEF,0EAA0E;AAC1E,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IAEhD,KAAK,MAAM,EAAE,IAAI,wBAAwB,EAAE,CAAC;QAC1C,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACjC,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IAC3C,KAAK,MAAM,EAAE,IAAI,oBAAoB,EAAE,CAAC;QACtC,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAaD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,6BAA6B,CAC3C,aAA+B,EAC/B,gBAAqC;IAErC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,gBAAgB,CAAC,CAAC;IACvD,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC;AACnE,CAAC"}
|