@stigmer/runner 3.10.0 → 3.11.1-dev.20260812192248
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/README.md +14 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/call-llm.js +9 -10
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +2 -1
- package/dist/activities/classify-tool-approvals.js +28 -2
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +39 -0
- package/dist/activities/discover-mcp-server.js +165 -28
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/emit-event.d.ts +14 -2
- package/dist/activities/emit-event.js +52 -17
- package/dist/activities/emit-event.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +8 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +28 -2
- package/dist/activities/execute-cursor/approval-state.js +7 -1
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/attachment-resolver.d.ts +14 -0
- package/dist/activities/execute-cursor/attachment-resolver.js +18 -4
- package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/blueprint-resolver.d.ts +1 -9
- package/dist/activities/execute-cursor/blueprint-resolver.js +6 -22
- package/dist/activities/execute-cursor/blueprint-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/env-resolver.js +3 -1
- package/dist/activities/execute-cursor/env-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/error-classifier.d.ts +59 -3
- package/dist/activities/execute-cursor/error-classifier.js +115 -6
- package/dist/activities/execute-cursor/error-classifier.js.map +1 -1
- package/dist/activities/execute-cursor/extract-structured-output.d.ts +29 -0
- package/dist/activities/execute-cursor/extract-structured-output.js +58 -0
- package/dist/activities/execute-cursor/extract-structured-output.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +14 -3
- package/dist/activities/execute-cursor/hook-script.js +72 -10
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +51 -11
- package/dist/activities/execute-cursor/index.js +177 -93
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/mcp-resolver.d.ts +24 -1
- package/dist/activities/execute-cursor/mcp-resolver.js +5 -2
- package/dist/activities/execute-cursor/mcp-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/model-pricing-data.d.ts +2 -0
- package/dist/activities/execute-cursor/model-pricing-data.js +13 -3
- package/dist/activities/execute-cursor/model-pricing-data.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +51 -4
- package/dist/activities/execute-cursor/prompt-builder.js +49 -7
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/turn-recovery.d.ts +52 -0
- package/dist/activities/execute-cursor/turn-recovery.js +193 -0
- package/dist/activities/execute-cursor/turn-recovery.js.map +1 -0
- package/dist/activities/execute-cursor/turn-stream.js +4 -1
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +18 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +68 -23
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/environment.js +3 -1
- package/dist/activities/execute-deep-agent/environment.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +15 -0
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +7 -7
- package/dist/activities/execute-deep-agent/prompt-builder.js +8 -2
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +10 -0
- package/dist/activities/execute-deep-agent/setup.js +65 -24
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/shell-env.d.ts +5 -1
- package/dist/activities/execute-deep-agent/shell-env.js +7 -9
- package/dist/activities/execute-deep-agent/shell-env.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +18 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js +15 -2
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +27 -6
- package/dist/activities/execute-deep-agent/subagent-wiring.js +26 -5
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/hydrate-workflow-execution.js +8 -3
- package/dist/activities/hydrate-workflow-execution.js.map +1 -1
- package/dist/activities/run-command.d.ts +5 -2
- package/dist/activities/run-command.js +22 -10
- package/dist/activities/run-command.js.map +1 -1
- package/dist/activities/run-env.d.ts +40 -0
- package/dist/activities/run-env.js +66 -0
- package/dist/activities/run-env.js.map +1 -0
- package/dist/activities/workflow-event-activities.d.ts +28 -10
- package/dist/activities/workflow-event-activities.js +87 -58
- package/dist/activities/workflow-event-activities.js.map +1 -1
- package/dist/bootstrap.js +5 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +21 -1
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +83 -21
- package/dist/client/stigmer-client.js +106 -29
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/encryption/config.d.ts +64 -0
- package/dist/encryption/config.js +108 -0
- package/dist/encryption/config.js.map +1 -0
- package/dist/encryption/index.d.ts +3 -0
- package/dist/encryption/index.js +3 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/encryption/payload-codec.d.ts +41 -0
- package/dist/encryption/payload-codec.js +130 -0
- package/dist/encryption/payload-codec.js.map +1 -0
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.js +7 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/path-normalization.d.ts +57 -0
- package/dist/middleware/path-normalization.js +109 -0
- package/dist/middleware/path-normalization.js.map +1 -0
- package/dist/middleware/types.d.ts +8 -0
- package/dist/payload-codecs.d.ts +17 -0
- package/dist/payload-codecs.js +42 -0
- package/dist/payload-codecs.js.map +1 -0
- package/dist/preflight.d.ts +31 -0
- package/dist/preflight.js +43 -0
- package/dist/preflight.js.map +1 -1
- package/dist/runner-manager.js +21 -17
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +11 -18
- package/dist/runner.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +9 -3
- package/dist/shared/approval-policy.js +15 -6
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/artifact-storage.d.ts +23 -1
- package/dist/shared/artifact-storage.js +51 -10
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/attachment-naming.d.ts +53 -0
- package/dist/shared/attachment-naming.js +59 -0
- package/dist/shared/attachment-naming.js.map +1 -0
- package/dist/shared/caller-identity.d.ts +23 -2
- package/dist/shared/caller-identity.js +36 -5
- package/dist/shared/caller-identity.js.map +1 -1
- package/dist/shared/channel-attachment.js +1 -0
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/shared/checkpointer/http-saver.d.ts +26 -1
- package/dist/shared/checkpointer/http-saver.js +54 -8
- package/dist/shared/checkpointer/http-saver.js.map +1 -1
- package/dist/shared/conversation-attachment.js +1 -0
- package/dist/shared/conversation-attachment.js.map +1 -1
- package/dist/shared/conversation-catchup.d.ts +9 -2
- package/dist/shared/conversation-catchup.js +39 -6
- package/dist/shared/conversation-catchup.js.map +1 -1
- package/dist/shared/datastore-attachment.d.ts +50 -7
- package/dist/shared/datastore-attachment.js +93 -11
- package/dist/shared/datastore-attachment.js.map +1 -1
- package/dist/shared/grpc-retry.d.ts +5 -1
- package/dist/shared/grpc-retry.js +5 -1
- package/dist/shared/grpc-retry.js.map +1 -1
- package/dist/shared/http-retry.d.ts +85 -0
- package/dist/shared/http-retry.js +101 -0
- package/dist/shared/http-retry.js.map +1 -0
- package/dist/shared/llm-backend.d.ts +275 -0
- package/dist/shared/llm-backend.js +425 -0
- package/dist/shared/llm-backend.js.map +1 -0
- package/dist/shared/llm-proxy.d.ts +8 -0
- package/dist/shared/llm-proxy.js +15 -0
- package/dist/shared/llm-proxy.js.map +1 -1
- package/dist/shared/mcp-enabled-tools.d.ts +57 -0
- package/dist/shared/mcp-enabled-tools.js +86 -0
- package/dist/shared/mcp-enabled-tools.js.map +1 -0
- package/dist/shared/mcp-manager.d.ts +11 -5
- package/dist/shared/mcp-manager.js +47 -8
- package/dist/shared/mcp-manager.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +39 -2
- package/dist/shared/mcp-resolver.js +38 -2
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/mcp-schema-sanitizer.d.ts +70 -0
- package/dist/shared/mcp-schema-sanitizer.js +197 -0
- package/dist/shared/mcp-schema-sanitizer.js.map +1 -0
- package/dist/shared/model-client.d.ts +18 -5
- package/dist/shared/model-client.js +149 -18
- package/dist/shared/model-client.js.map +1 -1
- package/dist/shared/model-error.js +198 -5
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/model-pricing-data.d.ts +2 -0
- package/dist/shared/model-pricing-data.js +13 -3
- package/dist/shared/model-pricing-data.js.map +1 -1
- package/dist/shared/model-registry.js +3 -2
- package/dist/shared/model-registry.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +33 -0
- package/dist/shared/plan-mode-permissions.js +35 -0
- package/dist/shared/plan-mode-permissions.js.map +1 -0
- package/dist/shared/registry-endpoint.d.ts +20 -0
- package/dist/shared/registry-endpoint.js +25 -0
- package/dist/shared/registry-endpoint.js.map +1 -1
- package/dist/shared/runner-credential-keys.d.ts +24 -0
- package/dist/shared/runner-credential-keys.js +47 -0
- package/dist/shared/runner-credential-keys.js.map +1 -0
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +2 -4
- package/dist/worker.js.map +1 -1
- package/dist/workflow-engine/resolve.d.ts +19 -1
- package/dist/workflow-engine/resolve.js +37 -2
- package/dist/workflow-engine/resolve.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +18 -0
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.d.ts +9 -0
- package/dist/workflows/call-agent-orchestrator.js +1 -0
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.js +9 -0
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/engine-core.js +23 -2
- package/dist/workflows/engine-core.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +1 -1
- package/dist/workflows/execute-from-execution.js +11 -1
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/dist/workflows/types.d.ts +8 -0
- package/package.json +9 -4
- package/src/__test-utils__/__tests__/vitest-global-setup.test.ts +37 -0
- package/src/__test-utils__/vitest-global-setup.ts +50 -0
- package/src/__tests__/bootstrap.test.ts +43 -0
- package/src/__tests__/claimcheck-codec.test.ts +36 -0
- package/src/__tests__/encryption-codec.test.ts +287 -0
- package/src/__tests__/fixtures/encrypted-payload-fixture.json +15 -0
- package/src/__tests__/history-encryption-e2e.test.ts +243 -0
- package/src/__tests__/preflight.test.ts +65 -2
- package/src/activities/__tests__/call-llm.test.ts +75 -0
- package/src/activities/__tests__/classify-tool-approvals.test.ts +117 -1
- package/src/activities/__tests__/discover-mcp-server.hang.test.ts +103 -0
- package/src/activities/__tests__/discover-mcp-server.test.ts +242 -1
- package/src/activities/__tests__/error-classifier.test.ts +94 -2
- package/src/activities/__tests__/run-command.test.ts +221 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +107 -8
- package/src/activities/call-llm.ts +9 -16
- package/src/activities/classify-tool-approvals.ts +34 -4
- package/src/activities/discover-mcp-server.ts +203 -31
- package/src/activities/emit-event.ts +65 -21
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +9 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +14 -0
- package/src/activities/execute-cursor/__tests__/attachment-resolver.test.ts +53 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +151 -16
- package/src/activities/execute-cursor/__tests__/error-classifier-extraction.test.ts +208 -0
- package/src/activities/execute-cursor/__tests__/extract-structured-output.test.ts +120 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +93 -0
- package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +125 -0
- package/src/activities/execute-cursor/__tests__/model-pricing.test.ts +62 -1
- package/src/activities/execute-cursor/__tests__/prompt-builder-delegation.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/turn-recovery.test.ts +160 -0
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +13 -0
- package/src/activities/execute-cursor/approval-state.ts +30 -1
- package/src/activities/execute-cursor/attachment-resolver.ts +31 -3
- package/src/activities/execute-cursor/blueprint-resolver.ts +7 -27
- package/src/activities/execute-cursor/env-resolver.ts +3 -1
- package/src/activities/execute-cursor/error-classifier.ts +131 -7
- package/src/activities/execute-cursor/extract-structured-output.ts +72 -0
- package/src/activities/execute-cursor/hook-script.ts +74 -10
- package/src/activities/execute-cursor/index.ts +209 -109
- package/src/activities/execute-cursor/mcp-resolver.ts +36 -2
- package/src/activities/execute-cursor/model-pricing-data.ts +22 -3
- package/src/activities/execute-cursor/prompt-builder.ts +85 -9
- package/src/activities/execute-cursor/turn-recovery.ts +208 -0
- package/src/activities/execute-cursor/turn-stream.ts +5 -2
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +110 -8
- package/src/activities/execute-deep-agent/__tests__/datastore-degradation.test.ts +104 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +2 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +174 -0
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +34 -5
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/shell-env.test.ts +24 -10
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +178 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +12 -7
- package/src/activities/execute-deep-agent/attachment-injector.ts +94 -30
- package/src/activities/execute-deep-agent/environment.ts +3 -1
- package/src/activities/execute-deep-agent/index.ts +20 -0
- package/src/activities/execute-deep-agent/prompt-builder.ts +20 -10
- package/src/activities/execute-deep-agent/setup.ts +85 -29
- package/src/activities/execute-deep-agent/shell-env.ts +8 -9
- package/src/activities/execute-deep-agent/subagent-transformer.ts +30 -2
- package/src/activities/execute-deep-agent/subagent-wiring.ts +39 -6
- package/src/activities/hydrate-workflow-execution.ts +8 -3
- package/src/activities/run-command.ts +25 -12
- package/src/activities/run-env.ts +79 -0
- package/src/activities/workflow-event-activities.ts +96 -69
- package/src/bootstrap.ts +5 -0
- package/src/claimcheck/payload-codec.ts +33 -1
- package/src/client/__tests__/stigmer-client.test.ts +110 -11
- package/src/client/stigmer-client.ts +150 -37
- package/src/encryption/config.ts +155 -0
- package/src/encryption/index.ts +3 -0
- package/src/encryption/payload-codec.ts +152 -0
- package/src/middleware/__tests__/path-normalization.test.ts +140 -0
- package/src/middleware/index.ts +8 -0
- package/src/middleware/path-normalization.ts +125 -0
- package/src/middleware/types.ts +11 -0
- package/src/payload-codecs.ts +62 -0
- package/src/preflight.ts +45 -0
- package/src/runner-manager.ts +26 -26
- package/src/runner.ts +12 -27
- package/src/shared/__tests__/approval-policy.test.ts +82 -39
- package/src/shared/__tests__/artifact-storage.test.ts +145 -2
- package/src/shared/__tests__/attachment-naming.test.ts +159 -0
- package/src/shared/__tests__/bedrock-adapter.test.ts +213 -0
- package/src/shared/__tests__/bedrock-seam.test.ts +390 -0
- package/src/shared/__tests__/caller-identity.test.ts +25 -0
- package/src/shared/__tests__/channel-attachment.test.ts +1 -1
- package/src/shared/__tests__/connect-backfill.test.ts +1 -0
- package/src/shared/__tests__/conversation-attachment.test.ts +1 -1
- package/src/shared/__tests__/conversation-catchup.test.ts +24 -0
- package/src/shared/__tests__/datastore-attachment.test.ts +129 -1
- package/src/shared/__tests__/foundry-adapter.test.ts +276 -0
- package/src/shared/__tests__/foundry-seam.test.ts +482 -0
- package/src/shared/__tests__/http-retry.test.ts +68 -0
- package/src/shared/__tests__/llm-backend.test.ts +616 -0
- package/src/shared/__tests__/mcp-enabled-tools.test.ts +86 -0
- package/src/shared/__tests__/mcp-manager.test.ts +137 -15
- package/src/shared/__tests__/mcp-resolver.test.ts +146 -3
- package/src/shared/__tests__/mcp-schema-sanitizer.test.ts +267 -0
- package/src/shared/__tests__/model-client.test.ts +200 -0
- package/src/shared/__tests__/model-error.test.ts +289 -1
- package/src/shared/__tests__/model-pricing.test.ts +62 -2
- package/src/shared/__tests__/model-registry.test.ts +22 -0
- package/src/shared/__tests__/synthesized-attachment.test.ts +1 -0
- package/src/shared/__tests__/vertex-adapter.test.ts +169 -0
- package/src/shared/__tests__/vertex-seam.test.ts +295 -0
- package/src/shared/approval-policy.ts +14 -7
- package/src/shared/artifact-storage.ts +75 -10
- package/src/shared/attachment-naming.ts +78 -0
- package/src/shared/caller-identity.ts +40 -5
- package/src/shared/channel-attachment.ts +1 -0
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +196 -1
- package/src/shared/checkpointer/http-saver.ts +71 -8
- package/src/shared/conversation-attachment.ts +1 -0
- package/src/shared/conversation-catchup.ts +39 -6
- package/src/shared/datastore-attachment.ts +106 -11
- package/src/shared/grpc-retry.ts +5 -1
- package/src/shared/http-retry.ts +139 -0
- package/src/shared/llm-backend.ts +544 -0
- package/src/shared/llm-proxy.ts +15 -0
- package/src/shared/mcp-enabled-tools.ts +105 -0
- package/src/shared/mcp-manager.ts +56 -8
- package/src/shared/mcp-resolver.ts +73 -2
- package/src/shared/mcp-schema-sanitizer.ts +224 -0
- package/src/shared/model-client.ts +179 -19
- package/src/shared/model-error.ts +222 -4
- package/src/shared/model-pricing-data.ts +22 -3
- package/src/shared/model-registry.ts +7 -2
- package/src/shared/plan-mode-permissions.ts +37 -0
- package/src/shared/registry-endpoint.ts +27 -0
- package/src/shared/runner-credential-keys.ts +46 -0
- package/src/worker.ts +4 -5
- package/src/workflow-engine/__tests__/tasks/emit-event.test.ts +127 -49
- package/src/workflow-engine/resolve.ts +48 -2
- package/src/workflow-engine/types.ts +18 -0
- package/src/workflows/__tests__/connect-mcp-server.test.ts +25 -0
- package/src/workflows/__tests__/execute-serverless-workflow.test.ts +68 -2
- package/src/workflows/call-agent-orchestrator.ts +10 -0
- package/src/workflows/connect-mcp-server.ts +9 -0
- package/src/workflows/engine-core.ts +23 -2
- package/src/workflows/execute-from-execution.ts +12 -2
- package/src/workflows/types.ts +8 -0
- package/dist/activities/execute-cursor/mcp-config.d.ts +0 -30
- package/dist/activities/execute-cursor/mcp-config.js +0 -39
- package/dist/activities/execute-cursor/mcp-config.js.map +0 -1
- package/src/activities/execute-cursor/mcp-config.ts +0 -66
|
@@ -12,14 +12,22 @@
|
|
|
12
12
|
* Error model: fail-hard. Any attachment failure aborts the entire injection
|
|
13
13
|
* and propagates a descriptive error. Attachments are explicit user inputs —
|
|
14
14
|
* running with partial inputs produces silently incorrect results.
|
|
15
|
+
*
|
|
16
|
+
* Duplicate names are NOT a failure (issue #364): a default-derived mount
|
|
17
|
+
* path that collides is renamed with the platform's `stem-2.ext` semantics
|
|
18
|
+
* (shared/attachment-naming.ts) and the rename is disclosed in the prompt's
|
|
19
|
+
* Input Files section. Only two attachments EXPLICITLY pinning the same
|
|
20
|
+
* `mountPath` still abort — that is a user contradiction no rename can
|
|
21
|
+
* honestly resolve.
|
|
15
22
|
*/
|
|
16
23
|
|
|
17
24
|
import { readFile } from "node:fs/promises";
|
|
18
25
|
import { createInflateRaw } from "node:zlib";
|
|
19
|
-
import {
|
|
26
|
+
import { posix } from "node:path";
|
|
20
27
|
import type { Attachment } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
21
28
|
import type { WorkspaceBackend } from "../../shared/workspace/types.js";
|
|
22
29
|
import type { ArtifactStorage } from "../../shared/artifact-storage.js";
|
|
30
|
+
import { allocateUniqueName } from "../../shared/attachment-naming.js";
|
|
23
31
|
import type {
|
|
24
32
|
VisionBudget,
|
|
25
33
|
VisionDegradedReason,
|
|
@@ -38,9 +46,17 @@ export { MAX_ZIP_FILES, MAX_ZIP_EXTRACTED_SIZE };
|
|
|
38
46
|
// ── Types ────────────────────────────────────────────────────────────
|
|
39
47
|
|
|
40
48
|
export interface InjectedFile {
|
|
49
|
+
/** The final on-disk basename — after any duplicate rename, so it always
|
|
50
|
+
* agrees with {@link path} and with the vision payload's image label. */
|
|
41
51
|
readonly filename: string;
|
|
42
52
|
readonly path: string;
|
|
43
53
|
readonly sizeBytes: number;
|
|
54
|
+
/**
|
|
55
|
+
* The attachment's original filename, present only when a duplicate name
|
|
56
|
+
* was renamed (shared/attachment-naming.ts) — rendered as disclosure in
|
|
57
|
+
* the prompt's Input Files section.
|
|
58
|
+
*/
|
|
59
|
+
readonly renamedFrom?: string;
|
|
44
60
|
/** Present when the attachment was accepted into the turn's vision payload. */
|
|
45
61
|
readonly vision?: VisionImage;
|
|
46
62
|
/**
|
|
@@ -257,7 +273,9 @@ export function validateZipForExtraction(
|
|
|
257
273
|
/**
|
|
258
274
|
* Download, validate, and inject all attachments into the workspace.
|
|
259
275
|
*
|
|
260
|
-
*
|
|
276
|
+
* Resolves all mount paths before any downloads begin: duplicate
|
|
277
|
+
* default-derived names are renamed (never fatal — see module doc), and an
|
|
278
|
+
* explicit-mountPath contradiction throws before any bytes move.
|
|
261
279
|
* On any failure, throws AttachmentInjectionError with an actionable message.
|
|
262
280
|
*/
|
|
263
281
|
export async function injectAttachments(opts: InjectAttachmentsOptions): Promise<InjectedFile[]> {
|
|
@@ -273,17 +291,24 @@ export async function injectAttachments(opts: InjectAttachmentsOptions): Promise
|
|
|
273
291
|
|
|
274
292
|
for (const attachment of attachments) {
|
|
275
293
|
const content = await downloadAttachment(attachment, storage, isLocalMode);
|
|
276
|
-
const mountPath = mountPaths.get(attachment)!;
|
|
294
|
+
const { path: mountPath, renamedFrom } = mountPaths.get(attachment)!;
|
|
277
295
|
|
|
278
296
|
if (attachment.extract) {
|
|
279
297
|
const entries = validateZipForExtraction(content, attachment.filename);
|
|
280
298
|
const extracted = await extractZipToWorkspace(
|
|
281
299
|
content, entries, mountPath, backend,
|
|
282
300
|
);
|
|
301
|
+
// A renamed mount DIR is visible through every extracted path; the
|
|
302
|
+
// entries themselves were not renamed, so they carry no renamedFrom
|
|
303
|
+
// (per-file disclosure would misattribute the rename).
|
|
283
304
|
injectedFiles.push(...extracted);
|
|
284
305
|
} else {
|
|
285
306
|
await backend.writeFileBuffer(mountPath, content);
|
|
286
|
-
|
|
307
|
+
// The final basename is the canonical filename: after a duplicate
|
|
308
|
+
// rename the original attachment.filename no longer names the file on
|
|
309
|
+
// disk, and the vision label must match what the prompt lists or the
|
|
310
|
+
// agent sees two images with one indistinguishable name.
|
|
311
|
+
const filename = posix.basename(mountPath);
|
|
287
312
|
// The bytes are already in hand for the workspace write — offer them to
|
|
288
313
|
// the vision budget before they go out of scope (the sniff decides
|
|
289
314
|
// eligibility; the budget owns every size/count rule).
|
|
@@ -292,6 +317,7 @@ export async function injectAttachments(opts: InjectAttachmentsOptions): Promise
|
|
|
292
317
|
filename,
|
|
293
318
|
path: mountPath,
|
|
294
319
|
sizeBytes: content.length,
|
|
320
|
+
...(renamedFrom !== undefined ? { renamedFrom } : {}),
|
|
295
321
|
...(vision?.kind === "accepted" ? { vision: vision.image } : {}),
|
|
296
322
|
...(vision?.kind === "degraded" ? { visionDegraded: vision.reason } : {}),
|
|
297
323
|
});
|
|
@@ -307,14 +333,24 @@ export async function injectAttachments(opts: InjectAttachmentsOptions): Promise
|
|
|
307
333
|
|
|
308
334
|
// ── Internal Helpers ─────────────────────────────────────────────────
|
|
309
335
|
|
|
336
|
+
interface ResolvedMountPath {
|
|
337
|
+
readonly path: string;
|
|
338
|
+
/** Present when a default-derived name was uniquified (issue #364). */
|
|
339
|
+
readonly renamedFrom?: string;
|
|
340
|
+
}
|
|
341
|
+
|
|
310
342
|
function resolveMountPaths(
|
|
311
343
|
attachments: readonly Attachment[],
|
|
312
|
-
): Map<Attachment,
|
|
313
|
-
const result = new Map<Attachment,
|
|
344
|
+
): Map<Attachment, ResolvedMountPath> {
|
|
345
|
+
const result = new Map<Attachment, ResolvedMountPath>();
|
|
314
346
|
const pathToAttachment = new Map<string, Attachment>();
|
|
315
347
|
|
|
348
|
+
// Pass 1: explicit mount paths claim their exact targets first. Two
|
|
349
|
+
// attachments explicitly pinning the SAME path is a user contradiction no
|
|
350
|
+
// rename can honestly resolve — keep rejecting with the actionable message.
|
|
316
351
|
for (const attachment of attachments) {
|
|
317
|
-
|
|
352
|
+
if (!attachment.mountPath) continue;
|
|
353
|
+
const mountPath = resolveExplicitMountPath(attachment);
|
|
318
354
|
const existing = pathToAttachment.get(mountPath);
|
|
319
355
|
|
|
320
356
|
if (existing) {
|
|
@@ -326,35 +362,63 @@ function resolveMountPaths(
|
|
|
326
362
|
}
|
|
327
363
|
|
|
328
364
|
pathToAttachment.set(mountPath, attachment);
|
|
329
|
-
result.set(attachment, mountPath);
|
|
365
|
+
result.set(attachment, { path: mountPath });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Pass 2: default-derived names (`.stigmer/inputs/{filename}`) uniquify
|
|
369
|
+
// around everything already taken — other defaults AND explicit paths that
|
|
370
|
+
// landed inside the inputs prefix — instead of failing the execution
|
|
371
|
+
// (issue #364). The taken-set is seeded with the basenames the explicit
|
|
372
|
+
// pass claimed directly under the prefix.
|
|
373
|
+
const takenNames = new Set<string>();
|
|
374
|
+
for (const path of pathToAttachment.keys()) {
|
|
375
|
+
if (path.startsWith(`${DEFAULT_INPUTS_PREFIX}/`)) {
|
|
376
|
+
const rest = path.slice(DEFAULT_INPUTS_PREFIX.length + 1);
|
|
377
|
+
if (rest.length > 0 && !rest.includes("/")) takenNames.add(rest);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const attachment of attachments) {
|
|
381
|
+
if (attachment.mountPath) continue;
|
|
382
|
+
const { name, renamedFrom } = allocateUniqueName(
|
|
383
|
+
deriveDefaultFilename(attachment),
|
|
384
|
+
takenNames,
|
|
385
|
+
);
|
|
386
|
+
const path = `${DEFAULT_INPUTS_PREFIX}/${name}`;
|
|
387
|
+
pathToAttachment.set(path, attachment);
|
|
388
|
+
result.set(
|
|
389
|
+
attachment,
|
|
390
|
+
renamedFrom !== undefined ? { path, renamedFrom } : { path },
|
|
391
|
+
);
|
|
330
392
|
}
|
|
331
393
|
|
|
332
394
|
return result;
|
|
333
395
|
}
|
|
334
396
|
|
|
335
|
-
function
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
// A caller-supplied mount path is untrusted. Stripping leading slashes does
|
|
345
|
-
// not stop `..` segments from climbing out of the workspace root on the
|
|
346
|
-
// non-`.stigmer/` branch (the `.stigmer/`-routed branch is already guarded
|
|
347
|
-
// by LocalWorkspaceBackend.resolvePath). Reject any path that normalizes to
|
|
348
|
-
// an escape before it reaches the backend write.
|
|
349
|
-
if (escapesRoot(cleaned)) {
|
|
350
|
-
throw new AttachmentInjectionError(
|
|
351
|
-
attachment.filename,
|
|
352
|
-
`mount path '${attachment.mountPath}' escapes the workspace root`,
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
return cleaned;
|
|
397
|
+
function resolveExplicitMountPath(attachment: Attachment): string {
|
|
398
|
+
const cleaned = attachment.mountPath.replace(/^\/+/, "");
|
|
399
|
+
if (cleaned.length === 0) {
|
|
400
|
+
throw new AttachmentInjectionError(
|
|
401
|
+
attachment.filename,
|
|
402
|
+
"mountPath resolves to an empty path after removing leading slashes",
|
|
403
|
+
);
|
|
356
404
|
}
|
|
405
|
+
// A caller-supplied mount path is untrusted. Stripping leading slashes does
|
|
406
|
+
// not stop `..` segments from climbing out of the workspace root on the
|
|
407
|
+
// non-`.stigmer/` branch (the `.stigmer/`-routed branch is already guarded
|
|
408
|
+
// by LocalWorkspaceBackend.resolvePath). Reject any path that normalizes to
|
|
409
|
+
// an escape before it reaches the backend write.
|
|
410
|
+
if (escapesRoot(cleaned)) {
|
|
411
|
+
throw new AttachmentInjectionError(
|
|
412
|
+
attachment.filename,
|
|
413
|
+
`mount path '${attachment.mountPath}' escapes the workspace root`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return cleaned;
|
|
417
|
+
}
|
|
357
418
|
|
|
419
|
+
// Derives the single-component filename an attachment without an explicit
|
|
420
|
+
// mountPath materializes under (the name that pass 2 uniquifies).
|
|
421
|
+
function deriveDefaultFilename(attachment: Attachment): string {
|
|
358
422
|
const rawName = attachment.filename || deriveFilename(attachment.storageKey);
|
|
359
423
|
if (!rawName) {
|
|
360
424
|
throw new AttachmentInjectionError(
|
|
@@ -373,7 +437,7 @@ function resolveMountPath(attachment: Attachment): string {
|
|
|
373
437
|
);
|
|
374
438
|
}
|
|
375
439
|
|
|
376
|
-
return
|
|
440
|
+
return filename;
|
|
377
441
|
}
|
|
378
442
|
|
|
379
443
|
// escapesRoot reports whether a workspace-relative mount path would climb out
|
|
@@ -30,7 +30,9 @@ export async function resolveEnvironment(
|
|
|
30
30
|
): Promise<EnvironmentResult> {
|
|
31
31
|
// A desktop runner exchanges its bootstrap credential for a token scoped to
|
|
32
32
|
// this execution's session, so cloud's decrypt gate binds the read (#156).
|
|
33
|
-
// No-op for cloud sandbox and OSS runners.
|
|
33
|
+
// No-op for cloud sandbox and OSS runners. A failed exchange throws and
|
|
34
|
+
// fails the activity: the bootstrap credential no longer decrypts
|
|
35
|
+
// (stigmer-cloud#218), so proceeding would resolve redacted placeholders.
|
|
34
36
|
const scopedToken = await client.acquireScopedRunnerToken({
|
|
35
37
|
agentExecutionId: executionId,
|
|
36
38
|
});
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from "../../shared/workspace/stigmer-link.js";
|
|
33
33
|
import type { ToolOutputOffloadContext } from "../../shared/status-offload.js";
|
|
34
34
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
35
|
+
import { formatDatastoreDegradationNotice } from "../../shared/datastore-attachment.js";
|
|
35
36
|
import { classifyTool } from "../../shared/tool-kind.js";
|
|
36
37
|
import {
|
|
37
38
|
POLICY_ENGINE_VERSION,
|
|
@@ -151,6 +152,25 @@ export function createDeepAgentActivities(config: Config) {
|
|
|
151
152
|
const initialStatus = shouldSeedFromPersistedTranscript(setup.execution)
|
|
152
153
|
? seedStatusFromExecution(setup.execution)
|
|
153
154
|
: create(AgentExecutionStatusSchema, {});
|
|
155
|
+
|
|
156
|
+
// Datastore degradation disclosure, operator half (issue #325): the
|
|
157
|
+
// prompt already tells the agent to disclose the outage; this makes
|
|
158
|
+
// it visible on the execution record without reading a transcript.
|
|
159
|
+
// Appended to initialStatus BEFORE streaming (not via a status
|
|
160
|
+
// builder): both the v2 and v3 stream paths construct their own
|
|
161
|
+
// builder around this same proto and only ever append messages, so
|
|
162
|
+
// this row survives whichever path runs and rides every persist.
|
|
163
|
+
if (setup.datastoreToolsMissing.length > 0) {
|
|
164
|
+
initialStatus.messages.push(create(AgentMessageSchema, {
|
|
165
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
166
|
+
content: formatDatastoreDegradationNotice(
|
|
167
|
+
setup.agent.spec?.datastoreUsages?.length ?? 0,
|
|
168
|
+
setup.datastoreToolsMissing,
|
|
169
|
+
),
|
|
170
|
+
timestamp: utcTimestamp(),
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
const statusBuilder = new StatusBuilder(executionId, initialStatus);
|
|
155
175
|
|
|
156
176
|
statusBuilder.setApprovalProvider({
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
buildImplementPlanDirective,
|
|
27
27
|
findApprovedPlanPath,
|
|
28
28
|
} from "../../shared/implement-plan-prompt.js";
|
|
29
|
+
import type { InjectedFile } from "./attachment-injector.js";
|
|
29
30
|
|
|
30
31
|
const RESPONSE_RULES = `
|
|
31
32
|
|
|
@@ -99,8 +100,11 @@ export interface PromptBuilderInput {
|
|
|
99
100
|
containerRoot: string;
|
|
100
101
|
skillsPromptSection: string;
|
|
101
102
|
/**
|
|
102
|
-
* The
|
|
103
|
-
* formatDatastoresSection)
|
|
103
|
+
* The datastores section (shared/datastore-attachment.ts
|
|
104
|
+
* formatDatastoresSection): `<available_datastores>` when the connected
|
|
105
|
+
* record-tool roster is complete, `<unavailable_datastores>` when setup's
|
|
106
|
+
* reconciliation found record tools missing (issue #325). Empty when the
|
|
107
|
+
* agent uses no datastores.
|
|
104
108
|
*/
|
|
105
109
|
datastoresPromptSection?: string;
|
|
106
110
|
/**
|
|
@@ -162,11 +166,10 @@ export interface PromptBuilderInput {
|
|
|
162
166
|
sessionContext?: string;
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
169
|
+
// The prompt renders the injector's own result type — a local structural twin
|
|
170
|
+
// once lived here and silently dropped the size field (`size` vs `sizeBytes`),
|
|
171
|
+
// so the "(N bytes)" annotation never rendered. One type, one truth.
|
|
172
|
+
export type { InjectedFile } from "./attachment-injector.js";
|
|
170
173
|
|
|
171
174
|
/**
|
|
172
175
|
* Which images ride the user message inline (in send order) and which
|
|
@@ -396,7 +399,7 @@ function buildReferencedFilesSection(
|
|
|
396
399
|
}
|
|
397
400
|
|
|
398
401
|
function buildInjectedFilesSection(
|
|
399
|
-
files: InjectedFile[],
|
|
402
|
+
files: readonly InjectedFile[],
|
|
400
403
|
vision?: VisionPromptInfo,
|
|
401
404
|
): string {
|
|
402
405
|
let section = "\n\n## Input Files\n\n";
|
|
@@ -410,8 +413,15 @@ function buildInjectedFilesSection(
|
|
|
410
413
|
"Do NOT modify or delete these files.\n\n";
|
|
411
414
|
|
|
412
415
|
for (const f of files) {
|
|
413
|
-
const sizeInfo =
|
|
414
|
-
|
|
416
|
+
const sizeInfo = ` (${f.sizeBytes} bytes)`;
|
|
417
|
+
// A duplicate-renamed file (attachment-naming.ts) discloses its original
|
|
418
|
+
// name so the agent can connect "the two report.pdfs" in the user's
|
|
419
|
+
// message to distinct files on disk.
|
|
420
|
+
const renameInfo =
|
|
421
|
+
f.renamedFrom !== undefined
|
|
422
|
+
? ` (renamed from duplicate '${f.renamedFrom}')`
|
|
423
|
+
: "";
|
|
424
|
+
section += `- \`${f.path}\`${sizeInfo}${renameInfo}\n`;
|
|
415
425
|
}
|
|
416
426
|
|
|
417
427
|
// The vision lines (shared wording, attachment-vision.ts) tell the model
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* the streaming phase needs.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { createDeepAgent
|
|
11
|
+
import { createDeepAgent } from "deepagents";
|
|
12
12
|
import { InteractionMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
13
13
|
import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
14
14
|
import { z } from "zod";
|
|
@@ -33,11 +33,13 @@ import {
|
|
|
33
33
|
} from "../../shared/caller-identity.js";
|
|
34
34
|
import { readSessionContext } from "../../shared/session-context.js";
|
|
35
35
|
import { connectMcpServers, type McpConnectionResult } from "../../shared/mcp-manager.js";
|
|
36
|
-
import { resolveMcpServers } from "../../shared/mcp-resolver.js";
|
|
36
|
+
import { mergeMcpServerUsages, resolveMcpServers } from "../../shared/mcp-resolver.js";
|
|
37
37
|
import { resolveMcpTransportPosture } from "../../shared/mcp-transport-guard.js";
|
|
38
38
|
import { backfillMcpServersIfNeeded } from "../../shared/connect-backfill.js";
|
|
39
39
|
import {
|
|
40
|
+
DATASTORE_ATTACHMENT_SLUG,
|
|
40
41
|
formatDatastoresSection,
|
|
42
|
+
missingRecordTools,
|
|
41
43
|
synthesizeDatastoreAttachment,
|
|
42
44
|
} from "../../shared/datastore-attachment.js";
|
|
43
45
|
import {
|
|
@@ -56,6 +58,7 @@ import { LocalWorkspaceBackend } from "../../shared/workspace/local-backend.js";
|
|
|
56
58
|
import type { WorkspaceBackend, ProvisionResult } from "../../shared/workspace/types.js";
|
|
57
59
|
import { createCasCaptureBackend } from "./cas-capture-backend.js";
|
|
58
60
|
import { buildShellEnv } from "./shell-env.js";
|
|
61
|
+
import { PLAN_MODE_PERMISSIONS } from "../../shared/plan-mode-permissions.js";
|
|
59
62
|
import { CasCaptureObserver } from "./cas-capture-observer.js";
|
|
60
63
|
import { isGitWorkTree, isPathCapturable } from "../../shared/filereview/git-substrate.js";
|
|
61
64
|
import { deriveCaptureMode } from "../../shared/filereview/capture.js";
|
|
@@ -126,6 +129,16 @@ export interface SetupResult {
|
|
|
126
129
|
readonly session: Session;
|
|
127
130
|
readonly workspaceBackend: WorkspaceBackend;
|
|
128
131
|
readonly mcpConnection: McpConnectionResult | null;
|
|
132
|
+
/**
|
|
133
|
+
* Record tools declared via datastore_usages but absent from the connected
|
|
134
|
+
* stigmer-records roster this turn (issue #325). Empty = healthy (also when
|
|
135
|
+
* the agent uses no datastores). Non-empty selects the degraded prompt
|
|
136
|
+
* section in Step 8, and the activity surfaces it to operators as a
|
|
137
|
+
* MESSAGE_SYSTEM row — the vision-facts pattern: setup computes the honest
|
|
138
|
+
* struct, the caller discloses. Recomputed every turn, so a healed outage
|
|
139
|
+
* stops disclosing on the next turn.
|
|
140
|
+
*/
|
|
141
|
+
readonly datastoreToolsMissing: readonly string[];
|
|
129
142
|
readonly mergedEnvVars: Record<string, string>;
|
|
130
143
|
readonly secretKeys: ReadonlySet<string>;
|
|
131
144
|
readonly modelName: string;
|
|
@@ -348,20 +361,36 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
348
361
|
: async () => true,
|
|
349
362
|
});
|
|
350
363
|
|
|
351
|
-
// Step 7: Resolve and connect MCP servers
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
364
|
+
// Step 7: Resolve and connect MCP servers. Session-wins-per-slug merge
|
|
365
|
+
// (the Cursor harness's blueprint semantics, shared so a duplicate slug
|
|
366
|
+
// resolves identically in both harnesses — one usage per server, whose
|
|
367
|
+
// enabled_tools is the one the connect-time filter honors).
|
|
368
|
+
const mcpServerUsages = mergeMcpServerUsages(
|
|
369
|
+
agent.spec!.mcpServerUsages || [],
|
|
370
|
+
session.spec!.mcpServerUsages || [],
|
|
371
|
+
);
|
|
356
372
|
const datastoreUsages = agent.spec!.datastoreUsages || [];
|
|
357
373
|
|
|
358
374
|
// The synthesized attachments' credential story (DD-006 D4): the
|
|
359
375
|
// exchanged token authenticates the discovery reads per-call (the
|
|
360
376
|
// messaging reach refuses a desktop runner's ambient embedded_runner
|
|
361
377
|
// credential; undefined lets the ambient credential apply). The
|
|
362
|
-
// attachment header falls back to the ambient credential.
|
|
363
|
-
|
|
364
|
-
|
|
378
|
+
// attachment header falls back to the ambient credential. Unlike the
|
|
379
|
+
// env read (which hard-fails on a broken exchange — secrets are
|
|
380
|
+
// load-bearing there), this exchange is opportunistic: every consumer
|
|
381
|
+
// below degrades to an empty answer by contract, and the server
|
|
382
|
+
// refuses the ambient fallback safely, so a failed exchange must not
|
|
383
|
+
// kill the run.
|
|
384
|
+
let exchangedRunnerToken: string | undefined;
|
|
385
|
+
try {
|
|
386
|
+
exchangedRunnerToken =
|
|
387
|
+
await client.acquireScopedRunnerToken({ agentExecutionId: executionId });
|
|
388
|
+
} catch (err) {
|
|
389
|
+
console.warn(
|
|
390
|
+
"[execute-deep-agent] Scoped-token exchange failed for attachment/discovery " +
|
|
391
|
+
`reads; degrading to the ambient credential: ${err instanceof Error ? err.message : err}`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
365
394
|
const attachmentCredential = exchangedRunnerToken
|
|
366
395
|
?? config.stigmerTokenRef?.current
|
|
367
396
|
?? config.stigmerToken;
|
|
@@ -556,6 +585,28 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
556
585
|
}
|
|
557
586
|
timing.mark("inject_attachments");
|
|
558
587
|
|
|
588
|
+
// Reconcile the declared datastores against the record tools actually
|
|
589
|
+
// connected (issue #325): the records roster always serves all five
|
|
590
|
+
// tools, so any absence means the store is degraded, and the prompt
|
|
591
|
+
// must say so instead of promising tools the agent does not have. The
|
|
592
|
+
// connected roster is post-connect truth — when usages exist, MCP
|
|
593
|
+
// connect always ran above (a thrown connect already failed the run).
|
|
594
|
+
const datastoreToolsMissing = datastoreUsages.length > 0 && mcpConnection
|
|
595
|
+
? missingRecordTools(
|
|
596
|
+
(mcpConnection.serverToolMap[DATASTORE_ATTACHMENT_SLUG] ?? [])
|
|
597
|
+
.map((t) => t.name),
|
|
598
|
+
)
|
|
599
|
+
: [];
|
|
600
|
+
if (datastoreToolsMissing.length > 0) {
|
|
601
|
+
console.warn(
|
|
602
|
+
`[setup] Datastore record tools degraded: declared ` +
|
|
603
|
+
`${datastoreUsages.length} datastore usage(s) but the ` +
|
|
604
|
+
`'${DATASTORE_ATTACHMENT_SLUG}' roster is missing ` +
|
|
605
|
+
`[${datastoreToolsMissing.join(", ")}] — the prompt discloses the ` +
|
|
606
|
+
`outage instead of the available-datastores section.`,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
559
610
|
// Step 8: Build enhanced system prompt
|
|
560
611
|
const systemPrompt = buildEnhancedSystemPrompt({
|
|
561
612
|
instructions,
|
|
@@ -563,7 +614,7 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
563
614
|
containerRoot: workspaceBackend.rootDir,
|
|
564
615
|
skillsPromptSection,
|
|
565
616
|
datastoresPromptSection: datastoreUsages.length > 0
|
|
566
|
-
? formatDatastoresSection(datastoreUsages)
|
|
617
|
+
? formatDatastoresSection(datastoreUsages, datastoreToolsMissing)
|
|
567
618
|
: undefined,
|
|
568
619
|
// "" (nothing sendable) threads as undefined: the tool alone still
|
|
569
620
|
// serves text sends inside a 24-hour window (DD-006 D6).
|
|
@@ -584,14 +635,14 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
584
635
|
// Step 9: Construct the LLM model. Resolution to the provider API id
|
|
585
636
|
// happens inside buildChatModel; modelName stays the registry id for
|
|
586
637
|
// pricing, the native-thinking heuristic, and sub-agent inheritance.
|
|
587
|
-
|
|
588
|
-
|
|
638
|
+
// The operator's STIGMER_LLM_REQUEST_TIMEOUT_MS bound is applied inside
|
|
639
|
+
// buildChatModel (#468) — the sub-agent modelFactory below silently
|
|
640
|
+
// dropped it when each caller parsed the env itself.
|
|
589
641
|
const { model } = await buildChatModel({
|
|
590
642
|
modelName,
|
|
591
643
|
proxyEndpoint: config.proxyEndpoint ?? undefined,
|
|
592
644
|
stigmerToken: config.stigmerToken ?? undefined,
|
|
593
645
|
headerScope: { executionId },
|
|
594
|
-
timeoutMs: requestTimeoutMs,
|
|
595
646
|
});
|
|
596
647
|
timing.mark("build_model");
|
|
597
648
|
|
|
@@ -601,6 +652,12 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
601
652
|
const execConfig = execution.spec!.executionConfig;
|
|
602
653
|
const isPlanMode = execConfig?.interactionMode === InteractionMode.PLAN;
|
|
603
654
|
const shellEnv = isPlanMode ? undefined : buildShellEnv(envResult.mergedEnvVars);
|
|
655
|
+
// Plan mode's filesystem permission rules, hoisted once: the parent graph,
|
|
656
|
+
// every sub-agent graph, AND the path-normalization middleware (issue #429)
|
|
657
|
+
// all derive from this single value, so a rule-bearing graph can never miss
|
|
658
|
+
// the shim that keeps prompt-compliant relative paths from dying in rule
|
|
659
|
+
// validation (`path must be absolute`).
|
|
660
|
+
const planModePermissions = isPlanMode ? PLAN_MODE_PERMISSIONS : undefined;
|
|
604
661
|
|
|
605
662
|
const toolServerMap = new Map<string, string>();
|
|
606
663
|
if (mcpConnection) {
|
|
@@ -621,12 +678,11 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
621
678
|
// gate. Both flow into sub-agents via the shared config below.
|
|
622
679
|
const leases = deriveActiveLeases(execution);
|
|
623
680
|
const globalBypass = leases.global;
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
)
|
|
681
|
+
// Layer-3 overrides ride each resolved server from its (session-wins
|
|
682
|
+
// merged) usage — see ResolvedMcpServer.toolApprovalOverrides (issue
|
|
683
|
+
// #349) — so there is no separate override input to pass here.
|
|
627
684
|
const approvalPolicies = mergeApprovalPolicies(
|
|
628
685
|
resolvedMcpServers?.resolvedServers ?? [],
|
|
629
|
-
agentOverrides,
|
|
630
686
|
leases,
|
|
631
687
|
);
|
|
632
688
|
|
|
@@ -707,6 +763,9 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
707
763
|
} : null,
|
|
708
764
|
otelSpans: { toolServerMap },
|
|
709
765
|
approvalGate: approvalGateConfig,
|
|
766
|
+
pathNormalization: planModePermissions
|
|
767
|
+
? { rootDir: workspaceBackend.rootDir }
|
|
768
|
+
: null,
|
|
710
769
|
});
|
|
711
770
|
timing.mark("build_middleware");
|
|
712
771
|
|
|
@@ -767,6 +826,12 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
767
826
|
// Presence of shellEnv is the shell-capability switch for sub-agent
|
|
768
827
|
// backends too (undefined in plan mode; see buildShellEnv above).
|
|
769
828
|
shellEnv,
|
|
829
|
+
// Plan mode's deny-all-writes rule, mirrored onto every sub-agent graph
|
|
830
|
+
// (issue #255) — pre-built CompiledSubAgents never inherit the parent's
|
|
831
|
+
// permissions, so read-only-by-construction must be baked in here.
|
|
832
|
+
// compileSubagents derives each sub-agent's path-normalization shim
|
|
833
|
+
// from this same value (issue #429).
|
|
834
|
+
...(planModePermissions ? { permissions: planModePermissions } : {}),
|
|
770
835
|
});
|
|
771
836
|
timing.mark("compile_subagents");
|
|
772
837
|
}
|
|
@@ -780,16 +845,6 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
780
845
|
responseFormat = jsonSchemaToZod(outputSchema as unknown as Record<string, unknown>);
|
|
781
846
|
}
|
|
782
847
|
|
|
783
|
-
// Plan mode is read-only. Deny every filesystem write operation at the tool
|
|
784
|
-
// level so write_file/edit_file/etc. cannot mutate the workspace — enforcing
|
|
785
|
-
// the InteractionMode.PLAN contract by construction, not by prompt. Rules are
|
|
786
|
-
// first-match-wins with a permissive default, so a single deny-all-writes
|
|
787
|
-
// rule is sufficient. (The Cursor harness enforces plan mode via its prompt
|
|
788
|
-
// prefix; the native harness enforces it here.)
|
|
789
|
-
const planModePermissions: FilesystemPermission[] = [
|
|
790
|
-
{ operations: ["write"], paths: ["/**"], mode: "deny" },
|
|
791
|
-
];
|
|
792
|
-
|
|
793
848
|
// File capture point. Apply-then-review is universal (Slice 2b), so the
|
|
794
849
|
// CAS-observing backend: git-tracked edits flow to disk (the turn-boundary
|
|
795
850
|
// git diff is authoritative), and the shared CAS observer records the pre-turn
|
|
@@ -810,7 +865,7 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
810
865
|
middleware: middleware as any,
|
|
811
866
|
subagents: compiledSubagents ?? undefined,
|
|
812
867
|
...(responseFormat ? { responseFormat } : {}),
|
|
813
|
-
...(
|
|
868
|
+
...(planModePermissions ? { permissions: planModePermissions } : {}),
|
|
814
869
|
} as Parameters<typeof createDeepAgent>[0]);
|
|
815
870
|
|
|
816
871
|
// Step 11: Prepare invocation input and config. The conversation catchup
|
|
@@ -881,6 +936,7 @@ export async function performSetup(deps: SetupDependencies): Promise<SetupResult
|
|
|
881
936
|
session,
|
|
882
937
|
workspaceBackend,
|
|
883
938
|
mcpConnection,
|
|
939
|
+
datastoreToolsMissing,
|
|
884
940
|
mergedEnvVars: envResult.mergedEnvVars,
|
|
885
941
|
secretKeys: envResult.secretKeys,
|
|
886
942
|
modelName,
|
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
* never once at process start.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
];
|
|
9
|
+
import { RUNNER_CREDENTIAL_ENV_KEYS } from "../../shared/runner-credential-keys.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Runner-internal keys that must never reach agent shell commands: every
|
|
13
|
+
* credential the runner holds for its own outbound calls (issue #385). The
|
|
14
|
+
* names — and the rule for adding one — live in runner-credential-keys.ts.
|
|
15
|
+
*/
|
|
16
|
+
export const SHELL_ENV_DENYLIST: readonly string[] = RUNNER_CREDENTIAL_ENV_KEYS;
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Build the environment map passed to deepagents' LocalShellBackend.
|