@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,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runner's file-review event producer: maps a harness-agnostic capture into
|
|
3
|
+
* the proto {@code CapturedFileChange} + {@code FileReviewEvent} ledger entries
|
|
4
|
+
* the server folds (append-only, by event_id) into the server-owned stream.
|
|
5
|
+
*
|
|
6
|
+
* This is the runner half of the apply-then-review subsystem. It is intentionally
|
|
7
|
+
* harness-agnostic — it takes per-file before/after content and a kind, not a
|
|
8
|
+
* git-specific shape — so the Cursor (git substrate) and deep-agent (mutation
|
|
9
|
+
* buffer) harnesses author IDENTICAL ledger entries through one seam. Event ids
|
|
10
|
+
* are deterministic ({@link eventId}) so a re-sent heartbeat or a Temporal retry
|
|
11
|
+
* never duplicates an event.
|
|
12
|
+
*/
|
|
13
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
14
|
+
import type { CapturedFileChange, FileReviewEvent, SnapshotRef, TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
15
|
+
import { DiffCompleteness, FileCaptureClass, FileChangeKind, FileReviewBlockReason, FileReviewEventType, FileReviewFailureKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
16
|
+
/**
|
|
17
|
+
* The deterministic idempotency key for a file-review event:
|
|
18
|
+
* `changeSetId:scopeId:eventTypeName`. Capture/reconcile/failure events are
|
|
19
|
+
* change-set-scoped (scopeId == changeSetId); the per-file decision scope is the
|
|
20
|
+
* server's concern. Identical to the Go/Java EventID.
|
|
21
|
+
*/
|
|
22
|
+
export declare function eventId(changeSetId: string, scopeId: string, type: FileReviewEventType): string;
|
|
23
|
+
/**
|
|
24
|
+
* One side (before/after) of a captured file's content, in one of three shapes:
|
|
25
|
+
*
|
|
26
|
+
* - `inline` — the raw text bytes, carried on the wire (the git substrate reads
|
|
27
|
+
* them from the tree for text files). A plain string is accepted as shorthand.
|
|
28
|
+
* - `ref` — a reference to an already-stored, content-addressed blob (the CAS
|
|
29
|
+
* substrate offloads ignored / non-git bodies to artifact storage and carries
|
|
30
|
+
* the ref, so the body is never stored twice).
|
|
31
|
+
* - `binary` — a git-tracked binary side: NO body is carried (a binary has no
|
|
32
|
+
* reviewable text diff, and reconcile sources its bytes byte-exact from the
|
|
33
|
+
* git ref, never from the wire), only the byte-true content address. This is
|
|
34
|
+
* what keeps the enforcement digest honest for binaries — hashing a UTF-8
|
|
35
|
+
* decode of binary bytes would be lossy.
|
|
36
|
+
*/
|
|
37
|
+
export type CapturedContent = {
|
|
38
|
+
readonly kind: "inline";
|
|
39
|
+
readonly text: string;
|
|
40
|
+
/**
|
|
41
|
+
* Byte-true SHA-256 of the RAW bytes, when the producer computed it at the
|
|
42
|
+
* source (the git substrate does). Preferred over hashing `text`, which for
|
|
43
|
+
* a non-UTF-8 blob (e.g. latin-1 with no NUL) would hash a lossy decode.
|
|
44
|
+
* Absent for the plain-string shorthand, where `text` IS the exact content.
|
|
45
|
+
*/
|
|
46
|
+
readonly sha256?: string;
|
|
47
|
+
} | {
|
|
48
|
+
readonly kind: "ref";
|
|
49
|
+
/** Content address of the referenced blob (the enforcement digest). */
|
|
50
|
+
readonly sha256: string;
|
|
51
|
+
readonly storageKey: string;
|
|
52
|
+
readonly sizeBytes: number;
|
|
53
|
+
readonly isBinary: boolean;
|
|
54
|
+
readonly mimeType?: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: "binary";
|
|
57
|
+
/** Byte-true SHA-256 of the raw bytes (the enforcement digest). */
|
|
58
|
+
readonly sha256: string;
|
|
59
|
+
};
|
|
60
|
+
/** A single captured file delta, harness-agnostic (no git/CAS specifics). */
|
|
61
|
+
export interface CapturedChangeInput {
|
|
62
|
+
/** Stable id of this file change within its set (correlation key, never a hash). */
|
|
63
|
+
readonly id: string;
|
|
64
|
+
/** Path before the change (workspace-root-relative). Empty for ADD. */
|
|
65
|
+
readonly pathBefore: string;
|
|
66
|
+
/** Path after the change. Empty for DELETE; differs from pathBefore for RENAME. */
|
|
67
|
+
readonly pathAfter: string;
|
|
68
|
+
readonly kind: FileChangeKind;
|
|
69
|
+
/** Which substrate captured this file (git-tracked, ignored, CAS, ...). */
|
|
70
|
+
readonly captureClass: FileCaptureClass;
|
|
71
|
+
/** Pre-edit content (inline string, or a blob ref). Omit for ADD. */
|
|
72
|
+
readonly before?: string | CapturedContent;
|
|
73
|
+
/** Post-edit content (inline string, or a blob ref). Omit for DELETE. */
|
|
74
|
+
readonly after?: string | CapturedContent;
|
|
75
|
+
/**
|
|
76
|
+
* False when this file's diff could not be captured completely (too large to
|
|
77
|
+
* persist inline before CAS exists, truncated, or binary-only). A change set
|
|
78
|
+
* with any incomplete file cannot be approved as complete.
|
|
79
|
+
*/
|
|
80
|
+
readonly diffComplete?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The honest cause the diff is not fully reviewable, set only alongside
|
|
83
|
+
* `diffComplete === false` (defaults to UNSPECIFIED). Informational provenance
|
|
84
|
+
* for the review UI — never an enforcement input and never folded into the
|
|
85
|
+
* digests. Binary is conveyed by FileContent.is_binary, so binary files leave
|
|
86
|
+
* this UNSPECIFIED. See {@link FileReviewBlockReason}.
|
|
87
|
+
*/
|
|
88
|
+
readonly blockedReason?: FileReviewBlockReason;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The enforcement digest of one content side. Inline text is hashed as UTF-8
|
|
92
|
+
* bytes; a `ref`/`binary` side already carries its byte-true content address, so
|
|
93
|
+
* it is reused verbatim. This single function is the sole sha authority — used
|
|
94
|
+
* both at capture (in {@link buildCapturedFileChange}) and at the resume-time
|
|
95
|
+
* reconcile check — so the two are consistent by construction across substrates.
|
|
96
|
+
*/
|
|
97
|
+
export declare function contentSha256(content: CapturedContent): string;
|
|
98
|
+
/**
|
|
99
|
+
* Build a proto {@link CapturedFileChange} from harness-agnostic input,
|
|
100
|
+
* computing the enforcement digests (`before_sha256`, `after_sha256`,
|
|
101
|
+
* `file_digest`) over the captured bytes. Content may be inline (git) or a blob
|
|
102
|
+
* ref (CAS) — the digests are identical either way, so the aggregate digest and
|
|
103
|
+
* the reconcile enforcement compose across both substrates.
|
|
104
|
+
*/
|
|
105
|
+
export declare function buildCapturedFileChange(input: CapturedChangeInput): CapturedFileChange;
|
|
106
|
+
/** Coordinates a change set's lifecycle so every event carries one identity. */
|
|
107
|
+
export interface ChangeSetContext {
|
|
108
|
+
readonly changeSetId: string;
|
|
109
|
+
readonly turnId: string;
|
|
110
|
+
/** "cursor" | "deep-agent". */
|
|
111
|
+
readonly harnessId: string;
|
|
112
|
+
/** ISO 8601 timestamp authored onto each event. */
|
|
113
|
+
readonly timestamp: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The BASELINE_CAPTURED event — the first event of a change set's lifecycle,
|
|
117
|
+
* authored at turn start so the projection can materialize the set before any
|
|
118
|
+
* candidate exists.
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildBaselineCapturedEvent(ctx: ChangeSetContext, baselineSnapshot: SnapshotRef | undefined): FileReviewEvent;
|
|
121
|
+
/**
|
|
122
|
+
* Derive a change set's {@link DiffCompleteness} rollup from its per-file
|
|
123
|
+
* signals — the single source of the three-way rule, shared by the turn-boundary
|
|
124
|
+
* capture ({@link buildCandidateCapturedEvent}) and the size backstop
|
|
125
|
+
* (status-offload), so the value can never be computed two ways:
|
|
126
|
+
*
|
|
127
|
+
* - every file complete → `COMPLETE`
|
|
128
|
+
* - else the ONLY incompleteness is binary (every incomplete file has a binary
|
|
129
|
+
* side) → `BINARY_SUMMARY_ONLY` — keepable in one acknowledged action, its
|
|
130
|
+
* exact bytes reconcile from the git ref / CAS blob
|
|
131
|
+
* - else (≥1 non-binary incomplete: secret-withheld / size-elided /
|
|
132
|
+
* uncapturable — no keepable bytes) → `PARTIAL_BLOCKED`
|
|
133
|
+
*
|
|
134
|
+
* Binary is proven by `FileContent.is_binary` on either side (DD-15 D2), never
|
|
135
|
+
* by the block reason. This mirrors the backend gate's `isBinaryChange`, so the
|
|
136
|
+
* runner's rollup and the gate's per-file re-derivation agree by construction.
|
|
137
|
+
*/
|
|
138
|
+
export declare function deriveDiffCompleteness(changes: readonly CapturedFileChange[]): DiffCompleteness;
|
|
139
|
+
/**
|
|
140
|
+
* The CANDIDATE_CAPTURED event — authored at the turn boundary, carrying the
|
|
141
|
+
* authoritative per-file diff and the aggregate digest. `diff_completeness` is
|
|
142
|
+
* the {@link deriveDiffCompleteness} rollup over the changes.
|
|
143
|
+
*
|
|
144
|
+
* `commandProvenance` (optional, DD-28) is the harness's approved-command turn
|
|
145
|
+
* facts: when present, the backend verifies the cited consent rows against its
|
|
146
|
+
* server-authored approval record and — on success — auto-keeps the set with a
|
|
147
|
+
* policy-origin decision instead of arming the review gate. Never folded into
|
|
148
|
+
* the aggregate digest (provenance, not content).
|
|
149
|
+
*/
|
|
150
|
+
export declare function buildCandidateCapturedEvent(ctx: ChangeSetContext, candidateSnapshot: SnapshotRef | undefined, changes: readonly CapturedFileChange[], commandProvenance?: TurnCommandProvenance): FileReviewEvent;
|
|
151
|
+
/** The RECONCILED event — authored after decisions are applied and verified. */
|
|
152
|
+
export declare function buildReconciledEvent(ctx: ChangeSetContext, approvedSnapshot: SnapshotRef | undefined): FileReviewEvent;
|
|
153
|
+
/** A terminal FAILED event in the file-review lifecycle (audit + resume). */
|
|
154
|
+
export declare function buildFailedEvent(ctx: ChangeSetContext, kind: FileReviewFailureKind, detail: string): FileReviewEvent;
|
|
155
|
+
/**
|
|
156
|
+
* Whether a CANDIDATE_CAPTURED event exists for `changeSetId` on the status's
|
|
157
|
+
* file_review stream — i.e. this turn actually produced a reviewable change
|
|
158
|
+
* set. The capture seam authors no event for a no-op turn (an edit fully
|
|
159
|
+
* reverted before the boundary), so this is the single signal both harnesses
|
|
160
|
+
* gate on before opening a review or stamping transcript rows: a row must
|
|
161
|
+
* never reference a change set that does not exist.
|
|
162
|
+
*/
|
|
163
|
+
export declare function hasCandidateCaptured(status: AgentExecutionStatus, changeSetId: string): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Append the runner-authored events onto the status's file_review stream
|
|
166
|
+
* (seeding it if absent), idempotently by event_id. The server re-applies the
|
|
167
|
+
* same append-only/by-id discipline on receipt, so re-sending across throttled
|
|
168
|
+
* persists is safe; this keeps the in-runner status consistent between persists.
|
|
169
|
+
*/
|
|
170
|
+
export declare function appendFileReviewEvents(status: AgentExecutionStatus, executionId: string, events: readonly FileReviewEvent[]): void;
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runner's file-review event producer: maps a harness-agnostic capture into
|
|
3
|
+
* the proto {@code CapturedFileChange} + {@code FileReviewEvent} ledger entries
|
|
4
|
+
* the server folds (append-only, by event_id) into the server-owned stream.
|
|
5
|
+
*
|
|
6
|
+
* This is the runner half of the apply-then-review subsystem. It is intentionally
|
|
7
|
+
* harness-agnostic — it takes per-file before/after content and a kind, not a
|
|
8
|
+
* git-specific shape — so the Cursor (git substrate) and deep-agent (mutation
|
|
9
|
+
* buffer) harnesses author IDENTICAL ledger entries through one seam. Event ids
|
|
10
|
+
* are deterministic ({@link eventId}) so a re-sent heartbeat or a Temporal retry
|
|
11
|
+
* never duplicates an event.
|
|
12
|
+
*/
|
|
13
|
+
import { create } from "@bufbuild/protobuf";
|
|
14
|
+
import { CapturedFileChangeSchema, FileReviewBaselineCapturedSchema, FileReviewCandidateCapturedSchema, FileReviewEventSchema, FileReviewEventStreamSchema, FileReviewFailureSchema, FileReviewReconciledSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
15
|
+
import { FileContentSchema, ToolCallOutputRefSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
16
|
+
import { DiffCompleteness, FileReviewBlockReason, FileReviewEventType, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
17
|
+
import { aggregateDigest, fileDigest, sha256Bytes } from "./digest.js";
|
|
18
|
+
import { looksBinary } from "../file-change.js";
|
|
19
|
+
// The runner is the author of capture/reconcile/failure events. "runner" (not
|
|
20
|
+
// the generic "system") matches the cross-edition corpus fixtures and the
|
|
21
|
+
// proto's actor convention ("runner" for capture/reconcile, "user" for
|
|
22
|
+
// decisions). The projection ignores actor, but keeping it canonical keeps the
|
|
23
|
+
// ledger self-describing and the dual-edition corpus honest.
|
|
24
|
+
const ACTOR_RUNNER = "runner";
|
|
25
|
+
/**
|
|
26
|
+
* The proto enum value NAME for a {@link FileReviewEventType}, e.g.
|
|
27
|
+
* `FILE_REVIEW_EVENT_TYPE_BASELINE_CAPTURED`. {@link eventId} hashes this name so
|
|
28
|
+
* the runner's ids are byte-identical to the Go `author.EventID` / Java
|
|
29
|
+
* `FileReviewStreamAuthor.eventId` (which use the proto name).
|
|
30
|
+
*/
|
|
31
|
+
const FILE_REVIEW_EVENT_TYPE_NAME = {
|
|
32
|
+
[FileReviewEventType.UNSPECIFIED]: "FILE_REVIEW_EVENT_TYPE_UNSPECIFIED",
|
|
33
|
+
[FileReviewEventType.BASELINE_CAPTURED]: "FILE_REVIEW_EVENT_TYPE_BASELINE_CAPTURED",
|
|
34
|
+
[FileReviewEventType.CANDIDATE_CAPTURED]: "FILE_REVIEW_EVENT_TYPE_CANDIDATE_CAPTURED",
|
|
35
|
+
[FileReviewEventType.FILE_DECIDED]: "FILE_REVIEW_EVENT_TYPE_FILE_DECIDED",
|
|
36
|
+
[FileReviewEventType.RECONCILED]: "FILE_REVIEW_EVENT_TYPE_RECONCILED",
|
|
37
|
+
[FileReviewEventType.FAILED]: "FILE_REVIEW_EVENT_TYPE_FAILED",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The deterministic idempotency key for a file-review event:
|
|
41
|
+
* `changeSetId:scopeId:eventTypeName`. Capture/reconcile/failure events are
|
|
42
|
+
* change-set-scoped (scopeId == changeSetId); the per-file decision scope is the
|
|
43
|
+
* server's concern. Identical to the Go/Java EventID.
|
|
44
|
+
*/
|
|
45
|
+
export function eventId(changeSetId, scopeId, type) {
|
|
46
|
+
return `${changeSetId}:${scopeId}:${FILE_REVIEW_EVENT_TYPE_NAME[type]}`;
|
|
47
|
+
}
|
|
48
|
+
/** Normalize the string-shorthand to a {@link CapturedContent}. */
|
|
49
|
+
function normalizeContent(v) {
|
|
50
|
+
if (v === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
return typeof v === "string" ? { kind: "inline", text: v } : v;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The enforcement digest of one content side. Inline text is hashed as UTF-8
|
|
56
|
+
* bytes; a `ref`/`binary` side already carries its byte-true content address, so
|
|
57
|
+
* it is reused verbatim. This single function is the sole sha authority — used
|
|
58
|
+
* both at capture (in {@link buildCapturedFileChange}) and at the resume-time
|
|
59
|
+
* reconcile check — so the two are consistent by construction across substrates.
|
|
60
|
+
*/
|
|
61
|
+
export function contentSha256(content) {
|
|
62
|
+
switch (content.kind) {
|
|
63
|
+
case "inline":
|
|
64
|
+
return content.sha256 ?? sha256Bytes(Buffer.from(content.text, "utf8"));
|
|
65
|
+
case "ref":
|
|
66
|
+
case "binary":
|
|
67
|
+
return content.sha256;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Build the proto {@link FileContent} for one content side (inline, ref, or binary). */
|
|
71
|
+
function toFileContent(content) {
|
|
72
|
+
if (content.kind === "inline") {
|
|
73
|
+
return create(FileContentSchema, {
|
|
74
|
+
body: { case: "inline", value: content.text },
|
|
75
|
+
isBinary: looksBinary(content.text),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// A git-tracked binary carries no body: its bytes reconcile from the git ref,
|
|
79
|
+
// and a text diff of binary content is meaningless. Consumers key off the
|
|
80
|
+
// is_binary flag (the SDK renders "binary file changed"; the backend gates
|
|
81
|
+
// approval on it), never a body.
|
|
82
|
+
if (content.kind === "binary") {
|
|
83
|
+
return create(FileContentSchema, { isBinary: true });
|
|
84
|
+
}
|
|
85
|
+
return create(FileContentSchema, {
|
|
86
|
+
body: {
|
|
87
|
+
case: "ref",
|
|
88
|
+
value: create(ToolCallOutputRefSchema, {
|
|
89
|
+
storageKey: content.storageKey,
|
|
90
|
+
sizeBytes: BigInt(content.sizeBytes),
|
|
91
|
+
contentHash: content.sha256,
|
|
92
|
+
mimeType: content.mimeType ?? "application/octet-stream",
|
|
93
|
+
isImage: false,
|
|
94
|
+
truncatedPreview: "",
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
isBinary: content.isBinary,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build a proto {@link CapturedFileChange} from harness-agnostic input,
|
|
102
|
+
* computing the enforcement digests (`before_sha256`, `after_sha256`,
|
|
103
|
+
* `file_digest`) over the captured bytes. Content may be inline (git) or a blob
|
|
104
|
+
* ref (CAS) — the digests are identical either way, so the aggregate digest and
|
|
105
|
+
* the reconcile enforcement compose across both substrates.
|
|
106
|
+
*/
|
|
107
|
+
export function buildCapturedFileChange(input) {
|
|
108
|
+
const before = normalizeContent(input.before);
|
|
109
|
+
const after = normalizeContent(input.after);
|
|
110
|
+
const beforeSha256 = before ? contentSha256(before) : "";
|
|
111
|
+
const afterSha256 = after ? contentSha256(after) : "";
|
|
112
|
+
const fc = create(CapturedFileChangeSchema, {
|
|
113
|
+
id: input.id,
|
|
114
|
+
pathBefore: input.pathBefore,
|
|
115
|
+
pathAfter: input.pathAfter,
|
|
116
|
+
kind: input.kind,
|
|
117
|
+
captureClass: input.captureClass,
|
|
118
|
+
beforeSha256,
|
|
119
|
+
afterSha256,
|
|
120
|
+
diffComplete: input.diffComplete ?? true,
|
|
121
|
+
blockedReason: input.blockedReason ?? FileReviewBlockReason.UNSPECIFIED,
|
|
122
|
+
fileDigest: fileDigest({
|
|
123
|
+
pathBefore: input.pathBefore,
|
|
124
|
+
pathAfter: input.pathAfter,
|
|
125
|
+
kind: input.kind,
|
|
126
|
+
beforeSha256,
|
|
127
|
+
afterSha256,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
if (before) {
|
|
131
|
+
fc.before = toFileContent(before);
|
|
132
|
+
}
|
|
133
|
+
if (after) {
|
|
134
|
+
fc.after = toFileContent(after);
|
|
135
|
+
}
|
|
136
|
+
return fc;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The BASELINE_CAPTURED event — the first event of a change set's lifecycle,
|
|
140
|
+
* authored at turn start so the projection can materialize the set before any
|
|
141
|
+
* candidate exists.
|
|
142
|
+
*/
|
|
143
|
+
export function buildBaselineCapturedEvent(ctx, baselineSnapshot) {
|
|
144
|
+
return create(FileReviewEventSchema, {
|
|
145
|
+
eventId: eventId(ctx.changeSetId, ctx.changeSetId, FileReviewEventType.BASELINE_CAPTURED),
|
|
146
|
+
changeSetId: ctx.changeSetId,
|
|
147
|
+
eventType: FileReviewEventType.BASELINE_CAPTURED,
|
|
148
|
+
timestamp: ctx.timestamp,
|
|
149
|
+
actor: ACTOR_RUNNER,
|
|
150
|
+
payload: {
|
|
151
|
+
case: "baselineCaptured",
|
|
152
|
+
value: create(FileReviewBaselineCapturedSchema, {
|
|
153
|
+
changeSetId: ctx.changeSetId,
|
|
154
|
+
turnId: ctx.turnId,
|
|
155
|
+
harnessId: ctx.harnessId,
|
|
156
|
+
...(baselineSnapshot ? { baselineSnapshot } : {}),
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Derive a change set's {@link DiffCompleteness} rollup from its per-file
|
|
163
|
+
* signals — the single source of the three-way rule, shared by the turn-boundary
|
|
164
|
+
* capture ({@link buildCandidateCapturedEvent}) and the size backstop
|
|
165
|
+
* (status-offload), so the value can never be computed two ways:
|
|
166
|
+
*
|
|
167
|
+
* - every file complete → `COMPLETE`
|
|
168
|
+
* - else the ONLY incompleteness is binary (every incomplete file has a binary
|
|
169
|
+
* side) → `BINARY_SUMMARY_ONLY` — keepable in one acknowledged action, its
|
|
170
|
+
* exact bytes reconcile from the git ref / CAS blob
|
|
171
|
+
* - else (≥1 non-binary incomplete: secret-withheld / size-elided /
|
|
172
|
+
* uncapturable — no keepable bytes) → `PARTIAL_BLOCKED`
|
|
173
|
+
*
|
|
174
|
+
* Binary is proven by `FileContent.is_binary` on either side (DD-15 D2), never
|
|
175
|
+
* by the block reason. This mirrors the backend gate's `isBinaryChange`, so the
|
|
176
|
+
* runner's rollup and the gate's per-file re-derivation agree by construction.
|
|
177
|
+
*/
|
|
178
|
+
export function deriveDiffCompleteness(changes) {
|
|
179
|
+
let sawIncomplete = false;
|
|
180
|
+
for (const c of changes) {
|
|
181
|
+
if (c.diffComplete)
|
|
182
|
+
continue;
|
|
183
|
+
sawIncomplete = true;
|
|
184
|
+
if (!(c.before?.isBinary || c.after?.isBinary)) {
|
|
185
|
+
// A non-binary incompleteness has no keepable bytes — it blocks the set
|
|
186
|
+
// outright, regardless of any binaries alongside it.
|
|
187
|
+
return DiffCompleteness.PARTIAL_BLOCKED;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!sawIncomplete)
|
|
191
|
+
return DiffCompleteness.COMPLETE;
|
|
192
|
+
return DiffCompleteness.BINARY_SUMMARY_ONLY;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The CANDIDATE_CAPTURED event — authored at the turn boundary, carrying the
|
|
196
|
+
* authoritative per-file diff and the aggregate digest. `diff_completeness` is
|
|
197
|
+
* the {@link deriveDiffCompleteness} rollup over the changes.
|
|
198
|
+
*
|
|
199
|
+
* `commandProvenance` (optional, DD-28) is the harness's approved-command turn
|
|
200
|
+
* facts: when present, the backend verifies the cited consent rows against its
|
|
201
|
+
* server-authored approval record and — on success — auto-keeps the set with a
|
|
202
|
+
* policy-origin decision instead of arming the review gate. Never folded into
|
|
203
|
+
* the aggregate digest (provenance, not content).
|
|
204
|
+
*/
|
|
205
|
+
export function buildCandidateCapturedEvent(ctx, candidateSnapshot, changes, commandProvenance) {
|
|
206
|
+
return create(FileReviewEventSchema, {
|
|
207
|
+
eventId: eventId(ctx.changeSetId, ctx.changeSetId, FileReviewEventType.CANDIDATE_CAPTURED),
|
|
208
|
+
changeSetId: ctx.changeSetId,
|
|
209
|
+
eventType: FileReviewEventType.CANDIDATE_CAPTURED,
|
|
210
|
+
timestamp: ctx.timestamp,
|
|
211
|
+
actor: ACTOR_RUNNER,
|
|
212
|
+
payload: {
|
|
213
|
+
case: "candidateCaptured",
|
|
214
|
+
value: create(FileReviewCandidateCapturedSchema, {
|
|
215
|
+
changeSetId: ctx.changeSetId,
|
|
216
|
+
...(candidateSnapshot ? { candidateSnapshot } : {}),
|
|
217
|
+
changes: [...changes],
|
|
218
|
+
aggregateDigest: aggregateDigest(changes.map((c) => ({
|
|
219
|
+
pathBefore: c.pathBefore,
|
|
220
|
+
pathAfter: c.pathAfter,
|
|
221
|
+
kind: c.kind,
|
|
222
|
+
beforeSha256: c.beforeSha256,
|
|
223
|
+
afterSha256: c.afterSha256,
|
|
224
|
+
}))),
|
|
225
|
+
diffCompleteness: deriveDiffCompleteness(changes),
|
|
226
|
+
...(commandProvenance ? { commandProvenance } : {}),
|
|
227
|
+
}),
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/** The RECONCILED event — authored after decisions are applied and verified. */
|
|
232
|
+
export function buildReconciledEvent(ctx, approvedSnapshot) {
|
|
233
|
+
return create(FileReviewEventSchema, {
|
|
234
|
+
eventId: eventId(ctx.changeSetId, ctx.changeSetId, FileReviewEventType.RECONCILED),
|
|
235
|
+
changeSetId: ctx.changeSetId,
|
|
236
|
+
eventType: FileReviewEventType.RECONCILED,
|
|
237
|
+
timestamp: ctx.timestamp,
|
|
238
|
+
actor: ACTOR_RUNNER,
|
|
239
|
+
payload: {
|
|
240
|
+
case: "reconciled",
|
|
241
|
+
value: create(FileReviewReconciledSchema, {
|
|
242
|
+
changeSetId: ctx.changeSetId,
|
|
243
|
+
...(approvedSnapshot ? { approvedSnapshot } : {}),
|
|
244
|
+
}),
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/** A terminal FAILED event in the file-review lifecycle (audit + resume). */
|
|
249
|
+
export function buildFailedEvent(ctx, kind, detail) {
|
|
250
|
+
return create(FileReviewEventSchema, {
|
|
251
|
+
eventId: eventId(ctx.changeSetId, ctx.changeSetId, FileReviewEventType.FAILED),
|
|
252
|
+
changeSetId: ctx.changeSetId,
|
|
253
|
+
eventType: FileReviewEventType.FAILED,
|
|
254
|
+
timestamp: ctx.timestamp,
|
|
255
|
+
actor: ACTOR_RUNNER,
|
|
256
|
+
payload: {
|
|
257
|
+
case: "failed",
|
|
258
|
+
value: create(FileReviewFailureSchema, { changeSetId: ctx.changeSetId, kind, detail }),
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Whether a CANDIDATE_CAPTURED event exists for `changeSetId` on the status's
|
|
264
|
+
* file_review stream — i.e. this turn actually produced a reviewable change
|
|
265
|
+
* set. The capture seam authors no event for a no-op turn (an edit fully
|
|
266
|
+
* reverted before the boundary), so this is the single signal both harnesses
|
|
267
|
+
* gate on before opening a review or stamping transcript rows: a row must
|
|
268
|
+
* never reference a change set that does not exist.
|
|
269
|
+
*/
|
|
270
|
+
export function hasCandidateCaptured(status, changeSetId) {
|
|
271
|
+
return (status.fileReviewEventStream?.events ?? []).some((e) => e.changeSetId === changeSetId &&
|
|
272
|
+
e.eventType === FileReviewEventType.CANDIDATE_CAPTURED);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Append the runner-authored events onto the status's file_review stream
|
|
276
|
+
* (seeding it if absent), idempotently by event_id. The server re-applies the
|
|
277
|
+
* same append-only/by-id discipline on receipt, so re-sending across throttled
|
|
278
|
+
* persists is safe; this keeps the in-runner status consistent between persists.
|
|
279
|
+
*/
|
|
280
|
+
export function appendFileReviewEvents(status, executionId, events) {
|
|
281
|
+
if (events.length === 0)
|
|
282
|
+
return;
|
|
283
|
+
if (!status.fileReviewEventStream) {
|
|
284
|
+
status.fileReviewEventStream = create(FileReviewEventStreamSchema, {
|
|
285
|
+
executionId,
|
|
286
|
+
events: [],
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const stream = status.fileReviewEventStream;
|
|
290
|
+
const seen = new Set(stream.events.map((e) => e.eventId));
|
|
291
|
+
for (const ev of events) {
|
|
292
|
+
if (seen.has(ev.eventId))
|
|
293
|
+
continue;
|
|
294
|
+
seen.add(ev.eventId);
|
|
295
|
+
stream.events.push(ev);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/shared/filereview/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
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 { FileChangeType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
64
|
+
import type { CapturedContent } from "./events.js";
|
|
65
|
+
/**
|
|
66
|
+
* Refs that pin the per-execution baseline and after trees against GC. Exported
|
|
67
|
+
* so the file-review producer can stamp them onto the ledger's SnapshotRef
|
|
68
|
+
* (GitTreeRef.ref) without re-deriving the format. Keyed by executionId only:
|
|
69
|
+
* the unified gate guarantees at most one un-reconciled change set per execution
|
|
70
|
+
* at a time, and the reconcile drops these refs before the next turn re-pins.
|
|
71
|
+
*/
|
|
72
|
+
export declare function baselineRef(executionId: string): string;
|
|
73
|
+
export declare function captureRef(executionId: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* A single file mutation captured from the git diff between the baseline and
|
|
76
|
+
* after trees. `path` is repo-relative (forward slashes); `changeType` drives
|
|
77
|
+
* restore/apply. `before`/`after` are the harness-agnostic {@link CapturedContent}
|
|
78
|
+
* for each side (absent per changeType: no `before` for a CREATE, no `after` for
|
|
79
|
+
* a DELETE) — text is carried inline, binary carries only its byte-true content
|
|
80
|
+
* address (its bytes reconcile byte-exact from the git ref, never the wire).
|
|
81
|
+
*/
|
|
82
|
+
export interface GitSubstrateChange {
|
|
83
|
+
/** Repo-relative path (the diff's path; the destination for a create/modify). */
|
|
84
|
+
readonly path: string;
|
|
85
|
+
readonly changeType: FileChangeType;
|
|
86
|
+
/** Pre-edit content (absent for a CREATE). */
|
|
87
|
+
readonly before?: CapturedContent;
|
|
88
|
+
/** Post-edit content (absent for a DELETE). */
|
|
89
|
+
readonly after?: CapturedContent;
|
|
90
|
+
}
|
|
91
|
+
/** Result of {@link captureChangeSet} / {@link recomputeChangeSet}. */
|
|
92
|
+
export interface CaptureResult {
|
|
93
|
+
/**
|
|
94
|
+
* The pre-turn tree sha (pinned behind the baseline ref). On resume this is
|
|
95
|
+
* the authoritative source for reverting rejected/undecided files exactly.
|
|
96
|
+
*/
|
|
97
|
+
readonly baselineTree: string;
|
|
98
|
+
/** The post-turn tree sha, also pinned behind the capture ref. */
|
|
99
|
+
readonly afterTree: string;
|
|
100
|
+
/** One entry per changed file (excluding the passed runner-owned paths). */
|
|
101
|
+
readonly changes: readonly GitSubstrateChange[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Whether `dir` is inside a git work tree (the capability switch: a real git
|
|
105
|
+
* workspace gets capture mode, anything else keeps the deny-gate fallback).
|
|
106
|
+
*/
|
|
107
|
+
export declare function isGitWorkTree(dir: string): Promise<boolean>;
|
|
108
|
+
/**
|
|
109
|
+
* Whether a repo-relative path is capturable by this substrate — i.e. it would
|
|
110
|
+
* appear in the `git add -A` snapshot. A gitignored path is NOT capturable: the
|
|
111
|
+
* snapshot cannot see it, so it can be neither reviewed nor reverted, and a
|
|
112
|
+
* capture-mode gate must keep gating it. Mirrors the Cursor hook's
|
|
113
|
+
* `__stigmer_is_gitignored` check: `git check-ignore -q` exits 0 when the path IS
|
|
114
|
+
* ignored, non-zero otherwise; a non-git context / error is treated as
|
|
115
|
+
* not-ignored (capturable), matching the hook's allow-on-error behavior.
|
|
116
|
+
*/
|
|
117
|
+
export declare function isPathCapturable(gitRoot: string, path: string): Promise<boolean>;
|
|
118
|
+
/**
|
|
119
|
+
* Snapshot the working tree at turn start and pin it behind the baseline ref.
|
|
120
|
+
* Returns the baseline tree sha (used by {@link captureChangeSet} and
|
|
121
|
+
* {@link restoreToBaseline} later in the SAME activity invocation). `excludePaths`
|
|
122
|
+
* are the runner-owned files the harness writes into the workspace (omit for none).
|
|
123
|
+
*/
|
|
124
|
+
export declare function snapshotBaseline(gitRoot: string, executionId: string, excludePaths?: readonly string[]): Promise<string>;
|
|
125
|
+
/**
|
|
126
|
+
* Capture the change set the turn produced: write the post-turn working tree,
|
|
127
|
+
* pin it behind the capture ref (so it survives the approval wait), and diff it
|
|
128
|
+
* against `baselineTree` into a per-file {@link GitSubstrateChange} list.
|
|
129
|
+
*
|
|
130
|
+
* Renames are intentionally NOT detected (`--no-renames`): a rename surfaces as
|
|
131
|
+
* a delete + create, which restores and applies exactly and matches the per-file
|
|
132
|
+
* review model.
|
|
133
|
+
*/
|
|
134
|
+
export declare function captureChangeSet(gitRoot: string, executionId: string, baselineTree: string, excludePaths?: readonly string[]): Promise<CaptureResult>;
|
|
135
|
+
/**
|
|
136
|
+
* Restore the given files to their `baselineTree` (pre-turn) bytes. On resume
|
|
137
|
+
* this reverts the REJECTED/undecided subset so a discarded change snaps back
|
|
138
|
+
* exactly; it is the generic "ensure baseline bytes" primitive. Surgical
|
|
139
|
+
* (bounded by the passed change set): an agent-created file is removed; an
|
|
140
|
+
* agent-modified or agent-deleted file is rewritten with its baseline bytes.
|
|
141
|
+
* `.stigmer` and other ignored paths are never touched (they are not in the
|
|
142
|
+
* change set). Idempotent — safe to re-run (e.g. on a Temporal retry).
|
|
143
|
+
*/
|
|
144
|
+
export declare function restoreToBaseline(gitRoot: string, baselineTree: string, changes: readonly GitSubstrateChange[]): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Ensure the APPROVED files hold their captured "after" bytes, as uncommitted
|
|
147
|
+
* working-tree changes (no commit). In capture mode the agent's edits are left
|
|
148
|
+
* applied through the review window, so on resume this is normally an idempotent
|
|
149
|
+
* re-assert of bytes already on disk; sourcing from the pinned "after" tree also
|
|
150
|
+
* makes it self-healing if the tree was reset (Temporal retry / sandbox
|
|
151
|
+
* recycle). An approved CREATE/MODIFY writes the after bytes; an approved DELETE
|
|
152
|
+
* removes the file. Rejected/undecided paths are not passed in (they go to
|
|
153
|
+
* {@link restoreToBaseline}).
|
|
154
|
+
*/
|
|
155
|
+
export declare function applyApprovedPaths(gitRoot: string, afterTree: string, approved: readonly GitSubstrateChange[]): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Recompute the captured change set on a RESUME, from the pinned baseline and
|
|
158
|
+
* capture refs — the authoritative source that survived the approval wait,
|
|
159
|
+
* independent of any persisted fidelity. Returns `undefined` when this was not a
|
|
160
|
+
* capture turn (the capture ref is absent), so the caller can fall through to the
|
|
161
|
+
* non-capture path.
|
|
162
|
+
*/
|
|
163
|
+
export declare function recomputeChangeSet(gitRoot: string, executionId: string): Promise<CaptureResult | undefined>;
|
|
164
|
+
/**
|
|
165
|
+
* Snapshot the post-reconcile working tree (after approved bytes were applied and
|
|
166
|
+
* rejected files were reverted) and pin it behind the approved ref. Returns the
|
|
167
|
+
* approved tree sha for the RECONCILED event's `approved_snapshot`, plus the ref
|
|
168
|
+
* so the producer can stamp the SnapshotRef. Idempotent under a Temporal retry.
|
|
169
|
+
*/
|
|
170
|
+
export declare function snapshotApproved(gitRoot: string, executionId: string, excludePaths?: readonly string[]): Promise<{
|
|
171
|
+
treeOid: string;
|
|
172
|
+
ref: string;
|
|
173
|
+
}>;
|
|
174
|
+
/** Release the per-execution baseline, capture, and approved refs (idempotent). */
|
|
175
|
+
export declare function dropCaptureRefs(gitRoot: string, executionId: string): Promise<void>;
|