@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
|
@@ -9,12 +9,24 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Supported delivery targets:
|
|
11
11
|
* - webhook: HTTP POST with Content-Type: application/cloudevents+json
|
|
12
|
-
* - signal:
|
|
12
|
+
* - signal: signal to another workflow execution's listen task, routed
|
|
13
|
+
* through the server's SendSignal lane
|
|
14
|
+
*
|
|
15
|
+
* Signal delivery is deliberately server-mediated (oss#517): a direct
|
|
16
|
+
* Temporal client here would bypass the authorization boundary (any
|
|
17
|
+
* workflow could signal any workflow id in the namespace) and is
|
|
18
|
+
* structurally incompatible with payload encryption — emit→listen is
|
|
19
|
+
* the platform's only runner-to-runner channel, and under per-identity
|
|
20
|
+
* runner keys a sender-encrypted signal fails closed at a receiver
|
|
21
|
+
* holding a different key. The server re-produces the payload, so each
|
|
22
|
+
* side's codec passes it through.
|
|
13
23
|
*
|
|
14
24
|
* CloudEvents spec: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
|
|
15
25
|
*/
|
|
16
26
|
|
|
17
27
|
import { randomUUID } from "node:crypto";
|
|
28
|
+
import type { JsonObject } from "@bufbuild/protobuf";
|
|
29
|
+
import { StigmerClient } from "../client/stigmer-client.js";
|
|
18
30
|
import { loadConfig } from "../config.js";
|
|
19
31
|
import { resolveRuntimePlaceholders } from "../workflow-engine/resolve.js";
|
|
20
32
|
|
|
@@ -24,7 +36,9 @@ export interface WebhookDeliveryTarget {
|
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
export interface SignalDeliveryTarget {
|
|
27
|
-
|
|
39
|
+
/** Target workflow execution id ("wfx_..."), as returned by run/create. */
|
|
40
|
+
readonly execution_id: string;
|
|
41
|
+
/** Signal name matching the target's listen task event id (verbatim). */
|
|
28
42
|
readonly signal_name: string;
|
|
29
43
|
}
|
|
30
44
|
|
|
@@ -54,6 +68,11 @@ export interface EmitEventResult {
|
|
|
54
68
|
|
|
55
69
|
const WEBHOOK_TIMEOUT_MS = 30_000;
|
|
56
70
|
|
|
71
|
+
// Delivery is best-effort by contract, so no single target may consume the
|
|
72
|
+
// CallFunction activity's whole 5m startToClose budget. Same bound as the
|
|
73
|
+
// webhook arm.
|
|
74
|
+
const SIGNAL_TIMEOUT_MS = 30_000;
|
|
75
|
+
|
|
57
76
|
function buildEnvelope(
|
|
58
77
|
config: EmitEventConfig,
|
|
59
78
|
executionId: string,
|
|
@@ -120,33 +139,53 @@ async function deliverWebhook(
|
|
|
120
139
|
}
|
|
121
140
|
}
|
|
122
141
|
|
|
142
|
+
function buildClient(): StigmerClient {
|
|
143
|
+
const config = loadConfig();
|
|
144
|
+
return new StigmerClient({
|
|
145
|
+
endpoint: config.stigmerBackendEndpoint,
|
|
146
|
+
token: config.stigmerToken,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
123
150
|
async function deliverSignal(
|
|
124
151
|
envelope: Record<string, unknown>,
|
|
125
152
|
target: SignalDeliveryTarget,
|
|
153
|
+
client: StigmerClient,
|
|
126
154
|
): Promise<DeliveryError | null> {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
155
|
+
// Refuse the pre-oss#517 field by name: direct-addressing raw Temporal
|
|
156
|
+
// workflow ids is exactly the capability server mediation removes.
|
|
157
|
+
const legacyWorkflowId = (target as { workflow_id?: unknown }).workflow_id;
|
|
158
|
+
if (!target.execution_id) {
|
|
159
|
+
const reason = legacyWorkflowId
|
|
160
|
+
? "signal delivery addresses workflow executions by 'execution_id' (\"wfx_...\"); 'workflow_id' is not supported"
|
|
161
|
+
: "signal delivery requires 'execution_id'";
|
|
162
|
+
return {
|
|
163
|
+
target: `signal:${String(legacyWorkflowId ?? "")}/${target.signal_name ?? ""}`,
|
|
164
|
+
error: reason,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (!target.signal_name) {
|
|
168
|
+
return {
|
|
169
|
+
target: `signal:${target.execution_id}/`,
|
|
170
|
+
error: "signal delivery requires 'signal_name'",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
144
173
|
|
|
174
|
+
try {
|
|
175
|
+
await client.sendWorkflowSignal(
|
|
176
|
+
target.execution_id,
|
|
177
|
+
target.signal_name,
|
|
178
|
+
envelope as JsonObject,
|
|
179
|
+
{ timeoutMs: SIGNAL_TIMEOUT_MS },
|
|
180
|
+
);
|
|
145
181
|
return null;
|
|
146
182
|
} catch (err) {
|
|
183
|
+
// Server refusals arrive as ConnectErrors whose messages carry the code
|
|
184
|
+
// (e.g. [not_found], [failed_precondition] for terminal executions) —
|
|
185
|
+
// surfaced verbatim in delivery_errors, same contract as the webhook arm.
|
|
147
186
|
const message = err instanceof Error ? err.message : String(err);
|
|
148
187
|
return {
|
|
149
|
-
target: `signal:${target.
|
|
188
|
+
target: `signal:${target.execution_id}/${target.signal_name}`,
|
|
150
189
|
error: message,
|
|
151
190
|
};
|
|
152
191
|
}
|
|
@@ -173,13 +212,18 @@ export async function emitEventAction(
|
|
|
173
212
|
const errors: DeliveryError[] = [];
|
|
174
213
|
const env = runtimeEnv ?? {};
|
|
175
214
|
|
|
215
|
+
// One client serves all signal targets of this emit; constructed lazily so
|
|
216
|
+
// webhook-only emits never pay for a gRPC transport.
|
|
217
|
+
let client: StigmerClient | undefined;
|
|
218
|
+
|
|
176
219
|
for (const target of config.delivery) {
|
|
177
220
|
let err: DeliveryError | null = null;
|
|
178
221
|
|
|
179
222
|
if ("webhook" in target) {
|
|
180
223
|
err = await deliverWebhook(envelope, target.webhook, env);
|
|
181
224
|
} else if ("signal" in target) {
|
|
182
|
-
|
|
225
|
+
client ??= buildClient();
|
|
226
|
+
err = await deliverSignal(envelope, target.signal, client);
|
|
183
227
|
}
|
|
184
228
|
|
|
185
229
|
if (err) {
|
|
@@ -108,6 +108,14 @@ export interface CursorHookHarnessOptions {
|
|
|
108
108
|
* non-pausing "unattended" kind and the adapt-and-explain agent message.
|
|
109
109
|
*/
|
|
110
110
|
unattendedSkip?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Per-server enabled_tools allow-lists (issue #350) — restricted servers
|
|
113
|
+
* only. The hook's manifest arm denies a beforeMCPExecution call whose
|
|
114
|
+
* mcp_server_name is listed here with a tool name outside its list (kind
|
|
115
|
+
* "disabled", ahead of every approval bypass). hookMcp payloads carry
|
|
116
|
+
* mcp_server_name "srv".
|
|
117
|
+
*/
|
|
118
|
+
mcpServerEnabledTools?: Record<string, string[]>;
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
/**
|
|
@@ -170,6 +178,7 @@ export function setupCursorHookHarness(opts: CursorHookHarnessOptions = {}): Cur
|
|
|
170
178
|
opts.captureIgnored ?? false,
|
|
171
179
|
opts.gitWorkspace ?? true,
|
|
172
180
|
opts.unattendedSkip ?? false,
|
|
181
|
+
opts.mcpServerEnabledTools ?? {},
|
|
173
182
|
);
|
|
174
183
|
writeFileSync(statePath, JSON.stringify(state), "utf-8");
|
|
175
184
|
}
|
|
@@ -219,6 +219,20 @@ describe("buildApprovalState", () => {
|
|
|
219
219
|
expect((state as unknown as Record<string, unknown>).builtInGatedList).toBeUndefined();
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
+
it("carries the enabled_tools manifest (issue #350) and defaults it to empty", () => {
|
|
223
|
+
// Default: no restriction — the hook's disabled arm is inert.
|
|
224
|
+
const bare = buildApprovalState(mcpPolicies, false, new Set());
|
|
225
|
+
expect(bare.mcpServerEnabledTools).toEqual({});
|
|
226
|
+
|
|
227
|
+
// Restricted servers ride through verbatim for the hook's server-scoped
|
|
228
|
+
// membership check.
|
|
229
|
+
const restricted = buildApprovalState(
|
|
230
|
+
mcpPolicies, false, new Set(), undefined, false, false, true, false,
|
|
231
|
+
{ planton: ["get_cloud_resource"] },
|
|
232
|
+
);
|
|
233
|
+
expect(restricted.mcpServerEnabledTools).toEqual({ planton: ["get_cloud_resource"] });
|
|
234
|
+
});
|
|
235
|
+
|
|
222
236
|
it("emits the CONTENT token when a grant carries a content digest", () => {
|
|
223
237
|
const grants = [{ toolName: "edit", mcpServerSlug: "", key: "write", salient: "/x/gated.txt", contentDigest: "deadbeef", sourceToolCallId: "consent-1" }];
|
|
224
238
|
const state = buildApprovalState(mcpPolicies, false, new Set(), grants);
|
|
@@ -114,6 +114,59 @@ describe("resolveAttachments", () => {
|
|
|
114
114
|
expect(readFileSync(join(platformDir, "inputs", "data.csv"), "utf-8")).toBe("a,b,c");
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
it("uniquifies duplicate filenames on the storage branch — neither file's bytes are lost (issue #364)", async () => {
|
|
118
|
+
// Before the fix this branch had no collision check and the second write
|
|
119
|
+
// silently overwrote the first.
|
|
120
|
+
const { storage } = makeInMemoryArtifactStorage();
|
|
121
|
+
await storage.upload("attachments/01AAA/report.pdf", Buffer.from("first bytes"), "application/pdf");
|
|
122
|
+
await storage.upload("attachments/01BBB/report.pdf", Buffer.from("second bytes"), "application/pdf");
|
|
123
|
+
|
|
124
|
+
const result = await resolveAttachments(
|
|
125
|
+
[
|
|
126
|
+
makeAttachment({ filename: "report.pdf", storageKey: "attachments/01AAA/report.pdf" }),
|
|
127
|
+
makeAttachment({ filename: "report.pdf", storageKey: "attachments/01BBB/report.pdf" }),
|
|
128
|
+
],
|
|
129
|
+
options({ storage }),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(result).toEqual([
|
|
133
|
+
{ filename: "report.pdf", relativePath: ".stigmer/inputs/report.pdf" },
|
|
134
|
+
{
|
|
135
|
+
filename: "report-2.pdf",
|
|
136
|
+
relativePath: ".stigmer/inputs/report-2.pdf",
|
|
137
|
+
renamedFrom: "report.pdf",
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
expect(readFileSync(join(platformDir, "inputs", "report.pdf"), "utf-8")).toBe("first bytes");
|
|
141
|
+
expect(readFileSync(join(platformDir, "inputs", "report-2.pdf"), "utf-8")).toBe("second bytes");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("uniquifies duplicate filenames across the local and storage branches (one shared taken-set)", async () => {
|
|
145
|
+
const srcPath = join(workspaceDir, "notes.md");
|
|
146
|
+
writeFileSync(srcPath, "local copy");
|
|
147
|
+
const { storage } = makeInMemoryArtifactStorage();
|
|
148
|
+
await storage.upload("attachments/01ABC/notes.md", Buffer.from("uploaded copy"), "text/markdown");
|
|
149
|
+
|
|
150
|
+
const result = await resolveAttachments(
|
|
151
|
+
[
|
|
152
|
+
makeAttachment({ filename: "notes.md", storageKey: "", localPath: srcPath }),
|
|
153
|
+
makeAttachment({ filename: "notes.md", storageKey: "attachments/01ABC/notes.md" }),
|
|
154
|
+
],
|
|
155
|
+
options({ storage }),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(result).toEqual([
|
|
159
|
+
{ filename: "notes.md", relativePath: ".stigmer/inputs/notes.md" },
|
|
160
|
+
{
|
|
161
|
+
filename: "notes-2.md",
|
|
162
|
+
relativePath: ".stigmer/inputs/notes-2.md",
|
|
163
|
+
renamedFrom: "notes.md",
|
|
164
|
+
},
|
|
165
|
+
]);
|
|
166
|
+
expect(readFileSync(join(platformDir, "inputs", "notes.md"), "utf-8")).toBe("local copy");
|
|
167
|
+
expect(readFileSync(join(platformDir, "inputs", "notes-2.md"), "utf-8")).toBe("uploaded copy");
|
|
168
|
+
});
|
|
169
|
+
|
|
117
170
|
it("ignores localPath in cloud mode and downloads by storage key", async () => {
|
|
118
171
|
const { storage } = makeInMemoryArtifactStorage();
|
|
119
172
|
await storage.upload("attachments/01ABC/plan.md", Buffer.from("from storage"), "text/markdown");
|
|
@@ -14,7 +14,7 @@ import { ApprovalAction, InteractionMode } from "@stigmer/protos/ai/stigmer/agen
|
|
|
14
14
|
import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
15
15
|
import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
16
16
|
|
|
17
|
-
import { buildPrompt, isHitlReinvocation } from "../index.js";
|
|
17
|
+
import { appendStructuredOutputDirective, buildPrompt, isHitlReinvocation, primarySendCarriesImages } from "../index.js";
|
|
18
18
|
import type { BuildPromptInput } from "../index.js";
|
|
19
19
|
import { buildReinvocationPrompt, formatInteractionModePrefix, formatImplementPlanSection, formatToolApprovalProtocol, buildToolApprovalRuleFile } from "../prompt-builder.js";
|
|
20
20
|
import { PLAN_MODE_DIRECTIVE } from "../../../shared/plan-mode-prompt.js";
|
|
@@ -47,7 +47,7 @@ function input(overrides: Partial<BuildPromptInput>): BuildPromptInput {
|
|
|
47
47
|
subAgents: [],
|
|
48
48
|
workspaceDirs: ["/tmp/ws"],
|
|
49
49
|
workspaceFileRefs: [],
|
|
50
|
-
|
|
50
|
+
attachments: [],
|
|
51
51
|
pendingApprovals: [],
|
|
52
52
|
...overrides,
|
|
53
53
|
};
|
|
@@ -103,6 +103,10 @@ describe("buildPrompt", () => {
|
|
|
103
103
|
expect(prompt).toContain("<available_datastores>");
|
|
104
104
|
expect(prompt).toContain("- clinic");
|
|
105
105
|
expect(prompt).toContain("describe_datastore");
|
|
106
|
+
// The standing failure-disclosure instruction (issue #325) is this
|
|
107
|
+
// harness's ONLY outage coverage: the Cursor SDK connects MCP itself,
|
|
108
|
+
// so the runner can never reconcile the live roster here.
|
|
109
|
+
expect(prompt).toContain("do not answer from memory");
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
it("omits the datastores section when the agent uses no datastores", () => {
|
|
@@ -251,7 +255,7 @@ describe("buildPrompt", () => {
|
|
|
251
255
|
});
|
|
252
256
|
});
|
|
253
257
|
|
|
254
|
-
describe("isHitlReinvocation (
|
|
258
|
+
describe("isHitlReinvocation (paired with resolution.reason at every consumer — issue #366)", () => {
|
|
255
259
|
it("is false with no decisions and false with an empty map", () => {
|
|
256
260
|
expect(isHitlReinvocation(undefined)).toBe(false);
|
|
257
261
|
expect(isHitlReinvocation(new Map())).toBe(false);
|
|
@@ -264,12 +268,121 @@ describe("isHitlReinvocation (the single discriminator for message-accompanied p
|
|
|
264
268
|
});
|
|
265
269
|
});
|
|
266
270
|
|
|
271
|
+
describe("HITL recovery — fresh agent mid-HITL (issue #366)", () => {
|
|
272
|
+
const decisions = () =>
|
|
273
|
+
new Map<string, ApprovalAction>([["tool-call-1", ApprovalAction.APPROVE]]);
|
|
274
|
+
const approvals = () => [
|
|
275
|
+
create(PendingApprovalSchema, {
|
|
276
|
+
toolCallId: "tool-call-1",
|
|
277
|
+
toolName: "Write",
|
|
278
|
+
message: "Write file: gated.txt",
|
|
279
|
+
}),
|
|
280
|
+
];
|
|
281
|
+
const recoveryInput = (overrides: Partial<BuildPromptInput> = {}) =>
|
|
282
|
+
input({
|
|
283
|
+
resolution: resolution("local", "created_after_resume_failure"),
|
|
284
|
+
approvalDecisions: decisions(),
|
|
285
|
+
pendingApprovals: approvals(),
|
|
286
|
+
...overrides,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("keeps the resumed-HITL prompt byte-identical to the bare reinvocation prompt (regression guard)", () => {
|
|
290
|
+
const prompt = buildPrompt(
|
|
291
|
+
input({
|
|
292
|
+
resolution: resolution("local", "resumed_successfully"),
|
|
293
|
+
approvalDecisions: decisions(),
|
|
294
|
+
pendingApprovals: approvals(),
|
|
295
|
+
}),
|
|
296
|
+
);
|
|
297
|
+
expect(prompt).toBe(buildReinvocationPrompt(approvals(), decisions()));
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("rebuilds full context for a fresh agent mid-HITL: instructions, the ORIGINAL user message, the state-loss disclosure, and the decisions", () => {
|
|
301
|
+
const prompt = buildPrompt(recoveryInput());
|
|
302
|
+
// The pre-fix failure mode: bare decisions with no story.
|
|
303
|
+
expect(prompt).not.toBe(buildReinvocationPrompt(approvals(), decisions()));
|
|
304
|
+
expect(prompt).toContain("<agent_instructions>");
|
|
305
|
+
expect(prompt).toContain("<tool_approval_protocol>");
|
|
306
|
+
expect(prompt).toContain(`<user_request>\n${USER_MESSAGE}\n</user_request>`);
|
|
307
|
+
expect(prompt).toContain("<turn_recovery>");
|
|
308
|
+
expect(prompt).toContain("Write file: gated.txt");
|
|
309
|
+
expect(prompt).toContain("APPROVED");
|
|
310
|
+
// The opaque tool-call id must not leak into the prompt (reinvocation rule).
|
|
311
|
+
expect(prompt).not.toContain("tool-call-1");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("orders the recovery chronologically: request, then the recovered work, then the decisions, ending on the continue directive", () => {
|
|
315
|
+
const prompt = buildPrompt(
|
|
316
|
+
recoveryInput({ turnRecoveryDigest: "Tool: Write file: draft.txt — completed" }),
|
|
317
|
+
);
|
|
318
|
+
const requestIdx = prompt.indexOf("<user_request>");
|
|
319
|
+
const recoveryIdx = prompt.indexOf("<turn_recovery>");
|
|
320
|
+
const decisionsIdx = prompt.indexOf("APPROVED");
|
|
321
|
+
expect(requestIdx).toBeGreaterThan(-1);
|
|
322
|
+
expect(recoveryIdx).toBeGreaterThan(requestIdx);
|
|
323
|
+
expect(decisionsIdx).toBeGreaterThan(recoveryIdx);
|
|
324
|
+
expect(prompt).toContain("Tool: Write file: draft.txt — completed");
|
|
325
|
+
expect(prompt.trimEnd().endsWith("do not ask the user for permission in prose.")).toBe(true);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("discloses the state loss even with NO transcript — otherwise the decisions read as reactions to proposals this agent never made", () => {
|
|
329
|
+
const prompt = buildPrompt(recoveryInput({ turnRecoveryDigest: undefined }));
|
|
330
|
+
expect(prompt).toContain("<turn_recovery>");
|
|
331
|
+
expect(prompt).toContain("no transcript of your progress is available");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("preserves already-applied semantics: exact-applied writes are described as done, not as work to redo", () => {
|
|
335
|
+
const prompt = buildPrompt(
|
|
336
|
+
recoveryInput({ appliedToolCallIds: new Set(["tool-call-1"]) }),
|
|
337
|
+
);
|
|
338
|
+
expect(prompt).toContain("ALREADY applied");
|
|
339
|
+
expect(prompt).not.toContain("Carry them out now");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("carries the session-standing context a first turn would get — the replacement agent inherits the whole story, not just the blueprint", () => {
|
|
343
|
+
const prompt = buildPrompt(
|
|
344
|
+
recoveryInput({ contextBridge: "User: earlier thread\nAssistant: earlier reply" }),
|
|
345
|
+
);
|
|
346
|
+
expect(prompt).toContain("<previous_conversation_context>");
|
|
347
|
+
expect(prompt).toContain("User: earlier thread");
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("selects the recovery shape for ANY non-resumed reason — decisions-only is safe only when native context is guaranteed", () => {
|
|
351
|
+
// created_first_execution + decisions cannot happen in practice
|
|
352
|
+
// (decisions are reconstructed from a persisted transcript, which
|
|
353
|
+
// implies a prior invocation) — but if it ever did, the bare decisions
|
|
354
|
+
// prompt would reproduce the #366 amnesia. Fail safe.
|
|
355
|
+
const prompt = buildPrompt(
|
|
356
|
+
recoveryInput({ resolution: resolution("local", "created_first_execution") }),
|
|
357
|
+
);
|
|
358
|
+
expect(prompt).toContain("<turn_recovery>");
|
|
359
|
+
expect(prompt).toContain("<agent_instructions>");
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("primary send skips images ONLY for a resumed HITL agent (which holds them natively); every fresh agent gets the re-delivery", () => {
|
|
363
|
+
expect(primarySendCarriesImages(decisions(), "resumed_successfully")).toBe(false);
|
|
364
|
+
// The #366 vision corollary: fresh agent mid-HITL after a
|
|
365
|
+
// resolution-time resume failure.
|
|
366
|
+
expect(primarySendCarriesImages(decisions(), "created_after_resume_failure")).toBe(true);
|
|
367
|
+
// Non-HITL turns always carry the message's images, resumed or not.
|
|
368
|
+
expect(primarySendCarriesImages(undefined, "resumed_successfully")).toBe(true);
|
|
369
|
+
expect(primarySendCarriesImages(undefined, "created_first_execution")).toBe(true);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("the structured-output directive is a pure append shared by the primary and recovery sends — absent schema, absent suffix", () => {
|
|
373
|
+
expect(appendStructuredOutputDirective("base", undefined)).toBe("base");
|
|
374
|
+
const withSchema = appendStructuredOutputDirective("base", { type: "object" });
|
|
375
|
+
expect(withSchema.startsWith("base\n\n---\nCRITICAL OUTPUT REQUIREMENT:")).toBe(true);
|
|
376
|
+
expect(withSchema).toContain('"type": "object"');
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
267
380
|
describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)", () => {
|
|
268
381
|
const RESUMED = { resolution: resolution("local", "resumed_successfully") };
|
|
269
382
|
|
|
270
383
|
it("announces this turn's attachments to a resumed agent (per-execution value, never inherited)", () => {
|
|
271
384
|
const prompt = buildPrompt(
|
|
272
|
-
input({ ...RESUMED,
|
|
385
|
+
input({ ...RESUMED, attachments: [{ path: ".stigmer/inputs/lease.pdf" }] }),
|
|
273
386
|
);
|
|
274
387
|
expect(prompt).toContain("<input_files>");
|
|
275
388
|
expect(prompt).toContain("`.stigmer/inputs/lease.pdf`");
|
|
@@ -277,6 +390,23 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
|
|
|
277
390
|
expect(prompt.endsWith(USER_MESSAGE)).toBe(true);
|
|
278
391
|
});
|
|
279
392
|
|
|
393
|
+
it("discloses a duplicate-renamed attachment's original name (issue #364)", () => {
|
|
394
|
+
const prompt = buildPrompt(
|
|
395
|
+
input({
|
|
396
|
+
...RESUMED,
|
|
397
|
+
attachments: [
|
|
398
|
+
{ path: ".stigmer/inputs/report.pdf" },
|
|
399
|
+
{ path: ".stigmer/inputs/report-2.pdf", renamedFrom: "report.pdf" },
|
|
400
|
+
],
|
|
401
|
+
}),
|
|
402
|
+
);
|
|
403
|
+
expect(prompt).toContain(
|
|
404
|
+
"- `.stigmer/inputs/report-2.pdf` (renamed from duplicate 'report.pdf')",
|
|
405
|
+
);
|
|
406
|
+
// The first file keeps a clean entry — no disclosure noise.
|
|
407
|
+
expect(prompt).toContain("- `.stigmer/inputs/report.pdf`\n");
|
|
408
|
+
});
|
|
409
|
+
|
|
280
410
|
it("keeps a resumed turn WITHOUT attachments byte-identical to the raw message (regression guard)", () => {
|
|
281
411
|
const prompt = buildPrompt(input({ ...RESUMED }));
|
|
282
412
|
expect(prompt).toBe(USER_MESSAGE);
|
|
@@ -286,7 +416,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
|
|
|
286
416
|
const prompt = buildPrompt(
|
|
287
417
|
input({
|
|
288
418
|
...RESUMED,
|
|
289
|
-
|
|
419
|
+
attachments: [{ path: ".stigmer/inputs/photo.jpg" }],
|
|
290
420
|
conversationCatchup: "User also said hello on the channel.",
|
|
291
421
|
}),
|
|
292
422
|
);
|
|
@@ -300,7 +430,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
|
|
|
300
430
|
const prompt = buildPrompt(
|
|
301
431
|
input({
|
|
302
432
|
...RESUMED,
|
|
303
|
-
|
|
433
|
+
attachments: [{ path: ".stigmer/inputs/a.jpg" }, { path: ".stigmer/inputs/big.png" }],
|
|
304
434
|
vision: {
|
|
305
435
|
inlineFilenames: ["a.jpg"],
|
|
306
436
|
notViewable: [{ path: ".stigmer/inputs/big.png", reason: "too_large" }],
|
|
@@ -316,7 +446,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
|
|
|
316
446
|
const prompt = buildPrompt(
|
|
317
447
|
input({
|
|
318
448
|
resolution: resolution("local", "created_first_execution"),
|
|
319
|
-
|
|
449
|
+
attachments: [{ path: ".stigmer/inputs/a.jpg" }],
|
|
320
450
|
vision: { inlineFilenames: ["a.jpg"], notViewable: [] },
|
|
321
451
|
}),
|
|
322
452
|
);
|
|
@@ -336,7 +466,7 @@ describe("attachments on a resumed turn (T04 — the mid-session WhatsApp case)"
|
|
|
336
466
|
message: "Write file: gated.txt",
|
|
337
467
|
}),
|
|
338
468
|
],
|
|
339
|
-
|
|
469
|
+
attachments: [{ path: ".stigmer/inputs/photo.jpg" }],
|
|
340
470
|
vision: { inlineFilenames: ["photo.jpg"], notViewable: [] },
|
|
341
471
|
}),
|
|
342
472
|
);
|
|
@@ -543,7 +673,10 @@ describe("formatImplementPlanSection", () => {
|
|
|
543
673
|
const PLAN_PATH = ".stigmer/inputs/plan.md";
|
|
544
674
|
|
|
545
675
|
it("wraps the attached-plan directive when the plan is among the attachments", () => {
|
|
546
|
-
const section = formatImplementPlanSection(true, [
|
|
676
|
+
const section = formatImplementPlanSection(true, [
|
|
677
|
+
{ path: PLAN_PATH },
|
|
678
|
+
{ path: ".stigmer/inputs/data.csv" },
|
|
679
|
+
]);
|
|
547
680
|
|
|
548
681
|
expect(section).toBeDefined();
|
|
549
682
|
expect(section!.startsWith("<implement_plan>")).toBe(true);
|
|
@@ -553,7 +686,9 @@ describe("formatImplementPlanSection", () => {
|
|
|
553
686
|
});
|
|
554
687
|
|
|
555
688
|
it("falls back to the conversation-plan directive when no plan attachment resolved", () => {
|
|
556
|
-
const section = formatImplementPlanSection(true, [
|
|
689
|
+
const section = formatImplementPlanSection(true, [
|
|
690
|
+
{ path: ".stigmer/inputs/data.csv" },
|
|
691
|
+
]);
|
|
557
692
|
|
|
558
693
|
expect(section).toBeDefined();
|
|
559
694
|
expect(section).not.toContain("plan.md");
|
|
@@ -561,12 +696,12 @@ describe("formatImplementPlanSection", () => {
|
|
|
561
696
|
});
|
|
562
697
|
|
|
563
698
|
it("returns undefined for an ordinary (non-build) execution", () => {
|
|
564
|
-
expect(formatImplementPlanSection(false, [PLAN_PATH])).toBeUndefined();
|
|
565
|
-
expect(formatImplementPlanSection(undefined, [PLAN_PATH])).toBeUndefined();
|
|
699
|
+
expect(formatImplementPlanSection(false, [{ path: PLAN_PATH }])).toBeUndefined();
|
|
700
|
+
expect(formatImplementPlanSection(undefined, [{ path: PLAN_PATH }])).toBeUndefined();
|
|
566
701
|
});
|
|
567
702
|
|
|
568
703
|
it("carries the plan-derived progress-tracking instruction (Tier 3)", () => {
|
|
569
|
-
const section = formatImplementPlanSection(true, [PLAN_PATH]);
|
|
704
|
+
const section = formatImplementPlanSection(true, [{ path: PLAN_PATH }]);
|
|
570
705
|
|
|
571
706
|
expect(section).toContain("to-do list");
|
|
572
707
|
expect(section).toContain("break the plan into");
|
|
@@ -577,7 +712,7 @@ describe("formatImplementPlanSection", () => {
|
|
|
577
712
|
input({
|
|
578
713
|
resolution: resolution("local", "created_first_execution"),
|
|
579
714
|
buildFromPlan: true,
|
|
580
|
-
|
|
715
|
+
attachments: [{ path: PLAN_PATH }],
|
|
581
716
|
}),
|
|
582
717
|
);
|
|
583
718
|
|
|
@@ -593,7 +728,7 @@ describe("formatImplementPlanSection", () => {
|
|
|
593
728
|
input({
|
|
594
729
|
resolution: resolution("local", "resumed_successfully"),
|
|
595
730
|
buildFromPlan: true,
|
|
596
|
-
|
|
731
|
+
attachments: [{ path: PLAN_PATH }],
|
|
597
732
|
}),
|
|
598
733
|
);
|
|
599
734
|
|
|
@@ -611,7 +746,7 @@ describe("formatImplementPlanSection", () => {
|
|
|
611
746
|
input({
|
|
612
747
|
resolution: resolution("local", "resumed_successfully"),
|
|
613
748
|
buildFromPlan: false,
|
|
614
|
-
|
|
749
|
+
attachments: [{ path: PLAN_PATH }],
|
|
615
750
|
}),
|
|
616
751
|
);
|
|
617
752
|
|