@stigmer/runner 3.0.9-dev.20260615153829 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/approval-contract/types.d.ts +174 -0
- package/dist/__test-utils__/approval-contract/types.js +24 -0
- package/dist/__test-utils__/approval-contract/types.js.map +1 -0
- package/dist/activities/call-agent-status.d.ts +19 -1
- package/dist/activities/call-agent-status.js +64 -5
- package/dist/activities/call-agent-status.js.map +1 -1
- package/dist/activities/call-llm.js +19 -53
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +24 -0
- package/dist/activities/classify-tool-approvals.js +69 -17
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -0
- package/dist/activities/discover-mcp-server.js +11 -1
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +140 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +130 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.d.ts +18 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +123 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-cursor/approval-policy.d.ts +17 -61
- package/dist/activities/execute-cursor/approval-policy.js +8 -120
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +219 -9
- package/dist/activities/execute-cursor/approval-state.js +233 -17
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +127 -0
- package/dist/activities/execute-cursor/capture-flow.js +234 -0
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -0
- package/dist/activities/execute-cursor/cas-observations.d.ts +96 -0
- package/dist/activities/execute-cursor/cas-observations.js +184 -0
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -0
- package/dist/activities/execute-cursor/command-provenance.d.ts +62 -0
- package/dist/activities/execute-cursor/command-provenance.js +128 -0
- package/dist/activities/execute-cursor/command-provenance.js.map +1 -0
- package/dist/activities/execute-cursor/exact-apply.d.ts +110 -0
- package/dist/activities/execute-cursor/exact-apply.js +204 -0
- package/dist/activities/execute-cursor/exact-apply.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +53 -24
- package/dist/activities/execute-cursor/hook-script.js +310 -47
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +8 -1
- package/dist/activities/execute-cursor/index.js +500 -64
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +258 -11
- package/dist/activities/execute-cursor/message-translator.js +836 -90
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +20 -23
- package/dist/activities/execute-cursor/prompt-builder.js +74 -15
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/workspace-setup.d.ts +17 -0
- package/dist/activities/execute-cursor/workspace-setup.js +212 -33
- package/dist/activities/execute-cursor/workspace-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.d.ts +19 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js +143 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +88 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +81 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -0
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +47 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +68 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -0
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +8 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +7 -7
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +42 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +47 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +79 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +112 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -0
- package/dist/activities/execute-deep-agent/hitl.d.ts +16 -3
- package/dist/activities/execute-deep-agent/hitl.js +11 -3
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.d.ts +2 -1
- package/dist/activities/execute-deep-agent/index.js +393 -33
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +7 -1
- package/dist/activities/execute-deep-agent/inline-publisher.js +23 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +53 -2
- package/dist/activities/execute-deep-agent/setup.js +149 -92
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +36 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +56 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +1 -0
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +34 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.js +26 -25
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +17 -5
- package/dist/activities/execute-deep-agent/status-builder.js +44 -3
- package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +2 -19
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming.js +3 -15
- package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js +23 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +30 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js +29 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js +32 -2
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +9 -5
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +2 -0
- package/dist/client/stigmer-client.js +2 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +85 -4
- package/dist/middleware/approval-gate.js +165 -38
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/types.d.ts +2 -5
- package/dist/shared/activity-input.d.ts +43 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.js.map +1 -0
- package/dist/shared/approval-canonicalize.d.ts +19 -0
- package/dist/shared/approval-canonicalize.js +119 -0
- package/dist/shared/approval-canonicalize.js.map +1 -0
- package/dist/shared/approval-fingerprint.d.ts +106 -0
- package/dist/shared/approval-fingerprint.js +113 -0
- package/dist/shared/approval-fingerprint.js.map +1 -0
- package/dist/shared/approval-policy.d.ts +182 -12
- package/dist/shared/approval-policy.js +213 -27
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +52 -0
- package/dist/shared/args-preview.js +93 -0
- package/dist/shared/args-preview.js.map +1 -0
- package/dist/shared/artifact-storage.d.ts +19 -1
- package/dist/shared/artifact-storage.js +48 -11
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/file-change.d.ts +44 -0
- package/dist/shared/file-change.js +57 -0
- package/dist/shared/file-change.js.map +1 -0
- package/dist/shared/file-tools.d.ts +107 -0
- package/dist/shared/file-tools.js +168 -0
- package/dist/shared/file-tools.js.map +1 -0
- package/dist/shared/filereview/capture.d.ts +202 -0
- package/dist/shared/filereview/capture.js +498 -0
- package/dist/shared/filereview/capture.js.map +1 -0
- package/dist/shared/filereview/cas-substrate.d.ts +190 -0
- package/dist/shared/filereview/cas-substrate.js +284 -0
- package/dist/shared/filereview/cas-substrate.js.map +1 -0
- package/dist/shared/filereview/digest.d.ts +40 -0
- package/dist/shared/filereview/digest.js +66 -0
- package/dist/shared/filereview/digest.js.map +1 -0
- package/dist/shared/filereview/events.d.ts +170 -0
- package/dist/shared/filereview/events.js +298 -0
- package/dist/shared/filereview/events.js.map +1 -0
- package/dist/shared/filereview/git-substrate.d.ts +175 -0
- package/dist/shared/filereview/git-substrate.js +439 -0
- package/dist/shared/filereview/git-substrate.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +11 -0
- package/dist/shared/filereview/index.js +12 -0
- package/dist/shared/filereview/index.js.map +1 -0
- package/dist/shared/filereview/secret-paths.d.ts +63 -0
- package/dist/shared/filereview/secret-paths.js +105 -0
- package/dist/shared/filereview/secret-paths.js.map +1 -0
- package/dist/shared/fingerprint-secret.d.ts +26 -0
- package/dist/shared/fingerprint-secret.js +47 -0
- package/dist/shared/fingerprint-secret.js.map +1 -0
- package/dist/shared/model-client.d.ts +51 -0
- package/dist/shared/model-client.js +77 -0
- package/dist/shared/model-client.js.map +1 -0
- package/dist/shared/plan-artifact.js +0 -2
- package/dist/shared/plan-artifact.js.map +1 -1
- package/dist/shared/status-offload.d.ts +83 -9
- package/dist/shared/status-offload.js +399 -79
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/status.js +14 -1
- package/dist/shared/status.js.map +1 -1
- package/dist/shared/tool-kind.d.ts +19 -0
- package/dist/shared/tool-kind.js +13 -0
- package/dist/shared/tool-kind.js.map +1 -1
- package/dist/shared/tool-row.d.ts +88 -0
- package/dist/shared/tool-row.js +127 -0
- package/dist/shared/tool-row.js.map +1 -0
- package/dist/shared/workspace/platform-dir.d.ts +25 -0
- package/dist/shared/workspace/platform-dir.js +38 -2
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.js +56 -7
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.d.ts +50 -0
- package/dist/workflows/connect-mcp-server.js +136 -15
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/types.d.ts +8 -0
- package/package.json +2 -2
- package/src/__test-utils__/approval-contract/contract.ts +224 -0
- package/src/__test-utils__/approval-contract/types.ts +179 -0
- package/src/__test-utils__/fake-artifact-storage.ts +72 -0
- package/src/__tests__/approval-gateway-contract.test.ts +29 -0
- package/src/__tests__/claimcheck-codec.test.ts +16 -53
- package/src/__tests__/golden-e2e.test.ts +2 -0
- package/src/__tests__/runner-token-coordinator.test.ts +3 -3
- package/src/activities/__tests__/call-agent-status.test.ts +135 -0
- package/src/activities/__tests__/call-llm.test.ts +1 -1
- package/src/activities/__tests__/classify-tool-approvals.test.ts +208 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +30 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +2 -1
- package/src/activities/call-agent-status.ts +74 -4
- package/src/activities/call-llm.ts +18 -63
- package/src/activities/classify-tool-approvals.ts +101 -19
- package/src/activities/discover-mcp-server.ts +29 -1
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +216 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +148 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +41 -9
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +292 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +68 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +1005 -0
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +187 -0
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +97 -0
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +240 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +203 -0
- package/src/activities/execute-cursor/__tests__/exact-apply.test.ts +375 -0
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +1294 -24
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +446 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +384 -110
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +171 -25
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +189 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +44 -23
- package/src/activities/execute-cursor/__tests__/workspace-setup.test.ts +190 -10
- package/src/activities/execute-cursor/approval-policy.ts +28 -159
- package/src/activities/execute-cursor/approval-state.ts +366 -18
- package/src/activities/execute-cursor/capture-flow.ts +323 -0
- package/src/activities/execute-cursor/cas-observations.ts +204 -0
- package/src/activities/execute-cursor/command-provenance.ts +168 -0
- package/src/activities/execute-cursor/exact-apply.ts +253 -0
- package/src/activities/execute-cursor/hook-script.ts +317 -51
- package/src/activities/execute-cursor/index.ts +575 -67
- package/src/activities/execute-cursor/message-translator.ts +963 -89
- package/src/activities/execute-cursor/prompt-builder.ts +80 -14
- package/src/activities/execute-cursor/workspace-setup.ts +257 -42
- package/src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts +180 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +134 -0
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +11 -24
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +64 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-observer.test.ts +163 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +8 -5
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +342 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +284 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +14 -36
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +31 -13
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +349 -0
- package/src/activities/execute-deep-agent/__tests__/stamp-flowed-rows.test.ts +119 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +91 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +9 -9
- package/src/activities/execute-deep-agent/__tests__/subagent-approval-propagation.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +213 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +3 -6
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +84 -1
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +4 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +80 -0
- package/src/activities/execute-deep-agent/attachment-injector.ts +20 -11
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +66 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +125 -0
- package/src/activities/execute-deep-agent/hitl.ts +23 -7
- package/src/activities/execute-deep-agent/index.ts +463 -40
- package/src/activities/execute-deep-agent/inline-publisher.ts +27 -4
- package/src/activities/execute-deep-agent/setup.ts +223 -125
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +64 -0
- package/src/activities/execute-deep-agent/status-builder-shared.ts +62 -23
- package/src/activities/execute-deep-agent/status-builder.ts +63 -8
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +2 -16
- package/src/activities/execute-deep-agent/streaming.ts +3 -13
- package/src/activities/execute-deep-agent/subagent-transformer.ts +53 -13
- package/src/activities/execute-deep-agent/subagent-wiring.ts +50 -3
- package/src/activities/execute-deep-agent/v3-status-builder.ts +36 -2
- package/src/claimcheck/payload-codec.ts +8 -8
- package/src/client/stigmer-client.ts +9 -1
- package/src/middleware/__tests__/approval-gate.test.ts +488 -4
- package/src/middleware/approval-gate.ts +247 -38
- package/src/middleware/types.ts +5 -5
- package/src/shared/__tests__/activity-input.test.ts +78 -0
- package/src/shared/__tests__/approval-canonicalize.test.ts +106 -0
- package/src/shared/__tests__/approval-fingerprint.test.ts +115 -0
- package/src/shared/__tests__/approval-policy.test.ts +274 -40
- package/src/shared/__tests__/args-preview.test.ts +78 -0
- package/src/shared/__tests__/artifact-storage-extended.test.ts +62 -10
- package/src/shared/__tests__/artifact-storage.test.ts +123 -11
- package/src/shared/__tests__/file-change.test.ts +85 -0
- package/src/shared/__tests__/file-tools.test.ts +90 -0
- package/src/shared/__tests__/fingerprint-secret.test.ts +51 -0
- package/src/shared/__tests__/lease-scope-corpus.test.ts +56 -0
- package/src/shared/__tests__/model-client.test.ts +162 -0
- package/src/shared/__tests__/plan-artifact.test.ts +11 -26
- package/src/shared/__tests__/policy-source-corpus.test.ts +58 -0
- package/src/shared/__tests__/status-offload.test.ts +573 -16
- package/src/shared/__tests__/status.test.ts +4 -5
- package/src/shared/__tests__/tool-kind.test.ts +24 -1
- package/src/shared/__tests__/tool-row.test.ts +221 -0
- package/src/shared/activity-input.ts +57 -0
- package/src/shared/approval-canonicalize.ts +159 -0
- package/src/shared/approval-fingerprint.ts +148 -0
- package/src/shared/approval-policy.ts +303 -27
- package/src/shared/args-preview.ts +98 -0
- package/src/shared/artifact-storage.ts +62 -11
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +1 -2
- package/src/shared/file-change.ts +64 -0
- package/src/shared/file-tools.ts +169 -0
- package/src/shared/filereview/__tests__/capture.test.ts +856 -0
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +404 -0
- package/src/shared/filereview/__tests__/digest.test.ts +100 -0
- package/src/shared/filereview/__tests__/events.test.ts +245 -0
- package/src/shared/filereview/__tests__/git-substrate.test.ts +362 -0
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +286 -0
- package/src/shared/filereview/__tests__/secret-paths.test.ts +121 -0
- package/src/shared/filereview/capture.ts +727 -0
- package/src/shared/filereview/cas-substrate.ts +401 -0
- package/src/shared/filereview/digest.ts +83 -0
- package/src/shared/filereview/events.ts +449 -0
- package/src/shared/filereview/git-substrate.ts +555 -0
- package/src/shared/filereview/index.ts +60 -0
- package/src/shared/filereview/secret-paths.ts +121 -0
- package/src/shared/fingerprint-secret.ts +53 -0
- package/src/shared/model-client.ts +122 -0
- package/src/shared/plan-artifact.ts +0 -2
- package/src/shared/status-offload.ts +433 -77
- package/src/shared/status.ts +13 -0
- package/src/shared/tool-kind.ts +33 -0
- package/src/shared/tool-row.ts +135 -0
- package/src/shared/workspace/platform-dir.ts +41 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +35 -18
- package/src/workflow-engine/__tests__/tasks/try.test.ts +1 -1
- package/src/workflows/__tests__/connect-mcp-server.test.ts +304 -29
- package/src/workflows/call-agent-orchestrator.ts +53 -6
- package/src/workflows/connect-mcp-server.ts +179 -24
- package/src/workflows/types.ts +8 -0
|
@@ -9,16 +9,25 @@
|
|
|
9
9
|
* incremental git writeback, and post-stream safety net.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { join } from "node:path";
|
|
12
14
|
import { Context, CancelledFailure } from "@temporalio/activity";
|
|
13
|
-
import { create, type JsonObject } from "@bufbuild/protobuf";
|
|
15
|
+
import { create, clone, type JsonObject } from "@bufbuild/protobuf";
|
|
14
16
|
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
17
|
+
import type { AgentExecution, AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
15
18
|
import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
16
|
-
import { ExecutionPhase, InteractionMode, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
19
|
+
import { ExecutionPhase, FileCaptureClass, FileChangeSetStatus, InteractionMode, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
17
20
|
import { activityStarted, activityFinished } from "../../idle-watchdog.js";
|
|
21
|
+
import { normalizeActivityInput, type ExecuteActivityInput } from "../../shared/activity-input.js";
|
|
18
22
|
import { persistStatus, slimStatus, utcTimestamp } from "../../shared/status.js";
|
|
19
23
|
import type { ToolOutputOffloadContext } from "../../shared/status-offload.js";
|
|
20
24
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
21
25
|
import { classifyTool } from "../../shared/tool-kind.js";
|
|
26
|
+
import {
|
|
27
|
+
POLICY_ENGINE_VERSION,
|
|
28
|
+
toProtoPolicySource,
|
|
29
|
+
type PolicySource,
|
|
30
|
+
} from "../../shared/approval-policy.js";
|
|
22
31
|
import type { Config } from "../../config.js";
|
|
23
32
|
import { StigmerClient } from "../../client/stigmer-client.js";
|
|
24
33
|
import { performSetup, type SetupResult } from "./setup.js";
|
|
@@ -28,7 +37,22 @@ import { StatusBuilder } from "./status-builder.js";
|
|
|
28
37
|
import { InlinePublisher } from "./inline-publisher.js";
|
|
29
38
|
import { WriteBackCoordinator } from "./writeback-coordinator.js";
|
|
30
39
|
import { processPostStream } from "./post-stream.js";
|
|
31
|
-
import { resolveResumeInput } from "./hitl.js";
|
|
40
|
+
import { resolveResumeInput, type GraphStateSnapshot } from "./hitl.js";
|
|
41
|
+
import { captureApprovalArtifacts } from "./approval-file-change.js";
|
|
42
|
+
import {
|
|
43
|
+
applyCaptureDecisions,
|
|
44
|
+
captureBaselineToLedger,
|
|
45
|
+
captureCandidateToLedger,
|
|
46
|
+
} from "../../shared/filereview/capture.js";
|
|
47
|
+
import { hasCandidateCaptured } from "../../shared/filereview/events.js";
|
|
48
|
+
import { casBlobReader, type CasPathCapture } from "../../shared/filereview/cas-substrate.js";
|
|
49
|
+
import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-paths.js";
|
|
50
|
+
import type { CasCaptureObserver } from "./cas-capture-observer.js";
|
|
51
|
+
import { collectSubAgentToolCallIds } from "../../shared/tool-row.js";
|
|
52
|
+
import { stampFlowedFileEditRows, stampFlowedSubAgentFileEditRows } from "./stamp-flowed-rows.js";
|
|
53
|
+
|
|
54
|
+
/** The harness id stamped on the deep-agent's file-review ledger events. */
|
|
55
|
+
const DEEP_AGENT_HARNESS_ID = "deep-agent";
|
|
32
56
|
|
|
33
57
|
export function createDeepAgentActivities(config: Config) {
|
|
34
58
|
const client = new StigmerClient({
|
|
@@ -40,7 +64,15 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
40
64
|
const streamingConfig = loadStreamingConfig();
|
|
41
65
|
|
|
42
66
|
return {
|
|
43
|
-
|
|
67
|
+
// Accepts the new typed object OR the legacy positional args (transitional
|
|
68
|
+
// dual-shape so the runner can deploy before the control planes — see
|
|
69
|
+
// shared/activity-input.ts). Drop the positional arm once both control
|
|
70
|
+
// planes send the object.
|
|
71
|
+
ExecuteDeepAgent: async (
|
|
72
|
+
arg0: ExecuteActivityInput | string,
|
|
73
|
+
arg1?: string,
|
|
74
|
+
): Promise<unknown> => {
|
|
75
|
+
const { executionId, threadId, turnSeq } = normalizeActivityInput(arg0, arg1);
|
|
44
76
|
activityStarted();
|
|
45
77
|
let setup: SetupResult | null = null;
|
|
46
78
|
|
|
@@ -53,25 +85,38 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
53
85
|
// oversized tool outputs (e.g. computer-use screenshots) to artifact
|
|
54
86
|
// storage so the UI can render them, and keep the status under the
|
|
55
87
|
// gRPC cap. Threaded into the streaming loop and reused for the
|
|
56
|
-
// terminal persists below so the guard is never skipped.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
// terminal persists below so the guard is never skipped. Undefined when
|
|
89
|
+
// there is no artifact store (proxy misconfig): offload is then disabled,
|
|
90
|
+
// but persistStatus still enforces the aggregate size cap, so persistence
|
|
91
|
+
// can never silently blow past the gRPC limit.
|
|
92
|
+
const statusOffload: ToolOutputOffloadContext | undefined = setup.artifactStorage
|
|
93
|
+
? { artifactStorage: setup.artifactStorage, executionId }
|
|
94
|
+
: undefined;
|
|
95
|
+
|
|
96
|
+
// Read the graph checkpoint once, to resolve how a pending approval
|
|
97
|
+
// interrupt continues (Command(resume) vs a fresh replay). Reading once
|
|
98
|
+
// avoids a redundant round-trip on the durable (http) saver.
|
|
99
|
+
const graphState: GraphStateSnapshot = await setup.agentGraph.getState(setup.langgraphConfig);
|
|
100
|
+
|
|
101
|
+
// Seed from the persisted transcript whenever the execution already has
|
|
102
|
+
// committed history, so streamed deltas append onto it instead of
|
|
103
|
+
// replacing it. This holds for every re-invocation — durable resume and
|
|
104
|
+
// memory-checkpointer replay alike — and is a no-op on a first run.
|
|
105
|
+
const initialStatus = shouldSeedFromPersistedTranscript(setup.execution)
|
|
106
|
+
? seedStatusFromExecution(setup.execution)
|
|
107
|
+
: create(AgentExecutionStatusSchema, {});
|
|
63
108
|
const statusBuilder = new StatusBuilder(executionId, initialStatus);
|
|
64
109
|
|
|
65
110
|
statusBuilder.setApprovalProvider({
|
|
66
111
|
policies: setup.approvalPolicies,
|
|
67
112
|
toolServerMap: setup.toolServerMap,
|
|
68
|
-
|
|
113
|
+
leasedCategories: setup.leasedCategories,
|
|
114
|
+
globalBypass: setup.globalBypass,
|
|
69
115
|
});
|
|
70
116
|
|
|
71
|
-
const resume =
|
|
117
|
+
const resume = resolveResumeInput(
|
|
72
118
|
setup.execution,
|
|
73
|
-
|
|
74
|
-
setup.langgraphConfig,
|
|
119
|
+
graphState,
|
|
75
120
|
setup.execution.spec!.message,
|
|
76
121
|
);
|
|
77
122
|
|
|
@@ -114,6 +159,116 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
114
159
|
})
|
|
115
160
|
: null;
|
|
116
161
|
|
|
162
|
+
// Apply-then-review capture identity + substrate root for this turn.
|
|
163
|
+
const gitRoot = setup.workspaceBackend.rootDir;
|
|
164
|
+
const changeSetId = `${executionId}:${turnSeq}`;
|
|
165
|
+
|
|
166
|
+
// (1) Capture-mode resume — reconcile any DECIDED change set FIRST (this
|
|
167
|
+
// drops the per-execution refs), before the next baseline re-pins them
|
|
168
|
+
// (refs are executionId-keyed). Then (2) short-circuit a pure file-review
|
|
169
|
+
// resume — the agent already finished in a prior segment, so finalize with
|
|
170
|
+
// NO model re-invocation. A mixed turn (a tool gate is also pending) falls
|
|
171
|
+
// through to resume the graph for the gated tool.
|
|
172
|
+
if (setup.captureMode) {
|
|
173
|
+
const decidedSets = (setup.execution.status?.fileChangeSets ?? []).filter(
|
|
174
|
+
(cs) => cs.status === FileChangeSetStatus.DECIDED,
|
|
175
|
+
);
|
|
176
|
+
let reconciledAny = false;
|
|
177
|
+
let reconcileFailed = false;
|
|
178
|
+
let reconcileFailureDetail = "";
|
|
179
|
+
// The CAS blob reader reconciles ignored-file decisions from the durable
|
|
180
|
+
// manifest (git-only turns have no manifest, so the CAS branch no-ops).
|
|
181
|
+
// Undefined with no store: this block still runs for a git workspace
|
|
182
|
+
// whose captureMode is true without storage, but such a turn captures no
|
|
183
|
+
// CAS files (captureIgnored is off), so its change set carries no CAS ref
|
|
184
|
+
// and applyCaptureDecisions never needs the reader.
|
|
185
|
+
const casReadBlob = setup.artifactStorage
|
|
186
|
+
? casBlobReader(setup.artifactStorage)
|
|
187
|
+
: undefined;
|
|
188
|
+
for (const changeSet of decidedSets) {
|
|
189
|
+
const capResult = await applyCaptureDecisions({
|
|
190
|
+
status: initialStatus,
|
|
191
|
+
gitRoot,
|
|
192
|
+
executionId,
|
|
193
|
+
changeSet,
|
|
194
|
+
harnessId: DEEP_AGENT_HARNESS_ID,
|
|
195
|
+
storage: setup.artifactStorage,
|
|
196
|
+
readBlob: casReadBlob,
|
|
197
|
+
gitWorkspace: setup.gitWorkspace,
|
|
198
|
+
});
|
|
199
|
+
if (!capResult.isCaptureTurn) continue;
|
|
200
|
+
reconciledAny = true;
|
|
201
|
+
if (capResult.failed) {
|
|
202
|
+
reconcileFailed = true;
|
|
203
|
+
reconcileFailureDetail =
|
|
204
|
+
capResult.failureDetail ?? "file review reconcile failed";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (reconciledAny) {
|
|
209
|
+
if (reconcileFailed) {
|
|
210
|
+
// What-you-approve-is-what-applies failed: nothing was applied.
|
|
211
|
+
initialStatus.phase = ExecutionPhase.EXECUTION_FAILED;
|
|
212
|
+
initialStatus.error = `File review reconcile failed: ${reconcileFailureDetail}`;
|
|
213
|
+
initialStatus.completedAt = utcTimestamp();
|
|
214
|
+
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
215
|
+
return slimStatus(initialStatus);
|
|
216
|
+
}
|
|
217
|
+
if (!hasPendingToolApprovals(setup.execution)) {
|
|
218
|
+
// Pure file review: the agent's final answer + outputs already rode
|
|
219
|
+
// the prior segment's persisted status (seeded into initialStatus).
|
|
220
|
+
// Complete now and push only the approved tree — NO model re-run.
|
|
221
|
+
//
|
|
222
|
+
// The discriminator is the PERSISTED transcript, not the live graph
|
|
223
|
+
// checkpoint: the memory checkpointer (OSS local / desktop) recreates
|
|
224
|
+
// the checkpoint empty each invocation, so a graphState-based check
|
|
225
|
+
// would wrongly fire for a mixed turn. A mixed turn leaves
|
|
226
|
+
// WAITING_APPROVAL tool rows in the transcript; a pure file-review
|
|
227
|
+
// turn leaves none.
|
|
228
|
+
initialStatus.phase = ExecutionPhase.EXECUTION_COMPLETED;
|
|
229
|
+
initialStatus.completedAt = utcTimestamp();
|
|
230
|
+
if (writebackCoordinator) {
|
|
231
|
+
await processCaptureWriteback(writebackCoordinator, executionId);
|
|
232
|
+
}
|
|
233
|
+
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
234
|
+
// Surface the agent's final outputs (computed in the prior segment,
|
|
235
|
+
// carried on the seeded status) the same way a normal completion does.
|
|
236
|
+
const slim = slimStatus(initialStatus) as Record<string, unknown>;
|
|
237
|
+
const lastAi = [...initialStatus.messages]
|
|
238
|
+
.reverse()
|
|
239
|
+
.find((m) => m.type === MessageType.MESSAGE_AI);
|
|
240
|
+
if (lastAi?.content) slim.final_text = lastAi.content;
|
|
241
|
+
if (initialStatus.structuredOutput !== undefined) {
|
|
242
|
+
slim.structured = initialStatus.structuredOutput;
|
|
243
|
+
}
|
|
244
|
+
return slim;
|
|
245
|
+
}
|
|
246
|
+
// else: mixed turn — fall through; the graph resumes for the gated
|
|
247
|
+
// tool and a new baseline is taken below for the continuation segment.
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Turn start (capture mode): pin the pre-turn tree + author BASELINE. Taken
|
|
252
|
+
// AFTER any reconcile above and AFTER performSetup's workspace writes, so
|
|
253
|
+
// the baseline absorbs the reconciled state and the runner-owned files.
|
|
254
|
+
let captureBaselineTree = "";
|
|
255
|
+
// Snapshot the sub-agent tool-call ids that exist BEFORE this turn's
|
|
256
|
+
// stream, so the turn-boundary stamp scopes itself to sub-agent rows
|
|
257
|
+
// created this turn (empty here by construction — this harness never
|
|
258
|
+
// seeds prior sub-agents — but computed, not assumed, so it stays correct
|
|
259
|
+
// if that ever changes; see collectSubAgentToolCallIds).
|
|
260
|
+
const priorSubAgentToolCallIds = collectSubAgentToolCallIds(initialStatus.subAgentExecutions);
|
|
261
|
+
if (setup.captureMode) {
|
|
262
|
+
captureBaselineTree = await captureBaselineToLedger({
|
|
263
|
+
status: initialStatus,
|
|
264
|
+
gitRoot,
|
|
265
|
+
executionId,
|
|
266
|
+
changeSetId,
|
|
267
|
+
harnessId: DEEP_AGENT_HARNESS_ID,
|
|
268
|
+
gitWorkspace: setup.gitWorkspace,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
117
272
|
const cancellationSignal = Context.current().cancellationSignal;
|
|
118
273
|
|
|
119
274
|
const result: StreamResult = await streamExecution({
|
|
@@ -127,13 +282,16 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
127
282
|
offload: statusOffload,
|
|
128
283
|
gracefulStop: setup.gracefulStop,
|
|
129
284
|
inlinePublisher,
|
|
130
|
-
|
|
285
|
+
// Capture mode: never push speculative edits during the turn. Writeback
|
|
286
|
+
// is deferred until the approved tree is reconciled (post-decision).
|
|
287
|
+
writebackCoordinator: setup.captureMode ? undefined : (writebackCoordinator ?? undefined),
|
|
131
288
|
heartbeatFn: (details) => Context.current().heartbeat(details),
|
|
132
289
|
isCancelledFn: () => cancellationSignal.aborted,
|
|
133
290
|
approvalProvider: {
|
|
134
291
|
policies: setup.approvalPolicies,
|
|
135
292
|
toolServerMap: setup.toolServerMap,
|
|
136
|
-
|
|
293
|
+
leasedCategories: setup.leasedCategories,
|
|
294
|
+
globalBypass: setup.globalBypass,
|
|
137
295
|
},
|
|
138
296
|
streamVersion: setup.streamVersion,
|
|
139
297
|
});
|
|
@@ -141,37 +299,94 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
141
299
|
await processPostStream({
|
|
142
300
|
status: initialStatus,
|
|
143
301
|
inlinePublisher,
|
|
144
|
-
|
|
302
|
+
// Capture mode: suppress the post-stream writeback finalize — the edits
|
|
303
|
+
// on disk are still speculative until reviewed. Writeback runs only on
|
|
304
|
+
// the approved tree (processCaptureWriteback, after reconcile).
|
|
305
|
+
writebackCoordinator: setup.captureMode ? null : writebackCoordinator,
|
|
145
306
|
pendingPublishPromises: result.pendingPublishPromises,
|
|
146
307
|
pendingWritebackPromises: result.pendingWritebackPromises,
|
|
147
308
|
executionId,
|
|
148
309
|
});
|
|
149
310
|
|
|
311
|
+
// Turn boundary (capture mode): capture the candidate change set from the
|
|
312
|
+
// git diff and author CANDIDATE_CAPTURED, then stamp the flowed file-edit
|
|
313
|
+
// rows with the change set id — they stay visible in place as
|
|
314
|
+
// observational records while file_change_sets remains the single
|
|
315
|
+
// decision surface. Skipped on an abnormal terminal (pause / stop /
|
|
316
|
+
// recursion limit) — there is no review to open. `fileReviewPending`
|
|
317
|
+
// then composes with any tool gate below.
|
|
318
|
+
let fileReviewPending = false;
|
|
319
|
+
const abnormalTerminal =
|
|
320
|
+
!!result.terminalStatus &&
|
|
321
|
+
initialStatus.phase !== ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
|
|
322
|
+
if (setup.captureMode && !abnormalTerminal) {
|
|
323
|
+
// Compose the CAS-captured paths (before-bytes from the observer,
|
|
324
|
+
// after-bytes re-read from disk) and the secret-blocked paths into the
|
|
325
|
+
// change set. The substrate class labels their provenance:
|
|
326
|
+
// GIT_IGNORED_CAPTURED for a git tree's ignored paths, NON_GIT_CAS for a
|
|
327
|
+
// non-git workspace (where these ARE the whole change set). In a git turn
|
|
328
|
+
// they compose with the git-tracked diff into one hybrid set; in a non-git
|
|
329
|
+
// turn they are the entire (CAS-only) set.
|
|
330
|
+
const casCaptureClass = setup.gitWorkspace
|
|
331
|
+
? FileCaptureClass.GIT_IGNORED_CAPTURED
|
|
332
|
+
: FileCaptureClass.NON_GIT_CAS;
|
|
333
|
+
const { casCaptures, unreviewablePaths } = await buildCasTurnCaptures(
|
|
334
|
+
setup.casObserver,
|
|
335
|
+
gitRoot,
|
|
336
|
+
casCaptureClass,
|
|
337
|
+
);
|
|
338
|
+
await captureCandidateToLedger({
|
|
339
|
+
status: initialStatus,
|
|
340
|
+
gitRoot,
|
|
341
|
+
executionId,
|
|
342
|
+
changeSetId,
|
|
343
|
+
baselineTree: captureBaselineTree,
|
|
344
|
+
harnessId: DEEP_AGENT_HARNESS_ID,
|
|
345
|
+
casCaptures,
|
|
346
|
+
storage: setup.artifactStorage,
|
|
347
|
+
unreviewablePaths,
|
|
348
|
+
unreviewableCaptureClass: casCaptureClass,
|
|
349
|
+
gitWorkspace: setup.gitWorkspace,
|
|
350
|
+
});
|
|
351
|
+
// Review is pending iff a CANDIDATE was actually authored (the seam
|
|
352
|
+
// drops no-op captures), so a turn that only touched-then-unchanged an
|
|
353
|
+
// ignored file does not open an empty review. Stamping is gated on the
|
|
354
|
+
// same signal: a row must never reference a change set that does not
|
|
355
|
+
// exist.
|
|
356
|
+
fileReviewPending = hasCandidateCaptured(initialStatus, changeSetId);
|
|
357
|
+
if (fileReviewPending) {
|
|
358
|
+
stampFlowedFileEditRows(initialStatus.messages, changeSetId);
|
|
359
|
+
stampFlowedSubAgentFileEditRows(
|
|
360
|
+
initialStatus.subAgentExecutions,
|
|
361
|
+
changeSetId,
|
|
362
|
+
priorSubAgentToolCallIds,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
150
367
|
if (result.terminalStatus) {
|
|
151
368
|
if (initialStatus.phase === ExecutionPhase.EXECUTION_PAUSED) {
|
|
152
369
|
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
153
370
|
console.log(`[ExecuteDeepAgent] Paused for execution ${executionId}: events=${result.eventsProcessed}`);
|
|
154
371
|
throw new CancelledFailure("Activity paused by orchestrator");
|
|
155
372
|
}
|
|
373
|
+
// Capture mode: a tool gate set WAITING during the stream while file
|
|
374
|
+
// edits also flowed this turn. The CANDIDATE event + stamped file-edit
|
|
375
|
+
// rows post-date the stream's slim, so persist + return the current
|
|
376
|
+
// status (both review surfaces — the file change set and the tool gate —
|
|
377
|
+
// are now pending). `!abnormalTerminal` means the capture block ran.
|
|
378
|
+
if (setup.captureMode && !abnormalTerminal) {
|
|
379
|
+
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
380
|
+
return slimStatus(initialStatus);
|
|
381
|
+
}
|
|
156
382
|
return result.terminalStatus;
|
|
157
383
|
}
|
|
158
384
|
|
|
159
|
-
if (!setup.
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.filter((intr) => intr.resumeValue === undefined)
|
|
165
|
-
.map((intr) => {
|
|
166
|
-
const val = intr.value as Record<string, unknown>;
|
|
167
|
-
return {
|
|
168
|
-
toolCallId: (val?.tool_call_id as string) ?? "",
|
|
169
|
-
toolName: (val?.tool_name as string) ?? "",
|
|
170
|
-
mcpServerSlug: (val?.mcp_server_slug as string) ?? "",
|
|
171
|
-
message: (val?.message as string) ?? "",
|
|
172
|
-
};
|
|
173
|
-
}),
|
|
174
|
-
) ?? [];
|
|
385
|
+
if (!setup.globalBypass) {
|
|
386
|
+
const postStreamGraphState = await setup.agentGraph.getState(setup.langgraphConfig);
|
|
387
|
+
const graphMessages = (postStreamGraphState.values as { messages?: unknown }).messages;
|
|
388
|
+
const aiMessages = Array.isArray(graphMessages) ? graphMessages : [];
|
|
389
|
+
const pendingInterrupts = detectPendingInterrupts(postStreamGraphState);
|
|
175
390
|
|
|
176
391
|
if (pendingInterrupts.length > 0) {
|
|
177
392
|
console.log(
|
|
@@ -187,7 +402,7 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
187
402
|
isStreaming: false,
|
|
188
403
|
});
|
|
189
404
|
for (const intr of pendingInterrupts) {
|
|
190
|
-
|
|
405
|
+
const toolCall = create(ToolCallSchema, {
|
|
191
406
|
id: intr.toolCallId,
|
|
192
407
|
name: intr.toolName,
|
|
193
408
|
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
@@ -197,7 +412,21 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
197
412
|
mcpServerSlug: intr.mcpServerSlug,
|
|
198
413
|
startedAt: utcTimestamp(),
|
|
199
414
|
toolKind: classifyTool(intr.toolName, intr.mcpServerSlug),
|
|
200
|
-
|
|
415
|
+
approvalPolicySource: toProtoPolicySource(intr.policySource),
|
|
416
|
+
policyEngineVersion: intr.policySource ? POLICY_ENGINE_VERSION : "",
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Capture a sanitized args preview while the graph is paused, so the
|
|
420
|
+
// approval UI renders the proposed change before the tool runs. Args
|
|
421
|
+
// are correlated from the AI-message tool call in graph state (the
|
|
422
|
+
// single source of truth).
|
|
423
|
+
const { argsPreview } = captureApprovalArtifacts({
|
|
424
|
+
toolCallId: intr.toolCallId,
|
|
425
|
+
messages: aiMessages,
|
|
426
|
+
});
|
|
427
|
+
if (argsPreview) toolCall.argsPreview = argsPreview;
|
|
428
|
+
|
|
429
|
+
aiMsg.toolCalls.push(toolCall);
|
|
201
430
|
}
|
|
202
431
|
initialStatus.messages.push(aiMsg);
|
|
203
432
|
|
|
@@ -206,8 +435,17 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
206
435
|
}
|
|
207
436
|
}
|
|
208
437
|
|
|
209
|
-
|
|
210
|
-
|
|
438
|
+
// Capture mode: file edits flowed but no tool gate fired — open file
|
|
439
|
+
// review instead of completing. The agent's final answer + outputs are
|
|
440
|
+
// still computed and persisted below, so the pure-file-review resume can
|
|
441
|
+
// complete from the seeded status with no model re-invocation.
|
|
442
|
+
const completeNow = !fileReviewPending;
|
|
443
|
+
initialStatus.phase = completeNow
|
|
444
|
+
? ExecutionPhase.EXECUTION_COMPLETED
|
|
445
|
+
: ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
|
|
446
|
+
if (completeNow) {
|
|
447
|
+
initialStatus.completedAt = utcTimestamp();
|
|
448
|
+
}
|
|
211
449
|
|
|
212
450
|
let structuredOutput: JsonObject | undefined;
|
|
213
451
|
let finalText: string | undefined;
|
|
@@ -257,9 +495,13 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
257
495
|
// first-class plan.md artifact so the UI can render a reviewable Plan
|
|
258
496
|
// card and a follow-up Implement run can reference it. Read-only mode
|
|
259
497
|
// produces no file to auto-publish, so this is the only artifact path.
|
|
498
|
+
// Requires artifact storage; with none (proxy misconfig) the plan text
|
|
499
|
+
// still lives in the final message — only the reviewable artifact is
|
|
500
|
+
// skipped (mirrors Cursor's storage-guarded plan publish).
|
|
260
501
|
if (
|
|
261
502
|
setup.execution.spec?.executionConfig?.interactionMode === InteractionMode.PLAN &&
|
|
262
|
-
finalText
|
|
503
|
+
finalText &&
|
|
504
|
+
setup.artifactStorage
|
|
263
505
|
) {
|
|
264
506
|
await publishPlanArtifact({
|
|
265
507
|
status: initialStatus,
|
|
@@ -269,10 +511,17 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
269
511
|
});
|
|
270
512
|
}
|
|
271
513
|
|
|
514
|
+
// Capture mode: push the approved tree exactly once, on terminal
|
|
515
|
+
// completion — never the speculative mid-turn edits.
|
|
516
|
+
if (setup.captureMode && completeNow && writebackCoordinator) {
|
|
517
|
+
await processCaptureWriteback(writebackCoordinator, executionId);
|
|
518
|
+
}
|
|
519
|
+
|
|
272
520
|
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
273
521
|
|
|
274
522
|
console.log(
|
|
275
|
-
`[ExecuteDeepAgent] Completed
|
|
523
|
+
`[ExecuteDeepAgent] ${completeNow ? "Completed" : "Awaiting file review for"} ` +
|
|
524
|
+
`execution ${executionId}: ` +
|
|
276
525
|
`events=${result.eventsProcessed}, ` +
|
|
277
526
|
`messages=${initialStatus.messages.length}, ` +
|
|
278
527
|
`artifacts=${initialStatus.artifacts.length}, ` +
|
|
@@ -280,6 +529,13 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
280
529
|
`hasStructuredOutput=${structuredOutput !== undefined}`,
|
|
281
530
|
);
|
|
282
531
|
|
|
532
|
+
// A WAITING (file-review) return carries no final outputs yet — those are
|
|
533
|
+
// surfaced when the pure-file-review resume completes from this persisted
|
|
534
|
+
// status. The COMPLETED return enriches the slim with final text/outputs.
|
|
535
|
+
if (!completeNow) {
|
|
536
|
+
return slimStatus(initialStatus);
|
|
537
|
+
}
|
|
538
|
+
|
|
283
539
|
const slim = slimStatus(initialStatus) as Record<string, unknown>;
|
|
284
540
|
if (finalText !== undefined) {
|
|
285
541
|
slim.final_text = finalText;
|
|
@@ -340,6 +596,173 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
340
596
|
};
|
|
341
597
|
}
|
|
342
598
|
|
|
599
|
+
/** A pending LangGraph approval interrupt, normalized from the graph checkpoint. */
|
|
600
|
+
interface PendingInterrupt {
|
|
601
|
+
readonly toolCallId: string;
|
|
602
|
+
readonly toolName: string;
|
|
603
|
+
readonly mcpServerSlug: string;
|
|
604
|
+
readonly message: string;
|
|
605
|
+
/** Gate provenance carried through the interrupt; undefined → UNSPECIFIED. */
|
|
606
|
+
readonly policySource: PolicySource | undefined;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Normalize the graph checkpoint's un-resumed interrupts into pending approvals.
|
|
611
|
+
* Used both before streaming (to detect whether a resume is a pure file review)
|
|
612
|
+
* and after (to seed the WAITING_FOR_APPROVAL tool rows).
|
|
613
|
+
*/
|
|
614
|
+
function detectPendingInterrupts(graphState: GraphStateSnapshot): PendingInterrupt[] {
|
|
615
|
+
const tasks = (graphState as {
|
|
616
|
+
tasks?: readonly {
|
|
617
|
+
interrupts?: readonly { value: Record<string, unknown>; resumeValue?: unknown }[];
|
|
618
|
+
}[];
|
|
619
|
+
}).tasks;
|
|
620
|
+
return (
|
|
621
|
+
tasks?.flatMap((task) =>
|
|
622
|
+
(task.interrupts ?? [])
|
|
623
|
+
.filter((intr) => intr.resumeValue === undefined)
|
|
624
|
+
.map((intr) => {
|
|
625
|
+
const val = intr.value as Record<string, unknown>;
|
|
626
|
+
return {
|
|
627
|
+
toolCallId: (val?.tool_call_id as string) ?? "",
|
|
628
|
+
toolName: (val?.tool_name as string) ?? "",
|
|
629
|
+
mcpServerSlug: (val?.mcp_server_slug as string) ?? "",
|
|
630
|
+
message: (val?.message as string) ?? "",
|
|
631
|
+
policySource: (val?.policy_source as PolicySource) || undefined,
|
|
632
|
+
};
|
|
633
|
+
}),
|
|
634
|
+
) ?? []
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Whether the persisted transcript holds any tool call awaiting approval — the
|
|
640
|
+
* checkpointer-independent signal that a resume must drive a tool gate (resume
|
|
641
|
+
* the graph), not just a file review. Scans root + sub-agent messages. A tool
|
|
642
|
+
* keeps `WAITING_APPROVAL` after the user decides (the decision rides
|
|
643
|
+
* `approval_action`) until the runner reconciles it, so this is true for a mixed
|
|
644
|
+
* turn's resume and false for a pure file-review turn.
|
|
645
|
+
*/
|
|
646
|
+
function hasPendingToolApprovals(execution: AgentExecution): boolean {
|
|
647
|
+
const status = execution.status;
|
|
648
|
+
if (!status) return false;
|
|
649
|
+
const anyWaiting = (msgs: { toolCalls: { status: ToolCallStatus }[] }[]): boolean =>
|
|
650
|
+
msgs.some((m) =>
|
|
651
|
+
m.toolCalls.some((tc) => tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL),
|
|
652
|
+
);
|
|
653
|
+
if (anyWaiting(status.messages)) return true;
|
|
654
|
+
return status.subAgentExecutions.some((sa) => anyWaiting(sa.messages));
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Assemble the turn's CAS captures and secret-blocked paths from the shared
|
|
659
|
+
* observer (the pre-turn bytes recorded by the parent AND every sub-agent CAS
|
|
660
|
+
* backend, plus the gate's secret-blocked set).
|
|
661
|
+
*
|
|
662
|
+
* For each observed CAS-owned path the after-bytes are re-read from disk (the
|
|
663
|
+
* authoritative net result of the turn; `null` when the file is gone). A path
|
|
664
|
+
* that is secret-like is diverted to `unreviewablePaths` and its before-bytes are
|
|
665
|
+
* dropped — the fail-closed backstop that holds even under the global bypass,
|
|
666
|
+
* where the gate did not run to block it up front. The result composes into the
|
|
667
|
+
* change set in {@link captureCandidateToLedger}.
|
|
668
|
+
*
|
|
669
|
+
* `captureClass` is the turn's CAS substrate class — GIT_IGNORED_CAPTURED for a
|
|
670
|
+
* git work tree's ignored paths, NON_GIT_CAS for a non-git workspace (where these
|
|
671
|
+
* ARE the whole change set) — so each captured path carries its true provenance.
|
|
672
|
+
*/
|
|
673
|
+
async function buildCasTurnCaptures(
|
|
674
|
+
observer: CasCaptureObserver,
|
|
675
|
+
workspaceRoot: string,
|
|
676
|
+
captureClass: FileCaptureClass,
|
|
677
|
+
): Promise<{ casCaptures: CasPathCapture[]; unreviewablePaths: string[] }> {
|
|
678
|
+
// The secret partition (pure, corpus-lockable) decides what may be captured;
|
|
679
|
+
// this shell only performs the IO for the capturable set. The backstop that
|
|
680
|
+
// withholds a secret observed under the global bypass lives in the partition.
|
|
681
|
+
const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
|
|
682
|
+
observer.before.keys(),
|
|
683
|
+
observer.blockedSecretPaths,
|
|
684
|
+
);
|
|
685
|
+
const casCaptures: CasPathCapture[] = [];
|
|
686
|
+
for (const relPath of capturablePaths) {
|
|
687
|
+
const after = await readFileOrNull(join(workspaceRoot, relPath));
|
|
688
|
+
casCaptures.push({
|
|
689
|
+
path: relPath,
|
|
690
|
+
before: observer.before.get(relPath) ?? null,
|
|
691
|
+
after,
|
|
692
|
+
captureClass,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
return { casCaptures, unreviewablePaths: [...unreviewablePaths] };
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** Raw bytes of a file, or `null` when it does not exist (a DELETE). */
|
|
699
|
+
async function readFileOrNull(absolutePath: string): Promise<Uint8Array | null> {
|
|
700
|
+
try {
|
|
701
|
+
return await readFile(absolutePath);
|
|
702
|
+
} catch {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Push the reconciled (approved) tree exactly once, on terminal completion in
|
|
709
|
+
* capture mode. The coordinator's {@link WriteBackCoordinator.finalize} commits +
|
|
710
|
+
* pushes whatever uncommitted changes remain — which, post-reconcile, is exactly
|
|
711
|
+
* the approved content (rejected files were already snapped back to baseline).
|
|
712
|
+
* Speculative mid-turn edits never reach here (writeback is suppressed during the
|
|
713
|
+
* turn). Fire-and-forget by the coordinator's own contract (errors logged).
|
|
714
|
+
*/
|
|
715
|
+
async function processCaptureWriteback(
|
|
716
|
+
writebackCoordinator: WriteBackCoordinator,
|
|
717
|
+
executionId: string,
|
|
718
|
+
): Promise<void> {
|
|
719
|
+
await writebackCoordinator.finalize();
|
|
720
|
+
console.log(`[ExecuteDeepAgent] capture writeback finalized for execution ${executionId}`);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Whether to seed the initial status from the persisted execution transcript.
|
|
725
|
+
*
|
|
726
|
+
* The Temporal workflow re-invokes ExecuteDeepAgent with the same thread_id on
|
|
727
|
+
* every continuation — HITL approval, pause/resume, and transient recovery. The
|
|
728
|
+
* runner owns the transcript and the server enforces it as append-only at
|
|
729
|
+
* identity, so any continuation must build ON the committed history rather than
|
|
730
|
+
* rebuilding from an empty proto and replacing it.
|
|
731
|
+
*
|
|
732
|
+
* The decision is therefore driven solely by whether the server already holds
|
|
733
|
+
* committed history for this execution — NOT by the live graph checkpoint, which
|
|
734
|
+
* the two checkpointers leave in different states for the same continuation:
|
|
735
|
+
* - durable (http): the checkpoint survives, the graph resumes via
|
|
736
|
+
* Command(resume), and streamEvents re-emits only post-checkpoint events;
|
|
737
|
+
* - memory (the OSS local / desktop default): the checkpoint is recreated
|
|
738
|
+
* empty every invocation, so the graph REPLAYS from scratch and the blind
|
|
739
|
+
* FIFO turns advance one gate at a time.
|
|
740
|
+
* Keying on the live checkpoint skipped seeding on that memory-replay path, so a
|
|
741
|
+
* second sequential gate emitted a transcript holding only the new gate, dropped
|
|
742
|
+
* the first gate's committed tool-call id, and tripped the server's append-only
|
|
743
|
+
* guard — the silent sequential-gate skip. Seeding whenever persisted history
|
|
744
|
+
* exists covers both paths; a re-emitted prior turn reconciles in place by
|
|
745
|
+
* tool-call id (StatusBuilder.rebuildToolCallIndex), so seeding never
|
|
746
|
+
* duplicates. False on a first run (no persisted messages), preserving the
|
|
747
|
+
* original start-from-empty behavior.
|
|
748
|
+
*/
|
|
749
|
+
function shouldSeedFromPersistedTranscript(execution: AgentExecution): boolean {
|
|
750
|
+
return (execution.status?.messages.length ?? 0) > 0;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Seed a fresh status from the persisted execution transcript so streamed
|
|
755
|
+
* deltas append onto existing history rather than replacing it. The clone keeps
|
|
756
|
+
* the persisted proto immutable. Terminal fields are cleared because the resumed
|
|
757
|
+
* run is in progress again; the StatusBuilder constructor re-sets the phase.
|
|
758
|
+
*/
|
|
759
|
+
function seedStatusFromExecution(execution: AgentExecution): AgentExecutionStatus {
|
|
760
|
+
const seeded = clone(AgentExecutionStatusSchema, execution.status!);
|
|
761
|
+
seeded.completedAt = "";
|
|
762
|
+
seeded.error = "";
|
|
763
|
+
return seeded;
|
|
764
|
+
}
|
|
765
|
+
|
|
343
766
|
async function cleanup(setup: SetupResult | null): Promise<void> {
|
|
344
767
|
if (!setup) return;
|
|
345
768
|
|