@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
|
@@ -7,11 +7,15 @@ import type { WorkflowEventDescriptor } from "../../workflow-engine/types.js";
|
|
|
7
7
|
|
|
8
8
|
const mockGetEventLogHighWaterMark = vi.fn<(executionId: string) => Promise<bigint>>();
|
|
9
9
|
const mockGetWorkflowExecution = vi.fn<(executionId: string) => Promise<unknown>>();
|
|
10
|
+
const mockUpdateStatus = vi.fn<(input: unknown) => Promise<unknown>>();
|
|
10
11
|
|
|
11
12
|
vi.mock("../../client/stigmer-client.js", () => ({
|
|
12
13
|
StigmerClient: vi.fn().mockImplementation(() => ({
|
|
13
14
|
getEventLogHighWaterMark: (...args: unknown[]) => mockGetEventLogHighWaterMark(...(args as [string])),
|
|
14
15
|
getWorkflowExecution: (...args: unknown[]) => mockGetWorkflowExecution(...(args as [string])),
|
|
16
|
+
workflowExecutionCommand: {
|
|
17
|
+
updateStatus: (...args: unknown[]) => mockUpdateStatus(args[0]),
|
|
18
|
+
},
|
|
15
19
|
})),
|
|
16
20
|
}));
|
|
17
21
|
|
|
@@ -29,8 +33,9 @@ describe("initSequenceFromEventLog", () => {
|
|
|
29
33
|
mockGetEventLogHighWaterMark.mockReset();
|
|
30
34
|
});
|
|
31
35
|
|
|
32
|
-
it("
|
|
33
|
-
await initSequenceFromEventLog("");
|
|
36
|
+
it("returns 0 and resets the legacy counter when executionId is empty", async () => {
|
|
37
|
+
const highWaterMark = await initSequenceFromEventLog("");
|
|
38
|
+
expect(highWaterMark).toBe(0);
|
|
34
39
|
|
|
35
40
|
const evt = toProtoEvent({
|
|
36
41
|
type: "task_started",
|
|
@@ -43,10 +48,11 @@ describe("initSequenceFromEventLog", () => {
|
|
|
43
48
|
expect(mockGetEventLogHighWaterMark).not.toHaveBeenCalled();
|
|
44
49
|
});
|
|
45
50
|
|
|
46
|
-
it("
|
|
51
|
+
it("returns 0 when server has no events", async () => {
|
|
47
52
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(0));
|
|
48
53
|
|
|
49
|
-
await initSequenceFromEventLog("wfx_test-1");
|
|
54
|
+
const highWaterMark = await initSequenceFromEventLog("wfx_test-1");
|
|
55
|
+
expect(highWaterMark).toBe(0);
|
|
50
56
|
|
|
51
57
|
const evt = toProtoEvent({
|
|
52
58
|
type: "task_started",
|
|
@@ -58,7 +64,15 @@ describe("initSequenceFromEventLog", () => {
|
|
|
58
64
|
expect(evt.sequenceNumber).toBe(BigInt(1));
|
|
59
65
|
});
|
|
60
66
|
|
|
61
|
-
it("
|
|
67
|
+
it("returns the high-water mark as a plain number (Temporal payload converter cannot carry BigInt)", async () => {
|
|
68
|
+
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(42));
|
|
69
|
+
|
|
70
|
+
const highWaterMark = await initSequenceFromEventLog("wfx_recovery-1");
|
|
71
|
+
expect(highWaterMark).toBe(42);
|
|
72
|
+
expect(typeof highWaterMark).toBe("number");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("seeds the legacy counter so pre-patch replays continue from N+1", async () => {
|
|
62
76
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(42));
|
|
63
77
|
|
|
64
78
|
await initSequenceFromEventLog("wfx_recovery-1");
|
|
@@ -87,7 +101,8 @@ describe("initSequenceFromEventLog", () => {
|
|
|
87
101
|
it("handles large sequence numbers", async () => {
|
|
88
102
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(999999));
|
|
89
103
|
|
|
90
|
-
await initSequenceFromEventLog("wfx_large-seq");
|
|
104
|
+
const highWaterMark = await initSequenceFromEventLog("wfx_large-seq");
|
|
105
|
+
expect(highWaterMark).toBe(999999);
|
|
91
106
|
|
|
92
107
|
const evt = toProtoEvent({
|
|
93
108
|
type: "task_started",
|
|
@@ -112,7 +127,52 @@ describe("toProtoEvent", () => {
|
|
|
112
127
|
});
|
|
113
128
|
|
|
114
129
|
describe("sequence numbering", () => {
|
|
115
|
-
it("
|
|
130
|
+
it("honors the workflow-assigned sequenceNumber when stamped on the descriptor", () => {
|
|
131
|
+
const evt = toProtoEvent({
|
|
132
|
+
type: "task_started",
|
|
133
|
+
taskName: "t1",
|
|
134
|
+
occurredAt: NOW,
|
|
135
|
+
taskKind: "set",
|
|
136
|
+
attemptNumber: 1,
|
|
137
|
+
sequenceNumber: 77,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(evt.sequenceNumber).toBe(BigInt(77));
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("workflow-assigned sequences are stable across conversions (retry idempotency)", () => {
|
|
144
|
+
const desc: WorkflowEventDescriptor = {
|
|
145
|
+
type: "task_started",
|
|
146
|
+
taskName: "t1",
|
|
147
|
+
occurredAt: NOW,
|
|
148
|
+
taskKind: "set",
|
|
149
|
+
attemptNumber: 1,
|
|
150
|
+
sequenceNumber: 5,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Simulates an activity retry re-converting the same descriptors:
|
|
154
|
+
// the sequence must not change between attempts.
|
|
155
|
+
expect(toProtoEvent(desc).sequenceNumber).toBe(BigInt(5));
|
|
156
|
+
expect(toProtoEvent(desc).sequenceNumber).toBe(BigInt(5));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("a stamped descriptor does not consume the legacy counter", () => {
|
|
160
|
+
const unstamped: WorkflowEventDescriptor = {
|
|
161
|
+
type: "task_started",
|
|
162
|
+
taskName: "t",
|
|
163
|
+
occurredAt: NOW,
|
|
164
|
+
taskKind: "set",
|
|
165
|
+
attemptNumber: 1,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const e1 = toProtoEvent(unstamped);
|
|
169
|
+
toProtoEvent({ ...unstamped, sequenceNumber: 99 });
|
|
170
|
+
const e2 = toProtoEvent(unstamped);
|
|
171
|
+
|
|
172
|
+
expect(e2.sequenceNumber).toBe(e1.sequenceNumber + BigInt(1));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("legacy path: assigns monotonically increasing sequence numbers when unstamped", () => {
|
|
116
176
|
const desc: WorkflowEventDescriptor = {
|
|
117
177
|
type: "task_started",
|
|
118
178
|
taskName: "t1",
|
|
@@ -130,7 +190,7 @@ describe("toProtoEvent", () => {
|
|
|
130
190
|
expect(e3.sequenceNumber).toBe(BigInt(3));
|
|
131
191
|
});
|
|
132
192
|
|
|
133
|
-
it("resets to 1 after re-initializing with empty executionId", async () => {
|
|
193
|
+
it("legacy path: resets to 1 after re-initializing with empty executionId", async () => {
|
|
134
194
|
toProtoEvent({
|
|
135
195
|
type: "task_started",
|
|
136
196
|
taskName: "t",
|
|
@@ -654,8 +714,13 @@ describe("toProtoEvent", () => {
|
|
|
654
714
|
});
|
|
655
715
|
|
|
656
716
|
describe("emitWorkflowEvents", () => {
|
|
717
|
+
beforeEach(() => {
|
|
718
|
+
mockUpdateStatus.mockReset();
|
|
719
|
+
});
|
|
720
|
+
|
|
657
721
|
it("returns silently for empty events array", async () => {
|
|
658
722
|
await expect(emitWorkflowEvents("exec-1", [])).resolves.toBeUndefined();
|
|
723
|
+
expect(mockUpdateStatus).not.toHaveBeenCalled();
|
|
659
724
|
});
|
|
660
725
|
|
|
661
726
|
it("returns silently for empty executionId", async () => {
|
|
@@ -667,6 +732,40 @@ describe("emitWorkflowEvents", () => {
|
|
|
667
732
|
attemptNumber: 1,
|
|
668
733
|
}];
|
|
669
734
|
await expect(emitWorkflowEvents("", events)).resolves.toBeUndefined();
|
|
735
|
+
expect(mockUpdateStatus).not.toHaveBeenCalled();
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
it("sends workflow-assigned sequence numbers through to the RPC", async () => {
|
|
739
|
+
mockUpdateStatus.mockResolvedValue({});
|
|
740
|
+
|
|
741
|
+
await emitWorkflowEvents("exec-1", [{
|
|
742
|
+
type: "task_started",
|
|
743
|
+
taskName: "t",
|
|
744
|
+
occurredAt: NOW,
|
|
745
|
+
taskKind: "set",
|
|
746
|
+
attemptNumber: 1,
|
|
747
|
+
sequenceNumber: 12,
|
|
748
|
+
}]);
|
|
749
|
+
|
|
750
|
+
expect(mockUpdateStatus).toHaveBeenCalledTimes(1);
|
|
751
|
+
const input = mockUpdateStatus.mock.calls[0][0] as { events: { sequenceNumber: bigint }[] };
|
|
752
|
+
expect(input.events).toHaveLength(1);
|
|
753
|
+
expect(input.events[0].sequenceNumber).toBe(BigInt(12));
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it("propagates RPC errors so the local activity retry policy fires", async () => {
|
|
757
|
+
mockUpdateStatus.mockRejectedValue(new Error("server unavailable"));
|
|
758
|
+
|
|
759
|
+
const events: WorkflowEventDescriptor[] = [{
|
|
760
|
+
type: "task_started",
|
|
761
|
+
taskName: "t",
|
|
762
|
+
occurredAt: NOW,
|
|
763
|
+
taskKind: "set",
|
|
764
|
+
attemptNumber: 1,
|
|
765
|
+
sequenceNumber: 1,
|
|
766
|
+
}];
|
|
767
|
+
|
|
768
|
+
await expect(emitWorkflowEvents("exec-1", events)).rejects.toThrow("server unavailable");
|
|
670
769
|
});
|
|
671
770
|
});
|
|
672
771
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
import { computeLlmCostMicros, ensureLoaded as ensurePricingLoaded } from "../shared/model-pricing.js";
|
|
32
32
|
import { resolveToApiModelId } from "../shared/model-registry.js";
|
|
33
33
|
import { buildChatModel } from "../shared/model-client.js";
|
|
34
|
+
import { checkDirectCredentials } from "../shared/llm-backend.js";
|
|
34
35
|
import { classifyModelCallError } from "../shared/model-error.js";
|
|
35
36
|
|
|
36
37
|
export interface LlmCallConfig {
|
|
@@ -169,23 +170,15 @@ export async function callLlmAction(
|
|
|
169
170
|
`structured=${!!config.response_schema} execution=${executionId}`,
|
|
170
171
|
);
|
|
171
172
|
|
|
172
|
-
// Direct mode (no proxy) requires
|
|
173
|
-
//
|
|
174
|
-
//
|
|
173
|
+
// Direct mode (no proxy) requires a usable credential path — the provider's
|
|
174
|
+
// own API key, or a configured model backend that authenticates itself
|
|
175
|
+
// (vertex uses Google ADC; requiring an Anthropic key there would reject a
|
|
176
|
+
// correctly-configured deployment). Validated here rather than in
|
|
177
|
+
// buildChatModel so the shared module stays free of Temporal failure types.
|
|
175
178
|
if (!proxyActive) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
`Set the API key in your environment or connect to a Stigmer Cloud deployment.`,
|
|
180
|
-
"LLM_MISSING_API_KEY",
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
if (provider === "anthropic" && !process.env.ANTHROPIC_API_KEY) {
|
|
184
|
-
throw ApplicationFailure.nonRetryable(
|
|
185
|
-
`ANTHROPIC_API_KEY is not set and no proxy is configured. ` +
|
|
186
|
-
`Set the API key in your environment or connect to a Stigmer Cloud deployment.`,
|
|
187
|
-
"LLM_MISSING_API_KEY",
|
|
188
|
-
);
|
|
179
|
+
const missing = checkDirectCredentials(provider);
|
|
180
|
+
if (missing !== null) {
|
|
181
|
+
throw ApplicationFailure.nonRetryable(missing, "LLM_MISSING_API_KEY");
|
|
189
182
|
}
|
|
190
183
|
}
|
|
191
184
|
|
|
@@ -23,6 +23,8 @@ import { SystemMessage, HumanMessage } from "@langchain/core/messages";
|
|
|
23
23
|
import { activityStarted, activityFinished } from "../idle-watchdog.js";
|
|
24
24
|
import { getSummarizationModel } from "../shared/model-registry.js";
|
|
25
25
|
import { buildChatModel } from "../shared/model-client.js";
|
|
26
|
+
import { checkDirectCredentials } from "../shared/llm-backend.js";
|
|
27
|
+
import { tryInferProvider } from "../shared/llm-proxy.js";
|
|
26
28
|
import type { Config } from "../config.js";
|
|
27
29
|
|
|
28
30
|
const BATCH_SIZE = 40;
|
|
@@ -149,7 +151,8 @@ Output one classification per tool, maintaining the input order.`;
|
|
|
149
151
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
150
152
|
|
|
151
153
|
export interface ClassifyToolsOptions {
|
|
152
|
-
|
|
154
|
+
/** Null when the deployment has no proxy — the runner calls providers directly. */
|
|
155
|
+
proxyEndpoint: string | null;
|
|
153
156
|
stigmerToken: string | null;
|
|
154
157
|
primaryModel: string;
|
|
155
158
|
}
|
|
@@ -166,6 +169,26 @@ export async function classifyTools(
|
|
|
166
169
|
|
|
167
170
|
const model = await getSummarizationModel(options.primaryModel);
|
|
168
171
|
|
|
172
|
+
// Direct mode with no credential path fails closed up front, with one
|
|
173
|
+
// actionable message instead of a provider 401 per batch. The message
|
|
174
|
+
// names the economy model and its provider because classification's
|
|
175
|
+
// provider follows the economy-model resolution, not the operator's key:
|
|
176
|
+
// "OPENAI_API_KEY is missing" is baffling to an operator holding an
|
|
177
|
+
// Anthropic key unless the log says which model classification wanted.
|
|
178
|
+
if (!options.proxyEndpoint) {
|
|
179
|
+
const provider = tryInferProvider(model);
|
|
180
|
+
const missing = provider === null ? null : checkDirectCredentials(provider);
|
|
181
|
+
if (missing !== null) {
|
|
182
|
+
console.warn(
|
|
183
|
+
`[ClassifyToolApprovals] Cannot classify ${tools.length} tool(s) for ` +
|
|
184
|
+
`'${serverName}': the classifier model '${model}' (${provider}) has no ` +
|
|
185
|
+
`credential path — failing closed, every tool requires approval until ` +
|
|
186
|
+
`a reconnect re-classifies it. ${missing}`,
|
|
187
|
+
);
|
|
188
|
+
return fallbackApprovals(tools);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
169
192
|
const batches: ToolDescriptor[][] = [];
|
|
170
193
|
for (let i = 0; i < tools.length; i += BATCH_SIZE) {
|
|
171
194
|
batches.push(tools.slice(i, i + BATCH_SIZE));
|
|
@@ -236,7 +259,7 @@ interface ClassifyBatchParams {
|
|
|
236
259
|
serverName: string;
|
|
237
260
|
serverDescription: string;
|
|
238
261
|
model: string;
|
|
239
|
-
proxyEndpoint: string;
|
|
262
|
+
proxyEndpoint: string | null;
|
|
240
263
|
stigmerToken: string | null;
|
|
241
264
|
mcpServerId: string | null;
|
|
242
265
|
batchIdx: number;
|
|
@@ -256,7 +279,7 @@ async function classifyBatch(params: ClassifyBatchParams): Promise<ToolApprovalR
|
|
|
256
279
|
// primary this routes to Claude, not the hardcoded OpenAI path it used to.
|
|
257
280
|
const { model: llm } = await buildChatModel({
|
|
258
281
|
modelName: model,
|
|
259
|
-
proxyEndpoint,
|
|
282
|
+
proxyEndpoint: proxyEndpoint ?? undefined,
|
|
260
283
|
stigmerToken: stigmerToken ?? undefined,
|
|
261
284
|
headerScope: { mcpServerId: mcpServerId ?? undefined },
|
|
262
285
|
maxTokens,
|
|
@@ -388,8 +411,15 @@ export function createClassifyToolApprovalsActivities(config: Config) {
|
|
|
388
411
|
`[ClassifyToolApprovals] Activity started: ${input.tools.length} tools for '${input.serverName}'`,
|
|
389
412
|
);
|
|
390
413
|
|
|
414
|
+
// Only a real proxy is an LLM proxy. stigmerBackendEndpoint (the
|
|
415
|
+
// gRPC control plane) serves no /v1/proxy/llm route — coercing it
|
|
416
|
+
// into one here used to 404 every classification in unproxied OSS
|
|
417
|
+
// and silently disable the vertex backend for this activity. (The
|
|
418
|
+
// registry fetch legitimately falls back to the backend endpoint —
|
|
419
|
+
// see shared/registry-endpoint.ts — because the control plane DOES
|
|
420
|
+
// serve /v1/proxy/model-registry; that fallback does not port here.)
|
|
391
421
|
return await classifyTools(input, {
|
|
392
|
-
proxyEndpoint: config.proxyEndpoint
|
|
422
|
+
proxyEndpoint: config.proxyEndpoint,
|
|
393
423
|
stigmerToken: config.stigmerToken,
|
|
394
424
|
primaryModel: config.primaryModel,
|
|
395
425
|
});
|
|
@@ -22,7 +22,7 @@ import { createHash } from "node:crypto";
|
|
|
22
22
|
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
23
23
|
import { activityStarted, activityFinished } from "../idle-watchdog.js";
|
|
24
24
|
import { StigmerClient } from "../client/stigmer-client.js";
|
|
25
|
-
import { mcpServerToResolved } from "../shared/mcp-resolver.js";
|
|
25
|
+
import { mcpServerToResolved, type ResolvedMcpServer } from "../shared/mcp-resolver.js";
|
|
26
26
|
import { toMcpClientConfig } from "../shared/mcp-manager.js";
|
|
27
27
|
import {
|
|
28
28
|
assertTransportAllowed,
|
|
@@ -31,11 +31,44 @@ import {
|
|
|
31
31
|
} from "../shared/mcp-transport-guard.js";
|
|
32
32
|
import { detectOAuthChallenge } from "../shared/mcp-oauth-detect.js";
|
|
33
33
|
import { injectAnonymousCallerIdentityForDiscovery } from "../shared/caller-identity.js";
|
|
34
|
+
import { startHeartbeat } from "../shared/heartbeat.js";
|
|
34
35
|
import { withTimeout } from "../shared/with-timeout.js";
|
|
35
36
|
import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
37
|
+
import type { EnvVarDeclaration } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
|
|
36
38
|
import type { Config } from "../config.js";
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
/**
|
|
41
|
+
* MCP session-init bounds, per transport (issue #239).
|
|
42
|
+
*
|
|
43
|
+
* A remote endpoint that accepts a connection but never completes the MCP
|
|
44
|
+
* handshake would otherwise hang initialization forever: the MCP SDK's SSE
|
|
45
|
+
* transport resolves only on the server's `endpoint` event, with no timer of
|
|
46
|
+
* its own, and mcp-adapters silently falls back to SSE whenever the
|
|
47
|
+
* streamable-HTTP POST is answered with a 4xx (monday.com's endpoint did
|
|
48
|
+
* exactly this — 4xx on POST, then a silently-open SSE stream).
|
|
49
|
+
*
|
|
50
|
+
* HTTP endpoints get a short bound: there is nothing to install or compile,
|
|
51
|
+
* so a healthy endpoint completes the handshake in seconds — a short bound is
|
|
52
|
+
* what converts the silent hang into a fast, actionable failure (with room for
|
|
53
|
+
* the 10s OAuth re-probe on the failure path). stdio servers keep the generous
|
|
54
|
+
* bound because their first run may compile or install packages (`go run`,
|
|
55
|
+
* `npx`) — the cold-start case (issue #243). The OSS server's connect-workflow
|
|
56
|
+
* run timeout (connectTimeout, controller/connect.go) is derived from the
|
|
57
|
+
* stdio bound + the classification floor, so both errors stay reachable.
|
|
58
|
+
*/
|
|
59
|
+
const HTTP_INIT_TIMEOUT_MS = 30_000;
|
|
60
|
+
const STDIO_INIT_TIMEOUT_MS = 270_000;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The server-side secret-redaction sentinel. Byte-for-byte the value both
|
|
64
|
+
* editions substitute for secret values a caller may not decrypt
|
|
65
|
+
* (`SecretEncryptionService.REDACTED_MARKER` in stigmer-cloud,
|
|
66
|
+
* `RedactedMarker` in the OSS Go server). Discovery dialing an
|
|
67
|
+
* endpoint with a redacted credential is guaranteed to fail confusingly
|
|
68
|
+
* (e.g. `Authorization: Bearer ***REDACTED***` → 401 → SSE-fallback limbo),
|
|
69
|
+
* so it is refused up front with an actionable error instead.
|
|
70
|
+
*/
|
|
71
|
+
const REDACTED_MARKER = "***REDACTED***";
|
|
39
72
|
|
|
40
73
|
const PLATFORM_INJECTABLE_MAP: Record<string, string> = {
|
|
41
74
|
STIGMER_SERVER_ADDRESS: "STIGMER_MCP_PUBLIC_ENDPOINT",
|
|
@@ -48,6 +81,13 @@ const PLATFORM_INJECTABLE_MAP: Record<string, string> = {
|
|
|
48
81
|
export interface DiscoverMcpServerInput {
|
|
49
82
|
mcpServerId: string;
|
|
50
83
|
executionContextId?: string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Execution-scoped token unlocking the connect EC's decrypted credentials
|
|
86
|
+
* (oss#535). Minted by the OSS handler and carried with the work item —
|
|
87
|
+
* discovery has no execution of its own to exchange for one. Absent on
|
|
88
|
+
* cloud, where the ambient connect_sandbox credential decrypts.
|
|
89
|
+
*/
|
|
90
|
+
executionContextToken?: string | null;
|
|
51
91
|
invokerIdentityAccountId?: string | null;
|
|
52
92
|
}
|
|
53
93
|
|
|
@@ -95,6 +135,28 @@ export interface ToolApprovalDict {
|
|
|
95
135
|
message: string;
|
|
96
136
|
}
|
|
97
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Raised when the credentials a server declares could not be delivered to
|
|
140
|
+
* discovery — the ExecutionContext read failed, came back empty, or returned
|
|
141
|
+
* redacted values (issue #239).
|
|
142
|
+
*
|
|
143
|
+
* Exists because the alternative is strictly worse: proceeding without the
|
|
144
|
+
* declared credentials either dies later as an opaque
|
|
145
|
+
* PlaceholderResolutionError (when a header/arg templates the missing var) or
|
|
146
|
+
* dials the endpoint with a garbage credential and strands the connect in the
|
|
147
|
+
* 4xx → SSE-fallback limbo this file's init bounds exist to contain. Failing
|
|
148
|
+
* here names the ROOT cause — credential delivery — instead of its downstream
|
|
149
|
+
* symptom. The message is user-facing and self-contained: it survives the
|
|
150
|
+
* Temporal boundary and the Go/Java connect wrappers include it in the
|
|
151
|
+
* user-facing failure text.
|
|
152
|
+
*/
|
|
153
|
+
export class CredentialResolutionError extends Error {
|
|
154
|
+
constructor(message: string) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "CredentialResolutionError";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
98
160
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
161
|
// Tools Fingerprint (pure, deterministic, safe in workflow code)
|
|
100
162
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -225,7 +287,7 @@ export async function discoverMcpServer(
|
|
|
225
287
|
input: DiscoverMcpServerInput,
|
|
226
288
|
deps: DiscoverDeps,
|
|
227
289
|
): Promise<DiscoverMcpServerOutput> {
|
|
228
|
-
const { mcpServerId, executionContextId } = input;
|
|
290
|
+
const { mcpServerId, executionContextId, executionContextToken } = input;
|
|
229
291
|
const { stigmerClient } = deps;
|
|
230
292
|
|
|
231
293
|
console.log(
|
|
@@ -242,14 +304,16 @@ export async function discoverMcpServer(
|
|
|
242
304
|
const slug = mcpServer.metadata?.slug || mcpServerId;
|
|
243
305
|
const previousState = extractPreviousState(mcpServer);
|
|
244
306
|
|
|
307
|
+
const declaredEnv = mcpServer.spec.env ?? {};
|
|
245
308
|
const envVars = await resolveEnvVarsForDiscovery(
|
|
246
309
|
stigmerClient,
|
|
247
310
|
executionContextId ?? null,
|
|
311
|
+
executionContextToken ?? null,
|
|
312
|
+
slug,
|
|
313
|
+
declaredEnv,
|
|
248
314
|
);
|
|
249
315
|
|
|
250
|
-
const declaredEnvKeys =
|
|
251
|
-
? new Set(Object.keys(mcpServer.spec.env))
|
|
252
|
-
: new Set<string>();
|
|
316
|
+
const declaredEnvKeys = new Set(Object.keys(declaredEnv));
|
|
253
317
|
const platformEnv = injectPlatformEnv(declaredEnvKeys, envVars);
|
|
254
318
|
|
|
255
319
|
// Discovery runs with no session, so every declared caller-identity key
|
|
@@ -277,6 +341,7 @@ export async function discoverMcpServer(
|
|
|
277
341
|
const { tools, resourceTemplates } = await connectAndDiscover(
|
|
278
342
|
slug,
|
|
279
343
|
connectionConfig,
|
|
344
|
+
resolved,
|
|
280
345
|
);
|
|
281
346
|
|
|
282
347
|
const newFp = toolsFingerprint(tools);
|
|
@@ -300,51 +365,150 @@ export async function discoverMcpServer(
|
|
|
300
365
|
// Env Var Resolution
|
|
301
366
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
302
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Resolve the discovery env from the connect flow's ExecutionContext,
|
|
370
|
+
* failing CLOSED when a server's declared credentials cannot be delivered.
|
|
371
|
+
*
|
|
372
|
+
* The strictness is keyed on whether the server declares any NON-OPTIONAL
|
|
373
|
+
* env var ("credentials expected"):
|
|
374
|
+
*
|
|
375
|
+
* - Credentials expected + the EC read errors, or the EC is missing/empty →
|
|
376
|
+
* {@link CredentialResolutionError}. The backend only creates a connect EC
|
|
377
|
+
* when it resolved credentials to deliver, so an unreadable or empty EC is
|
|
378
|
+
* a delivery failure (auth/scope refusal, transient backend error), never a
|
|
379
|
+
* normal state. Limping ahead was issue #239's failure mode: discovery died
|
|
380
|
+
* later as an opaque PlaceholderResolutionError or a doomed dial.
|
|
381
|
+
* - Any delivered value equal to the redaction sentinel →
|
|
382
|
+
* {@link CredentialResolutionError}, regardless of optionality. A redacted
|
|
383
|
+
* value means the server-side decrypt gate refused THIS runner's credential
|
|
384
|
+
* class/scope and fell closed to redaction — dialing with the literal
|
|
385
|
+
* sentinel can only produce a misleading 401.
|
|
386
|
+
* - No non-optional declarations → the old lenient path (warn and continue):
|
|
387
|
+
* servers declaring nothing (or only optional/injected keys like the
|
|
388
|
+
* caller-identity family) legitimately discover without an EC.
|
|
389
|
+
*/
|
|
303
390
|
async function resolveEnvVarsForDiscovery(
|
|
304
391
|
client: StigmerClient,
|
|
305
392
|
executionContextId: string | null,
|
|
393
|
+
executionContextToken: string | null,
|
|
394
|
+
slug: string,
|
|
395
|
+
declaredEnv: Record<string, EnvVarDeclaration>,
|
|
306
396
|
): Promise<Record<string, string>> {
|
|
397
|
+
const requiredKeys = Object.entries(declaredEnv)
|
|
398
|
+
.filter(([, decl]) => !decl.optional)
|
|
399
|
+
.map(([key]) => key);
|
|
400
|
+
const credentialsExpected = requiredKeys.length > 0;
|
|
401
|
+
|
|
307
402
|
if (!executionContextId) return {};
|
|
308
403
|
|
|
404
|
+
let execCtx: Awaited<ReturnType<typeof client.getExecutionContextByExecutionId>>;
|
|
309
405
|
try {
|
|
310
|
-
|
|
406
|
+
// The payload-carried token authenticates the read on OSS (oss#535);
|
|
407
|
+
// undefined on cloud, where the ambient credential applies instead.
|
|
408
|
+
execCtx = await client.getExecutionContextByExecutionId(
|
|
311
409
|
executionContextId,
|
|
410
|
+
executionContextToken ?? undefined,
|
|
312
411
|
);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
`
|
|
412
|
+
} catch (err) {
|
|
413
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
414
|
+
if (credentialsExpected) {
|
|
415
|
+
throw new CredentialResolutionError(
|
|
416
|
+
`Could not resolve the credentials MCP server '${slug}' requires ` +
|
|
417
|
+
`(${requiredKeys.join(", ")}): the connect credential store was ` +
|
|
418
|
+
`unreadable (${cause}). This is a platform-side delivery failure, ` +
|
|
419
|
+
`not a problem with your credentials — retry the connect, and if it ` +
|
|
420
|
+
`persists, re-run the OAuth sign-in or re-enter the credentials.`,
|
|
318
421
|
);
|
|
319
|
-
return {};
|
|
320
422
|
}
|
|
423
|
+
console.warn(
|
|
424
|
+
`[DiscoverMcpServer] Failed to resolve ExecutionContext '${executionContextId}': ` +
|
|
425
|
+
`${cause}`,
|
|
426
|
+
);
|
|
427
|
+
return {};
|
|
428
|
+
}
|
|
321
429
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
430
|
+
const data = execCtx?.spec?.data ?? {};
|
|
431
|
+
if (Object.keys(data).length === 0) {
|
|
432
|
+
if (credentialsExpected) {
|
|
433
|
+
throw new CredentialResolutionError(
|
|
434
|
+
`MCP server '${slug}' requires ${requiredKeys.join(", ")}, but the ` +
|
|
435
|
+
`connect flow delivered no credentials. Re-run the OAuth sign-in ` +
|
|
436
|
+
`(or re-enter the credentials) and connect again.`,
|
|
437
|
+
);
|
|
325
438
|
}
|
|
326
|
-
|
|
327
|
-
console.log(
|
|
328
|
-
`[DiscoverMcpServer] Resolved ${Object.keys(envVars).length} env var(s) ` +
|
|
329
|
-
`from ExecutionContext '${executionContextId}'`,
|
|
330
|
-
);
|
|
331
|
-
return envVars;
|
|
332
|
-
} catch (err) {
|
|
333
439
|
console.warn(
|
|
334
|
-
`[DiscoverMcpServer]
|
|
335
|
-
|
|
440
|
+
`[DiscoverMcpServer] ExecutionContext '${executionContextId}' not found ` +
|
|
441
|
+
`or empty — MCP server may not require environment variables`,
|
|
336
442
|
);
|
|
337
443
|
return {};
|
|
338
444
|
}
|
|
445
|
+
|
|
446
|
+
const redactedKeys = Object.entries(data)
|
|
447
|
+
.filter(([, execValue]) => execValue.value === REDACTED_MARKER)
|
|
448
|
+
.map(([key]) => key);
|
|
449
|
+
if (redactedKeys.length > 0) {
|
|
450
|
+
throw new CredentialResolutionError(
|
|
451
|
+
`The credentials for MCP server '${slug}' were delivered redacted ` +
|
|
452
|
+
`(${redactedKeys.join(", ")}): the platform refused to decrypt them ` +
|
|
453
|
+
`for this runner. This is a platform-side authorization failure — ` +
|
|
454
|
+
`retry the connect, and report it if it persists.`,
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const envVars: Record<string, string> = {};
|
|
459
|
+
for (const [key, execValue] of Object.entries(data)) {
|
|
460
|
+
envVars[key] = execValue.value;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
console.log(
|
|
464
|
+
`[DiscoverMcpServer] Resolved ${Object.keys(envVars).length} env var(s) ` +
|
|
465
|
+
`from ExecutionContext '${executionContextId}'`,
|
|
466
|
+
);
|
|
467
|
+
return envVars;
|
|
339
468
|
}
|
|
340
469
|
|
|
341
470
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
342
471
|
// MCP Connection + Enumeration
|
|
343
472
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
344
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Pick the session-init bound for a resolved server's transport.
|
|
476
|
+
*
|
|
477
|
+
* Exported for tests; pure so the timeout choice (the load-bearing half of
|
|
478
|
+
* the issue-#239 fix) can be pinned without wiring a slow clock.
|
|
479
|
+
*/
|
|
480
|
+
export function initTimeoutMsFor(connectionType: ResolvedMcpServer["connectionType"]): number {
|
|
481
|
+
return connectionType === "stdio" ? STDIO_INIT_TIMEOUT_MS : HTTP_INIT_TIMEOUT_MS;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* The user-facing message for a session-init timeout. Names the endpoint (or
|
|
486
|
+
* command) so the failure is diagnosable from the message alone, and explains
|
|
487
|
+
* the known silent-hang shape for HTTP endpoints — issue #239's mechanism.
|
|
488
|
+
*/
|
|
489
|
+
export function initTimeoutMessageFor(slug: string, resolved: ResolvedMcpServer): string {
|
|
490
|
+
const seconds = Math.round(initTimeoutMsFor(resolved.connectionType) / 1000);
|
|
491
|
+
if (resolved.connectionType === "stdio") {
|
|
492
|
+
return (
|
|
493
|
+
`MCP server '${slug}' (command: ${resolved.command}) did not complete ` +
|
|
494
|
+
`MCP initialization within ${seconds}s. If this server requires ` +
|
|
495
|
+
`compilation or package installation on first run (e.g. go run, npx), ` +
|
|
496
|
+
`the cold start may have exceeded the discovery timeout.`
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return (
|
|
500
|
+
`MCP server '${slug}' at ${resolved.url} did not complete MCP ` +
|
|
501
|
+
`initialization within ${seconds}s. The endpoint accepted the connection ` +
|
|
502
|
+
`but never finished the handshake — commonly an endpoint that rejects ` +
|
|
503
|
+
`streamable HTTP while leaving its SSE fallback stream silently open. ` +
|
|
504
|
+
`Verify the URL points at a live streamable-HTTP MCP endpoint.`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
345
508
|
async function connectAndDiscover(
|
|
346
509
|
slug: string,
|
|
347
510
|
connectionConfig: ReturnType<typeof toMcpClientConfig>,
|
|
511
|
+
resolved: ResolvedMcpServer,
|
|
348
512
|
): Promise<{
|
|
349
513
|
tools: DiscoveredToolResult[];
|
|
350
514
|
resourceTemplates: DiscoveredResourceTemplateResult[];
|
|
@@ -354,12 +518,10 @@ async function connectAndDiscover(
|
|
|
354
518
|
const resourceTemplates: DiscoveredResourceTemplateResult[] = [];
|
|
355
519
|
|
|
356
520
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
`start may have exceeded the discovery timeout.`;
|
|
362
|
-
await withTimeout(SESSION_INIT_TIMEOUT_MS, timeoutMessage, async () => {
|
|
521
|
+
await withTimeout(
|
|
522
|
+
initTimeoutMsFor(resolved.connectionType),
|
|
523
|
+
() => initTimeoutMessageFor(slug, resolved),
|
|
524
|
+
async () => {
|
|
363
525
|
await client.initializeConnections();
|
|
364
526
|
|
|
365
527
|
const mcpClient = await client.getClient(slug);
|
|
@@ -461,12 +623,22 @@ export function createDiscoverMcpServerActivities(config: Config) {
|
|
|
461
623
|
input: DiscoverMcpServerInput,
|
|
462
624
|
): Promise<DiscoverMcpServerOutput> => {
|
|
463
625
|
activityStarted();
|
|
626
|
+
// The connect workflow proxies this activity with a 60s heartbeatTimeout,
|
|
627
|
+
// so it MUST heartbeat: before this loop existed, any discovery slower
|
|
628
|
+
// than 60s (stdio cold start — issue #243) or wedged on a silent remote
|
|
629
|
+
// (issue #239) was killed by Temporal with an opaque heartbeat timeout
|
|
630
|
+
// instead of reaching this file's actionable init-timeout errors.
|
|
631
|
+
const hb = startHeartbeat(15_000, () => ({
|
|
632
|
+
phase: "discovering_mcp_server",
|
|
633
|
+
mcpServerId: input.mcpServerId,
|
|
634
|
+
}));
|
|
464
635
|
try {
|
|
465
636
|
return await discoverMcpServer(input, {
|
|
466
637
|
stigmerClient,
|
|
467
638
|
transportPosture: resolveMcpTransportPosture(config.mode),
|
|
468
639
|
});
|
|
469
640
|
} finally {
|
|
641
|
+
hb.stop();
|
|
470
642
|
activityFinished();
|
|
471
643
|
}
|
|
472
644
|
},
|