@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
|
@@ -9,17 +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
15
|
import { create, clone, type JsonObject } from "@bufbuild/protobuf";
|
|
14
16
|
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
15
17
|
import type { AgentExecution, AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
16
18
|
import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
17
|
-
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";
|
|
18
20
|
import { activityStarted, activityFinished } from "../../idle-watchdog.js";
|
|
21
|
+
import { normalizeActivityInput, type ExecuteActivityInput } from "../../shared/activity-input.js";
|
|
19
22
|
import { persistStatus, slimStatus, utcTimestamp } from "../../shared/status.js";
|
|
20
23
|
import type { ToolOutputOffloadContext } from "../../shared/status-offload.js";
|
|
21
24
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
22
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";
|
|
23
31
|
import type { Config } from "../../config.js";
|
|
24
32
|
import { StigmerClient } from "../../client/stigmer-client.js";
|
|
25
33
|
import { performSetup, type SetupResult } from "./setup.js";
|
|
@@ -30,6 +38,21 @@ import { InlinePublisher } from "./inline-publisher.js";
|
|
|
30
38
|
import { WriteBackCoordinator } from "./writeback-coordinator.js";
|
|
31
39
|
import { processPostStream } from "./post-stream.js";
|
|
32
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";
|
|
33
56
|
|
|
34
57
|
export function createDeepAgentActivities(config: Config) {
|
|
35
58
|
const client = new StigmerClient({
|
|
@@ -41,7 +64,15 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
41
64
|
const streamingConfig = loadStreamingConfig();
|
|
42
65
|
|
|
43
66
|
return {
|
|
44
|
-
|
|
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);
|
|
45
76
|
activityStarted();
|
|
46
77
|
let setup: SetupResult | null = null;
|
|
47
78
|
|
|
@@ -54,25 +85,24 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
54
85
|
// oversized tool outputs (e.g. computer-use screenshots) to artifact
|
|
55
86
|
// storage so the UI can render them, and keep the status under the
|
|
56
87
|
// gRPC cap. Threaded into the streaming loop and reused for the
|
|
57
|
-
// terminal persists below so the guard is never skipped.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// redundant round-trip on the durable (http) saver.
|
|
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.
|
|
68
99
|
const graphState: GraphStateSnapshot = await setup.agentGraph.getState(setup.langgraphConfig);
|
|
69
100
|
|
|
70
|
-
// Seed from the persisted transcript
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
const initialStatus = shouldSeedFromCheckpoint(graphState, setup.execution)
|
|
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)
|
|
76
106
|
? seedStatusFromExecution(setup.execution)
|
|
77
107
|
: create(AgentExecutionStatusSchema, {});
|
|
78
108
|
const statusBuilder = new StatusBuilder(executionId, initialStatus);
|
|
@@ -80,7 +110,8 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
80
110
|
statusBuilder.setApprovalProvider({
|
|
81
111
|
policies: setup.approvalPolicies,
|
|
82
112
|
toolServerMap: setup.toolServerMap,
|
|
83
|
-
|
|
113
|
+
leasedCategories: setup.leasedCategories,
|
|
114
|
+
globalBypass: setup.globalBypass,
|
|
84
115
|
});
|
|
85
116
|
|
|
86
117
|
const resume = resolveResumeInput(
|
|
@@ -128,6 +159,116 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
128
159
|
})
|
|
129
160
|
: null;
|
|
130
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
|
+
|
|
131
272
|
const cancellationSignal = Context.current().cancellationSignal;
|
|
132
273
|
|
|
133
274
|
const result: StreamResult = await streamExecution({
|
|
@@ -141,13 +282,16 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
141
282
|
offload: statusOffload,
|
|
142
283
|
gracefulStop: setup.gracefulStop,
|
|
143
284
|
inlinePublisher,
|
|
144
|
-
|
|
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),
|
|
145
288
|
heartbeatFn: (details) => Context.current().heartbeat(details),
|
|
146
289
|
isCancelledFn: () => cancellationSignal.aborted,
|
|
147
290
|
approvalProvider: {
|
|
148
291
|
policies: setup.approvalPolicies,
|
|
149
292
|
toolServerMap: setup.toolServerMap,
|
|
150
|
-
|
|
293
|
+
leasedCategories: setup.leasedCategories,
|
|
294
|
+
globalBypass: setup.globalBypass,
|
|
151
295
|
},
|
|
152
296
|
streamVersion: setup.streamVersion,
|
|
153
297
|
});
|
|
@@ -155,37 +299,94 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
155
299
|
await processPostStream({
|
|
156
300
|
status: initialStatus,
|
|
157
301
|
inlinePublisher,
|
|
158
|
-
|
|
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,
|
|
159
306
|
pendingPublishPromises: result.pendingPublishPromises,
|
|
160
307
|
pendingWritebackPromises: result.pendingWritebackPromises,
|
|
161
308
|
executionId,
|
|
162
309
|
});
|
|
163
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
|
+
|
|
164
367
|
if (result.terminalStatus) {
|
|
165
368
|
if (initialStatus.phase === ExecutionPhase.EXECUTION_PAUSED) {
|
|
166
369
|
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
167
370
|
console.log(`[ExecuteDeepAgent] Paused for execution ${executionId}: events=${result.eventsProcessed}`);
|
|
168
371
|
throw new CancelledFailure("Activity paused by orchestrator");
|
|
169
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
|
+
}
|
|
170
382
|
return result.terminalStatus;
|
|
171
383
|
}
|
|
172
384
|
|
|
173
|
-
if (!setup.
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
.filter((intr) => intr.resumeValue === undefined)
|
|
179
|
-
.map((intr) => {
|
|
180
|
-
const val = intr.value as Record<string, unknown>;
|
|
181
|
-
return {
|
|
182
|
-
toolCallId: (val?.tool_call_id as string) ?? "",
|
|
183
|
-
toolName: (val?.tool_name as string) ?? "",
|
|
184
|
-
mcpServerSlug: (val?.mcp_server_slug as string) ?? "",
|
|
185
|
-
message: (val?.message as string) ?? "",
|
|
186
|
-
};
|
|
187
|
-
}),
|
|
188
|
-
) ?? [];
|
|
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);
|
|
189
390
|
|
|
190
391
|
if (pendingInterrupts.length > 0) {
|
|
191
392
|
console.log(
|
|
@@ -201,7 +402,7 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
201
402
|
isStreaming: false,
|
|
202
403
|
});
|
|
203
404
|
for (const intr of pendingInterrupts) {
|
|
204
|
-
|
|
405
|
+
const toolCall = create(ToolCallSchema, {
|
|
205
406
|
id: intr.toolCallId,
|
|
206
407
|
name: intr.toolName,
|
|
207
408
|
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
@@ -211,7 +412,21 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
211
412
|
mcpServerSlug: intr.mcpServerSlug,
|
|
212
413
|
startedAt: utcTimestamp(),
|
|
213
414
|
toolKind: classifyTool(intr.toolName, intr.mcpServerSlug),
|
|
214
|
-
|
|
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);
|
|
215
430
|
}
|
|
216
431
|
initialStatus.messages.push(aiMsg);
|
|
217
432
|
|
|
@@ -220,8 +435,17 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
220
435
|
}
|
|
221
436
|
}
|
|
222
437
|
|
|
223
|
-
|
|
224
|
-
|
|
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
|
+
}
|
|
225
449
|
|
|
226
450
|
let structuredOutput: JsonObject | undefined;
|
|
227
451
|
let finalText: string | undefined;
|
|
@@ -271,9 +495,13 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
271
495
|
// first-class plan.md artifact so the UI can render a reviewable Plan
|
|
272
496
|
// card and a follow-up Implement run can reference it. Read-only mode
|
|
273
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).
|
|
274
501
|
if (
|
|
275
502
|
setup.execution.spec?.executionConfig?.interactionMode === InteractionMode.PLAN &&
|
|
276
|
-
finalText
|
|
503
|
+
finalText &&
|
|
504
|
+
setup.artifactStorage
|
|
277
505
|
) {
|
|
278
506
|
await publishPlanArtifact({
|
|
279
507
|
status: initialStatus,
|
|
@@ -283,10 +511,17 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
283
511
|
});
|
|
284
512
|
}
|
|
285
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
|
+
|
|
286
520
|
await persistStatus(client, executionId, initialStatus, { offload: statusOffload });
|
|
287
521
|
|
|
288
522
|
console.log(
|
|
289
|
-
`[ExecuteDeepAgent] Completed
|
|
523
|
+
`[ExecuteDeepAgent] ${completeNow ? "Completed" : "Awaiting file review for"} ` +
|
|
524
|
+
`execution ${executionId}: ` +
|
|
290
525
|
`events=${result.eventsProcessed}, ` +
|
|
291
526
|
`messages=${initialStatus.messages.length}, ` +
|
|
292
527
|
`artifacts=${initialStatus.artifacts.length}, ` +
|
|
@@ -294,6 +529,13 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
294
529
|
`hasStructuredOutput=${structuredOutput !== undefined}`,
|
|
295
530
|
);
|
|
296
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
|
+
|
|
297
539
|
const slim = slimStatus(initialStatus) as Record<string, unknown>;
|
|
298
540
|
if (finalText !== undefined) {
|
|
299
541
|
slim.final_text = finalText;
|
|
@@ -354,30 +596,158 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
354
596
|
};
|
|
355
597
|
}
|
|
356
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
|
+
|
|
357
723
|
/**
|
|
358
|
-
* Whether
|
|
724
|
+
* Whether to seed the initial status from the persisted execution transcript.
|
|
359
725
|
*
|
|
360
726
|
* The Temporal workflow re-invokes ExecuteDeepAgent with the same thread_id on
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
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.
|
|
365
731
|
*
|
|
366
|
-
* The
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
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.
|
|
371
748
|
*/
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
execution: AgentExecution,
|
|
375
|
-
): boolean {
|
|
376
|
-
const checkpointMessages = (graphState.values as { messages?: unknown }).messages;
|
|
377
|
-
const checkpointHasPriorTurns =
|
|
378
|
-
Array.isArray(checkpointMessages) && checkpointMessages.length > 0;
|
|
379
|
-
const persistedHasHistory = (execution.status?.messages.length ?? 0) > 0;
|
|
380
|
-
return checkpointHasPriorTurns && persistedHasHistory;
|
|
749
|
+
function shouldSeedFromPersistedTranscript(execution: AgentExecution): boolean {
|
|
750
|
+
return (execution.status?.messages.length ?? 0) > 0;
|
|
381
751
|
}
|
|
382
752
|
|
|
383
753
|
/**
|