@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,7 +9,15 @@
|
|
|
9
9
|
import { create } from "@bufbuild/protobuf";
|
|
10
10
|
import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
|
|
11
11
|
import type { StreamingUsageSummary } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
|
|
12
|
+
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
12
13
|
import { utcTimestamp } from "../../shared/status.js";
|
|
14
|
+
import {
|
|
15
|
+
POLICY_ENGINE_VERSION,
|
|
16
|
+
resolveApprovalProvenance,
|
|
17
|
+
toProtoPolicySource,
|
|
18
|
+
type MergedToolPolicy,
|
|
19
|
+
} from "../../shared/approval-policy.js";
|
|
20
|
+
import type { ToolApprovalCategory } from "../../shared/tool-kind.js";
|
|
13
21
|
|
|
14
22
|
// ── Usage Accumulator ──────────────────────────────────────────────
|
|
15
23
|
|
|
@@ -129,30 +137,61 @@ export function extractToolResultV3(output: unknown): string {
|
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
|
|
132
|
-
// ──
|
|
140
|
+
// ── Authorization Provenance ───────────────────────────────────────
|
|
133
141
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
142
|
+
/**
|
|
143
|
+
* The policy inputs the StatusBuilders need to attribute a tool call's approval
|
|
144
|
+
* provenance. The full {@link ApprovalPolicyProvider} (status-builder.ts) is
|
|
145
|
+
* structurally this — defined here so the shared stamper has no import cycle back
|
|
146
|
+
* into the builders.
|
|
147
|
+
*/
|
|
148
|
+
export interface ApprovalProvenanceInputs {
|
|
149
|
+
readonly policies: ReadonlyMap<string, MergedToolPolicy>;
|
|
150
|
+
readonly toolServerMap: ReadonlyMap<string, string>;
|
|
151
|
+
/**
|
|
152
|
+
* Built-in categories with a run-lifetime lease, so a leased built-in is
|
|
153
|
+
* attributed to its lease (approval_lease) rather than the plain category gate.
|
|
154
|
+
* Optional (mirroring {@link ApprovalGateConfig.leasedCategories}); absent =
|
|
155
|
+
* no lease active.
|
|
156
|
+
*/
|
|
157
|
+
readonly leasedCategories?: ReadonlySet<ToolApprovalCategory>;
|
|
158
|
+
/** Pre-armed spec.auto_approve_all — the one whole-run global bypass. */
|
|
159
|
+
readonly globalBypass: boolean;
|
|
160
|
+
}
|
|
138
161
|
|
|
139
|
-
|
|
162
|
+
/** Shared empty set so a provider without leases allocates nothing per call. */
|
|
163
|
+
const NO_LEASED_CATEGORIES: ReadonlySet<ToolApprovalCategory> = new Set();
|
|
140
164
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
/**
|
|
166
|
+
* Stamp a tool call's authorization provenance — which policy layer governs it —
|
|
167
|
+
* alongside `tool_kind`, in exactly the spot both builders classify the tool.
|
|
168
|
+
*
|
|
169
|
+
* This is the read-side companion to the gate: it records WHY a tool is gated or
|
|
170
|
+
* auto-approved for every observed tool call, so the persisted record is
|
|
171
|
+
* auditable and the UI can explain the gate. Built-ins that no layer governs (a
|
|
172
|
+
* read-only built-in) and the no-provider path both leave the field at
|
|
173
|
+
* UNSPECIFIED, like an unclassified tool_kind.
|
|
174
|
+
*/
|
|
175
|
+
export function stampApprovalProvenance(
|
|
176
|
+
tc: ToolCall,
|
|
177
|
+
provider: ApprovalProvenanceInputs | null,
|
|
178
|
+
): void {
|
|
179
|
+
if (!provider) return;
|
|
180
|
+
const serverSlug = tc.mcpServerSlug || provider.toolServerMap.get(tc.name) || "";
|
|
181
|
+
const source = resolveApprovalProvenance(
|
|
182
|
+
tc.name,
|
|
183
|
+
serverSlug,
|
|
184
|
+
provider.policies,
|
|
185
|
+
provider.leasedCategories ?? NO_LEASED_CATEGORIES,
|
|
186
|
+
provider.globalBypass,
|
|
187
|
+
);
|
|
188
|
+
tc.approvalPolicySource = toProtoPolicySource(source);
|
|
189
|
+
if (source) tc.policyEngineVersion = POLICY_ENGINE_VERSION;
|
|
158
190
|
}
|
|
191
|
+
|
|
192
|
+
// ── Approval Args Sanitization ─────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
// Relocated to src/shared/args-preview.ts so both harnesses share one home for
|
|
195
|
+
// arg sanitization. Re-exported here so the native importers (status-builder,
|
|
196
|
+
// v3-status-builder, approval-file-change) keep their existing import path.
|
|
197
|
+
export { sanitizeArgsPreview } from "../../shared/args-preview.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
MessageType,
|
|
26
26
|
ToolCallStatus,
|
|
27
27
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
28
|
-
import {
|
|
28
|
+
import { resolveApprovalMessage as resolveApprovalMsg } from "../../shared/approval-policy.js";
|
|
29
29
|
import { classifyTool } from "../../shared/tool-kind.js";
|
|
30
30
|
import { ExecutionState } from "./execution-state.js";
|
|
31
31
|
import { utcTimestamp } from "../../shared/status.js";
|
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
UsageAccumulator,
|
|
35
35
|
extractToolResult,
|
|
36
36
|
sanitizeArgsPreview,
|
|
37
|
+
stampApprovalProvenance,
|
|
38
|
+
type ApprovalProvenanceInputs,
|
|
37
39
|
} from "./status-builder-shared.js";
|
|
38
40
|
|
|
39
41
|
/** Minimal LangGraph streamEvents v2 event shape. */
|
|
@@ -48,11 +50,16 @@ export interface StreamEvent {
|
|
|
48
50
|
|
|
49
51
|
type EventHandler = (event: StreamEvent, namespace: string) => void;
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
53
|
+
/**
|
|
54
|
+
* The approval inputs the StatusBuilder reads to set requires-approval and to
|
|
55
|
+
* attribute each tool call's authorization provenance.
|
|
56
|
+
*
|
|
57
|
+
* Extends {@link ApprovalProvenanceInputs} so the provenance stamper and the
|
|
58
|
+
* builder share one shape. `globalBypass` is the pre-armed spec.auto_approve_all
|
|
59
|
+
* (the one whole-run global bypass); `leasedCategories` lets a leased built-in be
|
|
60
|
+
* attributed to its run-lifetime lease rather than the plain category gate.
|
|
61
|
+
*/
|
|
62
|
+
export interface ApprovalPolicyProvider extends ApprovalProvenanceInputs {}
|
|
56
63
|
|
|
57
64
|
export class StatusBuilder {
|
|
58
65
|
readonly executionId: string;
|
|
@@ -67,6 +74,14 @@ export class StatusBuilder {
|
|
|
67
74
|
this.executionId = executionId;
|
|
68
75
|
this.state = new ExecutionState(initialStatus);
|
|
69
76
|
|
|
77
|
+
// Resume path: when constructed from a persisted transcript (status seeded
|
|
78
|
+
// in index.ts on a durable-checkpoint resume), rebuild the tool-call index
|
|
79
|
+
// so resumed tool events reconcile to the existing calls instead of
|
|
80
|
+
// duplicating them. A first run carries no messages, so this is a no-op.
|
|
81
|
+
if (initialStatus.messages.length > 0) {
|
|
82
|
+
this.state.rebuildToolCallIndex();
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
initialStatus.phase = ExecutionPhase.EXECUTION_IN_PROGRESS;
|
|
71
86
|
if (!initialStatus.startedAt) {
|
|
72
87
|
initialStatus.startedAt = utcTimestamp();
|
|
@@ -198,11 +213,29 @@ export class StatusBuilder {
|
|
|
198
213
|
}
|
|
199
214
|
|
|
200
215
|
private handleToolStart(event: StreamEvent, namespace: string): void {
|
|
216
|
+
const toolName = event.name ?? "unknown_tool";
|
|
217
|
+
|
|
218
|
+
// Resume reconciliation (v2): the seeded WAITING_APPROVAL tool call was
|
|
219
|
+
// indexed by its tool_call_id, but a resumed on_tool_start carries a fresh
|
|
220
|
+
// LangGraph run_id and exposes no tool_call_id. Match by tool name +
|
|
221
|
+
// pending-approval status so the existing call is resolved in place rather
|
|
222
|
+
// than duplicated, then re-key it under run_id so handleToolEnd resolves the
|
|
223
|
+
// same object. This name match is a v2-only fallback — v3 (the default) keys
|
|
224
|
+
// by the exact tool_call_id; it resolves the first pending call of that
|
|
225
|
+
// name, which is unambiguous for the single-gate resume the workflow drives.
|
|
226
|
+
const seeded = this.findResumableSeededToolCall(toolName);
|
|
227
|
+
if (seeded) {
|
|
228
|
+
seeded.status = ToolCallStatus.TOOL_CALL_RUNNING;
|
|
229
|
+
this.state.toolCalls.set(event.run_id, seeded);
|
|
230
|
+
this.state.toolStartTimes.set(event.run_id, performance.now());
|
|
231
|
+
this._forceNextUpdate = true;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
201
235
|
const parentMsg = this.state.currentAiMessage.get(namespace)
|
|
202
236
|
?? this.ensureAiMessageForToolCall(event.run_id, namespace);
|
|
203
237
|
if (!parentMsg) return;
|
|
204
238
|
|
|
205
|
-
const toolName = event.name ?? "unknown_tool";
|
|
206
239
|
const rawArgs = event.data?.input as Record<string, unknown> | undefined;
|
|
207
240
|
const args = rawArgs ?? {};
|
|
208
241
|
|
|
@@ -228,6 +261,11 @@ export class StatusBuilder {
|
|
|
228
261
|
// Classify after mcpServerSlug is set so MCP tools resolve correctly.
|
|
229
262
|
tc.toolKind = classifyTool(tc.name, tc.mcpServerSlug);
|
|
230
263
|
|
|
264
|
+
// Stamp authorization provenance in the same spot as tool_kind, for every
|
|
265
|
+
// observed tool call (gated or auto-approved). A gated call is re-seeded on
|
|
266
|
+
// reinvocation (index.ts) with the same source carried through the interrupt.
|
|
267
|
+
stampApprovalProvenance(tc, this.approvalProvider);
|
|
268
|
+
|
|
231
269
|
if (approvalReq.requiresApproval) {
|
|
232
270
|
tc.requiresApproval = true;
|
|
233
271
|
tc.approvalMessage = approvalReq.message;
|
|
@@ -248,6 +286,23 @@ export class StatusBuilder {
|
|
|
248
286
|
this._forceNextUpdate = true;
|
|
249
287
|
}
|
|
250
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Find a seeded tool call awaiting approval for the given name, for v2 resume
|
|
291
|
+
* reconciliation. Returns the first WAITING_APPROVAL call of that name in the
|
|
292
|
+
* rebuilt index, or undefined when none is pending (the normal first-run path).
|
|
293
|
+
*/
|
|
294
|
+
private findResumableSeededToolCall(toolName: string): ToolCall | undefined {
|
|
295
|
+
for (const tc of this.state.toolCalls.values()) {
|
|
296
|
+
if (
|
|
297
|
+
tc.name === toolName &&
|
|
298
|
+
tc.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL
|
|
299
|
+
) {
|
|
300
|
+
return tc;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
251
306
|
private checkApprovalRequirement(
|
|
252
307
|
toolName: string,
|
|
253
308
|
args: Record<string, unknown>,
|
|
@@ -258,7 +313,7 @@ export class StatusBuilder {
|
|
|
258
313
|
|
|
259
314
|
const serverSlug = this.approvalProvider.toolServerMap.get(toolName) ?? "";
|
|
260
315
|
|
|
261
|
-
if (this.approvalProvider.
|
|
316
|
+
if (this.approvalProvider.globalBypass) {
|
|
262
317
|
return { requiresApproval: false, message: "", serverSlug };
|
|
263
318
|
}
|
|
264
319
|
|
|
@@ -9,12 +9,7 @@
|
|
|
9
9
|
import type { V3ProtocolEvent } from "./v3-event-recorder.js";
|
|
10
10
|
import type { InlinePublisher } from "./inline-publisher.js";
|
|
11
11
|
import type { WriteBackCoordinator } from "./writeback-coordinator.js";
|
|
12
|
-
|
|
13
|
-
const FILE_MODIFYING_TOOLS = new Set([
|
|
14
|
-
"write_file", "edit_file", "create_file",
|
|
15
|
-
"write", "edit", "create",
|
|
16
|
-
"str_replace_editor",
|
|
17
|
-
]);
|
|
12
|
+
import { extractFilePath, isFileModifyingTool } from "../../shared/file-tools.js";
|
|
18
13
|
|
|
19
14
|
interface CachedToolInput {
|
|
20
15
|
readonly toolName: string;
|
|
@@ -64,7 +59,7 @@ export class StreamingSideEffects {
|
|
|
64
59
|
const cached = this.inputCache.get(callId);
|
|
65
60
|
this.inputCache.delete(callId);
|
|
66
61
|
if (!cached) return;
|
|
67
|
-
if (!
|
|
62
|
+
if (!isFileModifyingTool(cached.toolName)) return;
|
|
68
63
|
|
|
69
64
|
const filePath = extractFilePath(cached.input);
|
|
70
65
|
if (!filePath) return;
|
|
@@ -78,12 +73,3 @@ export class StreamingSideEffects {
|
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
75
|
}
|
|
81
|
-
|
|
82
|
-
function extractFilePath(input: Record<string, unknown>): string | null {
|
|
83
|
-
if (typeof input.path === "string") return input.path;
|
|
84
|
-
if (typeof input.file_path === "string") return input.file_path;
|
|
85
|
-
if (typeof input.filePath === "string") return input.filePath;
|
|
86
|
-
if (typeof input.filename === "string") return input.filename;
|
|
87
|
-
if (typeof input.file === "string") return input.file;
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
@@ -42,6 +42,7 @@ import type { InlinePublisher } from "./inline-publisher.js";
|
|
|
42
42
|
import type { WriteBackCoordinator } from "./writeback-coordinator.js";
|
|
43
43
|
import { createV2EventRecorder } from "./event-recorder.js";
|
|
44
44
|
import { streamExecutionV3 } from "./streaming-v3.js";
|
|
45
|
+
import { extractFilePath, isFileModifyingTool } from "../../shared/file-tools.js";
|
|
45
46
|
|
|
46
47
|
const DEFAULT_STALL_TIMEOUT_MS = 120_000;
|
|
47
48
|
|
|
@@ -283,25 +284,14 @@ function checkStallTimeout(
|
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
const FILE_MODIFYING_TOOLS = new Set([
|
|
287
|
-
"write_file", "edit_file", "create_file",
|
|
288
|
-
"write", "edit", "create",
|
|
289
|
-
"str_replace_editor",
|
|
290
|
-
]);
|
|
291
|
-
|
|
292
287
|
function extractFilePathFromToolEnd(event: StreamEvent): string | null {
|
|
293
288
|
const toolName = event.name ?? "";
|
|
294
|
-
if (!
|
|
289
|
+
if (!isFileModifyingTool(toolName)) return null;
|
|
295
290
|
|
|
296
291
|
const input = event.data?.input as Record<string, unknown> | undefined;
|
|
297
292
|
if (!input) return null;
|
|
298
293
|
|
|
299
|
-
|
|
300
|
-
if (typeof input.file_path === "string") return input.file_path;
|
|
301
|
-
if (typeof input.filename === "string") return input.filename;
|
|
302
|
-
if (typeof input.file === "string") return input.file;
|
|
303
|
-
|
|
304
|
-
return null;
|
|
294
|
+
return extractFilePath(input);
|
|
305
295
|
}
|
|
306
296
|
|
|
307
297
|
export class StallTimeoutError extends Error {
|
|
@@ -25,7 +25,9 @@ import type { SubAgent, McpServerUsage } from "@stigmer/protos/ai/stigmer/agenti
|
|
|
25
25
|
|
|
26
26
|
import type { WorkspaceBackend } from "../../shared/workspace/types.js";
|
|
27
27
|
import type { StigmerClient } from "../../client/stigmer-client.js";
|
|
28
|
-
import type {
|
|
28
|
+
import type { ApprovalGateConfig } from "../../middleware/approval-gate.js";
|
|
29
|
+
import { CasCaptureFilesystemBackend } from "./cas-capture-backend.js";
|
|
30
|
+
import type { CasCaptureObserver } from "./cas-capture-observer.js";
|
|
29
31
|
import type { CostCapMiddleware, StigmerMiddleware } from "../../middleware/index.js";
|
|
30
32
|
import { createThinkTool } from "../../middleware/index.js";
|
|
31
33
|
import { buildSubAgentMiddleware } from "./subagent-wiring.js";
|
|
@@ -131,18 +133,32 @@ export interface SubagentTransformOptions {
|
|
|
131
133
|
readonly parentMcpUsages: readonly McpServerUsage[];
|
|
132
134
|
readonly skillClient: StigmerClient;
|
|
133
135
|
readonly workspaceBackend: WorkspaceBackend;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
/**
|
|
137
|
+
* The parent's approval-gate config, inherited verbatim so a mutating tool
|
|
138
|
+
* inside a sub-agent is gated identically to one in the parent (same policies,
|
|
139
|
+
* toolServerMap, fingerprint key, execution id). Null under auto-approve-all,
|
|
140
|
+
* where the parent gate is inert too — sub-agents then install no gate either.
|
|
141
|
+
*/
|
|
142
|
+
readonly approvalGate: ApprovalGateConfig | null;
|
|
143
|
+
/**
|
|
144
|
+
* The parent turn's shared CAS observer, present only in capture mode. When
|
|
145
|
+
* supplied, each sub-agent is built with a CAS-observing filesystem backend
|
|
146
|
+
* wired to THIS observer, so its gitignored writes are captured into the same
|
|
147
|
+
* change set as the parent's (Session 26, DD-19). Absent outside capture mode,
|
|
148
|
+
* where sub-agents keep the plain backend and the classic gitignored deny-gate.
|
|
149
|
+
*/
|
|
150
|
+
readonly casObserver?: CasCaptureObserver;
|
|
136
151
|
readonly parentModelName: string;
|
|
137
152
|
readonly parentHasNativeThinking: boolean;
|
|
138
153
|
readonly costCap?: CostCapMiddleware;
|
|
139
154
|
/**
|
|
140
155
|
* Builds a configured chat-model instance for a given model name. When
|
|
141
156
|
* provided, sub-agents are compiled with the same proxy-aware model client
|
|
142
|
-
* as the parent (base URL, auth headers, API key)
|
|
143
|
-
*
|
|
157
|
+
* as the parent (base URL, auth headers, API key) — including registry-id
|
|
158
|
+
* resolution, which is why this is async. When absent (unit tests), the
|
|
159
|
+
* model name string is passed to deepagents directly.
|
|
144
160
|
*/
|
|
145
|
-
readonly modelFactory?: (modelName: string) => BaseChatModel
|
|
161
|
+
readonly modelFactory?: (modelName: string) => Promise<BaseChatModel>;
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
// =========================================================================
|
|
@@ -404,9 +420,12 @@ export async function compileSubagents(
|
|
|
404
420
|
transformed: readonly TransformedSubagent[],
|
|
405
421
|
opts: {
|
|
406
422
|
readonly costCap?: CostCapMiddleware;
|
|
423
|
+
readonly approvalGate?: ApprovalGateConfig | null;
|
|
407
424
|
readonly parentModelName: string;
|
|
408
425
|
readonly workspaceRootDir: string;
|
|
409
|
-
|
|
426
|
+
/** Shared CAS observer (capture mode only); see {@link SubagentTransformOptions.casObserver}. */
|
|
427
|
+
readonly casObserver?: CasCaptureObserver;
|
|
428
|
+
readonly modelFactory?: (modelName: string) => Promise<BaseChatModel>;
|
|
410
429
|
},
|
|
411
430
|
): Promise<CompiledSubAgent[]> {
|
|
412
431
|
if (transformed.length === 0) return [];
|
|
@@ -416,23 +435,40 @@ export async function compileSubagents(
|
|
|
416
435
|
|
|
417
436
|
for (const spec of transformed) {
|
|
418
437
|
try {
|
|
438
|
+
// Structural coupling (DD-19): a sub-agent gate flows gitignored writes into
|
|
439
|
+
// CAS iff a CAS observer backs that sub-agent's filesystem backend. Deriving
|
|
440
|
+
// both from the same `casObserver` makes "unobserved unreviewable bytes"
|
|
441
|
+
// impossible by construction.
|
|
419
442
|
const middleware = buildSubAgentMiddleware({
|
|
420
443
|
costCap: opts.costCap,
|
|
444
|
+
approvalGate: opts.approvalGate,
|
|
445
|
+
captureIgnored: !!opts.casObserver,
|
|
421
446
|
});
|
|
422
447
|
|
|
423
448
|
const modelName = spec.model ?? opts.parentModelName;
|
|
424
|
-
// Use a configured model instance (proxy base URL + auth
|
|
425
|
-
// factory is supplied so sub-agent LLM
|
|
426
|
-
// proxy as the parent. Falling back to the
|
|
427
|
-
// construct a default client (unit tests /
|
|
428
|
-
|
|
449
|
+
// Use a configured model instance (proxy base URL + auth, plus registry
|
|
450
|
+
// id -> API id resolution) when a factory is supplied so sub-agent LLM
|
|
451
|
+
// calls route through the same proxy as the parent. Falling back to the
|
|
452
|
+
// bare name lets deepagents construct a default client (unit tests /
|
|
453
|
+
// no-proxy paths).
|
|
454
|
+
const model = opts.modelFactory ? await opts.modelFactory(modelName) : modelName;
|
|
455
|
+
|
|
456
|
+
// Capture mode: a CAS-observing backend wired to the shared observer, so
|
|
457
|
+
// the sub-agent's gitignored writes are captured (same class the parent
|
|
458
|
+
// uses). Otherwise the plain backend + classic gitignored deny-gate.
|
|
459
|
+
const backend = opts.casObserver
|
|
460
|
+
? new CasCaptureFilesystemBackend(
|
|
461
|
+
{ rootDir: opts.workspaceRootDir },
|
|
462
|
+
{ observer: opts.casObserver },
|
|
463
|
+
)
|
|
464
|
+
: new FilesystemBackend({ rootDir: opts.workspaceRootDir });
|
|
429
465
|
|
|
430
466
|
const agentGraph = await createDeepAgent({
|
|
431
467
|
model,
|
|
432
468
|
systemPrompt: spec.systemPrompt,
|
|
433
469
|
tools: spec.tools.length > 0 ? spec.tools : undefined,
|
|
434
470
|
middleware: middleware as unknown[],
|
|
435
|
-
backend
|
|
471
|
+
backend,
|
|
436
472
|
generalPurposeAgent: false,
|
|
437
473
|
} as Parameters<typeof createDeepAgent>[0]);
|
|
438
474
|
|
|
@@ -489,6 +525,8 @@ export async function transformAndCompileSubagents(
|
|
|
489
525
|
parentMcpUsages,
|
|
490
526
|
skillClient,
|
|
491
527
|
workspaceBackend,
|
|
528
|
+
approvalGate,
|
|
529
|
+
casObserver,
|
|
492
530
|
parentModelName,
|
|
493
531
|
parentHasNativeThinking,
|
|
494
532
|
costCap,
|
|
@@ -602,8 +640,10 @@ export async function transformAndCompileSubagents(
|
|
|
602
640
|
// Step 4: Compile all subagents
|
|
603
641
|
const compiled = await compileSubagents(allSpecs, {
|
|
604
642
|
costCap,
|
|
643
|
+
approvalGate,
|
|
605
644
|
parentModelName,
|
|
606
645
|
workspaceRootDir: workspaceBackend.rootDir,
|
|
646
|
+
casObserver,
|
|
607
647
|
modelFactory,
|
|
608
648
|
});
|
|
609
649
|
|
|
@@ -5,13 +5,21 @@
|
|
|
5
5
|
* - Fresh loop detection (independent cycle tracking)
|
|
6
6
|
* - Fresh tool truncation (same limits as parent)
|
|
7
7
|
* - Periodic execution budget (interval=30, max=4 advisories)
|
|
8
|
+
* - Approval gate (so a mutating tool *inside* a sub-agent is gated, not
|
|
9
|
+
* bypassed) — installed only when the parent itself is gated
|
|
10
|
+
* (`approvalGate` present; absent under auto-approve-all)
|
|
8
11
|
* - Shared cost cap view (parent's budget, no reset on sub-agent start)
|
|
9
12
|
*
|
|
10
13
|
* The sub-agent middleware stack does NOT include:
|
|
11
14
|
* - Graceful stop (parent handles STOP signal propagation)
|
|
12
15
|
* - OTel spans (parent's OTel context propagates automatically)
|
|
13
16
|
* - Error hints (applied at tool level, not sub-agent level)
|
|
14
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* Approval-gate note: the gate is what *creates* the LangGraph `interrupt()`.
|
|
19
|
+
* A sub-agent's interrupt does surface at the parent checkpoint and resumes
|
|
20
|
+
* correctly (verified in subagent-approval-propagation.test.ts), but only if
|
|
21
|
+
* the gate runs inside the sub-agent in the first place — omitting it here was
|
|
22
|
+
* the live HITL bypass this wiring closes.
|
|
15
23
|
*/
|
|
16
24
|
|
|
17
25
|
import type { StigmerMiddleware, ToolTruncationConfig } from "../../middleware/types.js";
|
|
@@ -19,6 +27,10 @@ import type { CostCapMiddleware } from "../../middleware/index.js";
|
|
|
19
27
|
import { createLoopDetectionMiddleware } from "../../middleware/loop-detection.js";
|
|
20
28
|
import { createToolTruncationMiddleware } from "../../middleware/tool-truncation.js";
|
|
21
29
|
import { createExecutionBudgetMiddleware } from "../../middleware/execution-budget.js";
|
|
30
|
+
import {
|
|
31
|
+
createApprovalGateMiddleware,
|
|
32
|
+
type ApprovalGateConfig,
|
|
33
|
+
} from "../../middleware/approval-gate.js";
|
|
22
34
|
|
|
23
35
|
const SUB_AGENT_ADVISORY_INTERVAL = 30;
|
|
24
36
|
const SUB_AGENT_MAX_ADVISORIES = 4;
|
|
@@ -26,13 +38,31 @@ const SUB_AGENT_MAX_ADVISORIES = 4;
|
|
|
26
38
|
export interface SubAgentMiddlewareOptions {
|
|
27
39
|
readonly costCap?: CostCapMiddleware;
|
|
28
40
|
readonly toolTruncation?: Partial<ToolTruncationConfig>;
|
|
41
|
+
/**
|
|
42
|
+
* Approval gate config inherited from the parent. When present, the sub-agent
|
|
43
|
+
* gates its own mutating tool calls; when null/undefined (e.g. auto-approve-
|
|
44
|
+
* all, where the parent gate is inert too) no gate is installed.
|
|
45
|
+
*/
|
|
46
|
+
readonly approvalGate?: ApprovalGateConfig | null;
|
|
47
|
+
/**
|
|
48
|
+
* Route the sub-agent's gitignored writes into CAS capture (apply-then-review)
|
|
49
|
+
* instead of the interrupt gate. TRUE iff a CAS observer backs the sub-agent's
|
|
50
|
+
* filesystem backend (DD-19) — `compileSubagents` derives it from
|
|
51
|
+
* `!!casObserver`, keeping the gate and the backend coupled. Default false =
|
|
52
|
+
* the classic gitignored deny-gate, because flowing a gitignored edit on an
|
|
53
|
+
* unobserved backend would apply unreviewable bytes.
|
|
54
|
+
*/
|
|
55
|
+
readonly captureIgnored?: boolean;
|
|
29
56
|
}
|
|
30
57
|
|
|
31
58
|
/**
|
|
32
59
|
* Build the middleware stack for a single sub-agent.
|
|
33
60
|
*
|
|
34
|
-
* Returns an ordered array
|
|
35
|
-
* loop detection → execution budget (periodic) → tool truncation →
|
|
61
|
+
* Returns an ordered array mirroring the parent composition:
|
|
62
|
+
* loop detection → execution budget (periodic) → tool truncation →
|
|
63
|
+
* [approval gate] → cost cap view. The gate sits before the cost-cap view so an
|
|
64
|
+
* approval pause happens before budget accounting, matching the parent order
|
|
65
|
+
* (…→ truncation → graceful-stop → approval gate → cost cap …).
|
|
36
66
|
*/
|
|
37
67
|
export function buildSubAgentMiddleware(
|
|
38
68
|
options: SubAgentMiddlewareOptions = {},
|
|
@@ -48,6 +78,23 @@ export function buildSubAgentMiddleware(
|
|
|
48
78
|
|
|
49
79
|
stack.push(createToolTruncationMiddleware(options.toolTruncation));
|
|
50
80
|
|
|
81
|
+
if (options.approvalGate) {
|
|
82
|
+
// captureIgnored (DD-19): a sub-agent flows gitignored writes into CAS iff a
|
|
83
|
+
// CAS observer backs its filesystem backend (compileSubagents passes this as
|
|
84
|
+
// `!!casObserver`). When true, inherit the parent gate verbatim so its
|
|
85
|
+
// captureIgnored + recordBlockedSecret feed the SAME shared observer that
|
|
86
|
+
// backs the sub-agent's writes. When false (default; non-capture mode, or no
|
|
87
|
+
// observer), force CAS routing off and drop the blocked-secret sink so
|
|
88
|
+
// gitignored paths stay on the interrupt gate — a flowed gitignored edit on an
|
|
89
|
+
// unobserved backend would apply unobserved, unreviewable bytes. Sub-agent
|
|
90
|
+
// git-tracked edits are always captured by the backend-agnostic boundary diff.
|
|
91
|
+
stack.push(createApprovalGateMiddleware(
|
|
92
|
+
options.captureIgnored
|
|
93
|
+
? options.approvalGate
|
|
94
|
+
: { ...options.approvalGate, captureIgnored: false, recordBlockedSecret: undefined },
|
|
95
|
+
));
|
|
96
|
+
}
|
|
97
|
+
|
|
51
98
|
if (options.costCap) {
|
|
52
99
|
stack.push(options.costCap.forSubAgent());
|
|
53
100
|
}
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
MessageType,
|
|
25
25
|
ToolCallStatus,
|
|
26
26
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
27
|
-
import {
|
|
27
|
+
import { resolveApprovalMessage as resolveApprovalMsg } from "../../shared/approval-policy.js";
|
|
28
28
|
import { classifyTool } from "../../shared/tool-kind.js";
|
|
29
29
|
import { ExecutionState } from "./execution-state.js";
|
|
30
30
|
import { utcTimestamp } from "../../shared/status.js";
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
UsageAccumulator,
|
|
37
37
|
extractToolResultV3,
|
|
38
38
|
sanitizeArgsPreview,
|
|
39
|
+
stampApprovalProvenance,
|
|
39
40
|
} from "./status-builder-shared.js";
|
|
40
41
|
import { SubAgentTracker } from "./subagent-tracker.js";
|
|
41
42
|
|
|
@@ -54,6 +55,15 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
|
|
|
54
55
|
this.executionId = executionId;
|
|
55
56
|
this.state = new ExecutionState(initialStatus);
|
|
56
57
|
|
|
58
|
+
// Resume path: when constructed from a persisted transcript (status seeded
|
|
59
|
+
// in index.ts on a durable-checkpoint resume), rebuild the tool-call index
|
|
60
|
+
// so resumed tool_started/tool_finished events reconcile to the existing
|
|
61
|
+
// calls instead of duplicating them. A first run carries no messages, so
|
|
62
|
+
// this is a no-op.
|
|
63
|
+
if (initialStatus.messages.length > 0) {
|
|
64
|
+
this.state.rebuildToolCallIndex();
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
initialStatus.phase = ExecutionPhase.EXECUTION_IN_PROGRESS;
|
|
58
68
|
if (!initialStatus.startedAt) {
|
|
59
69
|
initialStatus.startedAt = utcTimestamp();
|
|
@@ -244,6 +254,25 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
|
|
|
244
254
|
input: Record<string, unknown>,
|
|
245
255
|
namespace: string,
|
|
246
256
|
): void {
|
|
257
|
+
// Resume reconciliation: the gated tool call already exists, seeded from the
|
|
258
|
+
// persisted transcript of a prior invocation (seedStatusFromExecution in
|
|
259
|
+
// index.ts). The durable checkpoint re-emits tool_started now that approval
|
|
260
|
+
// is granted — flip the existing call to RUNNING in place rather than
|
|
261
|
+
// appending a duplicate or re-triggering the approval gate. v3 keys by
|
|
262
|
+
// tool_call_id, so this is an exact match (no name heuristics needed).
|
|
263
|
+
const existing = this.state.toolCalls.get(callId);
|
|
264
|
+
if (existing) {
|
|
265
|
+
if (existing.status === ToolCallStatus.TOOL_CALL_WAITING_APPROVAL) {
|
|
266
|
+
existing.status = ToolCallStatus.TOOL_CALL_RUNNING;
|
|
267
|
+
}
|
|
268
|
+
if (Object.keys(input).length > 0 && !existing.args) {
|
|
269
|
+
existing.args = input as JsonObject;
|
|
270
|
+
}
|
|
271
|
+
this.state.toolStartTimes.set(callId, performance.now());
|
|
272
|
+
this._forceNextUpdate = true;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
247
276
|
const agentNs = this.resolveAgentNamespace(namespace);
|
|
248
277
|
const parentMsg = this.state.currentAiMessage.get(agentNs)
|
|
249
278
|
?? this.ensureAiMessageForToolCall(agentNs);
|
|
@@ -271,6 +300,11 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
|
|
|
271
300
|
// Classify after mcpServerSlug is set so MCP tools resolve correctly.
|
|
272
301
|
tc.toolKind = classifyTool(tc.name, tc.mcpServerSlug);
|
|
273
302
|
|
|
303
|
+
// Stamp authorization provenance in the same spot as tool_kind, for every
|
|
304
|
+
// observed tool call (gated or auto-approved). A gated call is re-seeded on
|
|
305
|
+
// reinvocation (index.ts) with the same source carried through the interrupt.
|
|
306
|
+
stampApprovalProvenance(tc, this.approvalProvider);
|
|
307
|
+
|
|
274
308
|
if (approvalReq.requiresApproval) {
|
|
275
309
|
tc.requiresApproval = true;
|
|
276
310
|
tc.approvalMessage = approvalReq.message;
|
|
@@ -368,7 +402,7 @@ export class V3StatusBuilder implements ExecutionStatusWriter {
|
|
|
368
402
|
|
|
369
403
|
const serverSlug = this.approvalProvider.toolServerMap.get(toolName) ?? "";
|
|
370
404
|
|
|
371
|
-
if (this.approvalProvider.
|
|
405
|
+
if (this.approvalProvider.globalBypass) {
|
|
372
406
|
return { requiresApproval: false, message: "", serverSlug };
|
|
373
407
|
}
|
|
374
408
|
|
|
@@ -82,15 +82,15 @@ export class ClaimcheckPayloadCodec implements PayloadCodec {
|
|
|
82
82
|
Buffer.from(payload.data!).toString("utf-8"),
|
|
83
83
|
);
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
85
|
+
let rawBuf: Buffer;
|
|
86
|
+
try {
|
|
87
|
+
rawBuf = await this.storage.download(marker.key);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
// Preserve the claimcheck-scoped error contract; the shared download error
|
|
90
|
+
// already carries the HTTP status (proxy) or the miss (local) as the cause.
|
|
91
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
92
|
+
throw new Error(`Claimcheck retrieve failed for key ${marker.key}: ${cause}`);
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
const rawBuf = Buffer.from(await response.arrayBuffer());
|
|
94
94
|
const dataBuf = marker.compressed ? decompress(rawBuf) : rawBuf;
|
|
95
95
|
|
|
96
96
|
return {
|
|
@@ -273,12 +273,20 @@ export class StigmerClient {
|
|
|
273
273
|
async updateWorkflowExecutionStatus(
|
|
274
274
|
executionId: string,
|
|
275
275
|
status: WorkflowExecutionStatus,
|
|
276
|
-
options?: {
|
|
276
|
+
options?: {
|
|
277
|
+
updatePendingApprovals?: boolean;
|
|
278
|
+
updatePendingFileReviews?: boolean;
|
|
279
|
+
// The child a per-child merge targets (required whenever either update flag
|
|
280
|
+
// is set, including the scoped-clear case where the list is empty).
|
|
281
|
+
pendingUpdateChildAgentExecutionId?: string;
|
|
282
|
+
},
|
|
277
283
|
): Promise<WorkflowExecution> {
|
|
278
284
|
const input = create(WorkflowExecutionUpdateStatusInputSchema, {
|
|
279
285
|
executionId,
|
|
280
286
|
status,
|
|
281
287
|
updatePendingApprovals: options?.updatePendingApprovals ?? false,
|
|
288
|
+
updatePendingFileReviews: options?.updatePendingFileReviews ?? false,
|
|
289
|
+
pendingUpdateChildAgentExecutionId: options?.pendingUpdateChildAgentExecutionId ?? "",
|
|
282
290
|
});
|
|
283
291
|
return this.workflowExecutionCommand.updateStatus(input);
|
|
284
292
|
}
|