@stigmer/runner 3.9.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -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 +32 -0
- package/dist/activities/discover-mcp-server.js +162 -27
- package/dist/activities/discover-mcp-server.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 +25 -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 +40 -3
- package/dist/activities/execute-cursor/error-classifier.js +81 -3
- 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 +5 -1
- package/dist/activities/execute-cursor/index.js +59 -58
- 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/prompt-builder.d.ts +18 -4
- package/dist/activities/execute-cursor/prompt-builder.js +12 -7
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- 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 -26
- package/dist/activities/execute-deep-agent/setup.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 +8 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +11 -4
- package/dist/activities/execute-deep-agent/subagent-wiring.js +13 -4
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/hydrate-workflow-execution.js +3 -1
- package/dist/activities/hydrate-workflow-execution.js.map +1 -1
- 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/claimcheck/payload-codec.js +21 -1
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +9 -4
- package/dist/client/stigmer-client.js +28 -15
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/encryption/config.d.ts +32 -0
- package/dist/encryption/config.js +68 -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/payload-codecs.d.ts +16 -0
- package/dist/payload-codecs.js +38 -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 +5 -15
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +5 -16
- 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/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/attachment-vision.d.ts +43 -2
- package/dist/shared/attachment-vision.js +72 -6
- package/dist/shared/attachment-vision.js.map +1 -1
- 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 +42 -1
- package/dist/shared/checkpointer/http-saver.js +96 -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/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/http-retry.d.ts +43 -0
- package/dist/shared/http-retry.js +50 -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 +17 -2
- package/dist/shared/mcp-manager.js +37 -25
- 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/model-client.d.ts +12 -5
- package/dist/shared/model-client.js +138 -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-registry.d.ts +20 -2
- package/dist/shared/model-registry.js +37 -2
- package/dist/shared/model-registry.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +26 -0
- package/dist/shared/plan-mode-permissions.js +28 -0
- package/dist/shared/plan-mode-permissions.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/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 +7 -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/package.json +8 -2
- package/src/__tests__/claimcheck-codec.test.ts +36 -0
- package/src/__tests__/encryption-codec.test.ts +234 -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 +50 -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 +203 -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 +190 -32
- 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 +92 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +40 -14
- 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__/prompt-builder-delegation.test.ts +1 -1
- 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 +38 -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 +91 -4
- 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 +63 -72
- package/src/activities/execute-cursor/mcp-resolver.ts +36 -2
- package/src/activities/execute-cursor/prompt-builder.ts +34 -9
- package/src/activities/execute-cursor/turn-stream.ts +5 -2
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +132 -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__/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__/subagent-plan-mode-permissions.test.ts +173 -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 +86 -31
- package/src/activities/execute-deep-agent/subagent-transformer.ts +23 -1
- package/src/activities/execute-deep-agent/subagent-wiring.ts +14 -4
- package/src/activities/hydrate-workflow-execution.ts +3 -1
- package/src/activities/workflow-event-activities.ts +96 -69
- package/src/claimcheck/payload-codec.ts +33 -1
- package/src/client/__tests__/stigmer-client.test.ts +8 -8
- package/src/client/stigmer-client.ts +32 -18
- package/src/encryption/config.ts +91 -0
- package/src/encryption/index.ts +3 -0
- package/src/encryption/payload-codec.ts +152 -0
- package/src/payload-codecs.ts +56 -0
- package/src/preflight.ts +45 -0
- package/src/runner-manager.ts +6 -24
- package/src/runner.ts +6 -25
- package/src/shared/__tests__/approval-policy.test.ts +82 -39
- package/src/shared/__tests__/attachment-naming.test.ts +159 -0
- package/src/shared/__tests__/attachment-vision.test.ts +97 -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__/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 +67 -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 +170 -2
- package/src/shared/__tests__/mcp-resolver.test.ts +146 -3
- package/src/shared/__tests__/model-client.test.ts +154 -0
- package/src/shared/__tests__/model-error.test.ts +289 -1
- package/src/shared/__tests__/model-registry.test.ts +71 -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/attachment-naming.ts +78 -0
- package/src/shared/attachment-vision.ts +92 -9
- 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 +117 -9
- package/src/shared/conversation-attachment.ts +1 -0
- package/src/shared/datastore-attachment.ts +106 -11
- package/src/shared/http-retry.ts +50 -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 +43 -26
- package/src/shared/mcp-resolver.ts +73 -2
- package/src/shared/model-client.ts +161 -19
- package/src/shared/model-error.ts +222 -4
- package/src/shared/model-registry.ts +50 -2
- package/src/shared/plan-mode-permissions.ts +30 -0
- package/src/worker.ts +4 -5
- package/src/workflow-engine/types.ts +18 -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 +7 -0
- package/src/workflows/engine-core.ts +23 -2
- package/src/workflows/execute-from-execution.ts +12 -2
|
@@ -10,6 +10,14 @@ vi.mock("../../idle-watchdog.js", () => ({
|
|
|
10
10
|
activityFinished: vi.fn(),
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
+
// The factory starts a Temporal heartbeat loop; outside an activity context
|
|
14
|
+
// its ticks would throw, so replace it with a stop-spy and assert the
|
|
15
|
+
// start/stop contract instead.
|
|
16
|
+
const mockHeartbeatStop = vi.fn();
|
|
17
|
+
vi.mock("../../shared/heartbeat.js", () => ({
|
|
18
|
+
startHeartbeat: vi.fn(() => ({ stop: mockHeartbeatStop, cancelled: false, workerShutdown: false })),
|
|
19
|
+
}));
|
|
20
|
+
|
|
13
21
|
const mockInitializeConnections = vi.fn();
|
|
14
22
|
const mockGetClient = vi.fn();
|
|
15
23
|
const mockClose = vi.fn().mockResolvedValue(undefined);
|
|
@@ -600,6 +608,122 @@ describe("DiscoverMcpServer activity", () => {
|
|
|
600
608
|
expect(result.newToolsFingerprint).toBe("");
|
|
601
609
|
});
|
|
602
610
|
|
|
611
|
+
it("fails closed when the EC read errors and the server requires credentials", async () => {
|
|
612
|
+
// Issue #239: limping ahead with an empty env either died later as an
|
|
613
|
+
// opaque PlaceholderResolutionError or dialed the endpoint with a
|
|
614
|
+
// garbage credential and wedged in the 4xx → SSE-fallback limbo. The
|
|
615
|
+
// failure must name the root cause: credential delivery.
|
|
616
|
+
const { discoverMcpServer, CredentialResolutionError } = await import("../discover-mcp-server.js");
|
|
617
|
+
|
|
618
|
+
const spec = makeHttpSpec("https://mcp.monday.com/mcp");
|
|
619
|
+
spec.env = { MONDAY_ACCESS_TOKEN: { isSecret: true } };
|
|
620
|
+
const mockClient = makeMockStigmerClient({
|
|
621
|
+
mcpServer: makeMcpServer({ metadata: { slug: "monday" }, spec }),
|
|
622
|
+
});
|
|
623
|
+
mockClient.getExecutionContextByExecutionId = vi
|
|
624
|
+
.fn()
|
|
625
|
+
.mockRejectedValue(new Error("PERMISSION_DENIED: not scope-bound"));
|
|
626
|
+
|
|
627
|
+
await expect(
|
|
628
|
+
discoverMcpServer(
|
|
629
|
+
{ mcpServerId: "mcp-monday", executionContextId: "ctx-1" },
|
|
630
|
+
{ stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
|
|
631
|
+
),
|
|
632
|
+
).rejects.toThrow(CredentialResolutionError);
|
|
633
|
+
expect(mockInitializeConnections).not.toHaveBeenCalled();
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("keeps the lenient path when the EC read errors but no env is declared", async () => {
|
|
637
|
+
const { discoverMcpServer } = await import("../discover-mcp-server.js");
|
|
638
|
+
|
|
639
|
+
const mockClient = makeMockStigmerClient({
|
|
640
|
+
mcpServer: makeMcpServer({
|
|
641
|
+
metadata: { slug: "no-env" },
|
|
642
|
+
spec: makeHttpSpec("https://mcp.example.com"),
|
|
643
|
+
}),
|
|
644
|
+
});
|
|
645
|
+
mockClient.getExecutionContextByExecutionId = vi
|
|
646
|
+
.fn()
|
|
647
|
+
.mockRejectedValue(new Error("transient"));
|
|
648
|
+
|
|
649
|
+
mockInitializeConnections.mockResolvedValue({});
|
|
650
|
+
mockGetClient.mockResolvedValue(makeMockMcpClient({ tools: [] }));
|
|
651
|
+
|
|
652
|
+
const result = await discoverMcpServer(
|
|
653
|
+
{ mcpServerId: "mcp-lenient", executionContextId: "ctx-2" },
|
|
654
|
+
{ stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
|
|
655
|
+
);
|
|
656
|
+
expect(result.tools).toEqual([]);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("keeps the lenient path when all declared env vars are optional", async () => {
|
|
660
|
+
// Caller-identity-style servers declare only optional keys; discovery
|
|
661
|
+
// legitimately proceeds without an EC for them (the injections below
|
|
662
|
+
// resolveEnvVarsForDiscovery supply the values).
|
|
663
|
+
const { discoverMcpServer } = await import("../discover-mcp-server.js");
|
|
664
|
+
|
|
665
|
+
const spec = makeHttpSpec("https://mcp.example.com");
|
|
666
|
+
spec.env = { STIGMER_CALLER_IDENTITY_KIND: { optional: true } };
|
|
667
|
+
const mockClient = makeMockStigmerClient({
|
|
668
|
+
mcpServer: makeMcpServer({ metadata: { slug: "optional-only" }, spec }),
|
|
669
|
+
});
|
|
670
|
+
mockClient.getExecutionContextByExecutionId = vi
|
|
671
|
+
.fn()
|
|
672
|
+
.mockRejectedValue(new Error("transient"));
|
|
673
|
+
|
|
674
|
+
mockInitializeConnections.mockResolvedValue({});
|
|
675
|
+
mockGetClient.mockResolvedValue(makeMockMcpClient({ tools: [] }));
|
|
676
|
+
|
|
677
|
+
const result = await discoverMcpServer(
|
|
678
|
+
{ mcpServerId: "mcp-optional", executionContextId: "ctx-3" },
|
|
679
|
+
{ stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
|
|
680
|
+
);
|
|
681
|
+
expect(result.tools).toEqual([]);
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it("fails closed when the EC is empty and the server requires credentials", async () => {
|
|
685
|
+
const { discoverMcpServer, CredentialResolutionError } = await import("../discover-mcp-server.js");
|
|
686
|
+
|
|
687
|
+
const spec = makeHttpSpec("https://mcp.monday.com/mcp");
|
|
688
|
+
spec.env = { MONDAY_ACCESS_TOKEN: { isSecret: true } };
|
|
689
|
+
const mockClient = makeMockStigmerClient({
|
|
690
|
+
mcpServer: makeMcpServer({ metadata: { slug: "monday" }, spec }),
|
|
691
|
+
executionContext: { spec: { data: {} } },
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const promise = discoverMcpServer(
|
|
695
|
+
{ mcpServerId: "mcp-monday", executionContextId: "ctx-4" },
|
|
696
|
+
{ stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
|
|
697
|
+
);
|
|
698
|
+
await expect(promise).rejects.toThrow(CredentialResolutionError);
|
|
699
|
+
await expect(promise).rejects.toThrow(/delivered no credentials/);
|
|
700
|
+
expect(mockInitializeConnections).not.toHaveBeenCalled();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("fails closed when a delivered credential is the redaction sentinel", async () => {
|
|
704
|
+
// A redacted value means the server-side decrypt gate refused this
|
|
705
|
+
// runner's credential class/scope — dialing with the literal sentinel
|
|
706
|
+
// can only produce a misleading 401 (the issue-#239 M2 mechanism).
|
|
707
|
+
const { discoverMcpServer, CredentialResolutionError } = await import("../discover-mcp-server.js");
|
|
708
|
+
|
|
709
|
+
const spec = makeHttpSpec("https://mcp.monday.com/mcp");
|
|
710
|
+
spec.env = { MONDAY_ACCESS_TOKEN: { isSecret: true } };
|
|
711
|
+
const mockClient = makeMockStigmerClient({
|
|
712
|
+
mcpServer: makeMcpServer({ metadata: { slug: "monday" }, spec }),
|
|
713
|
+
executionContext: {
|
|
714
|
+
spec: { data: { MONDAY_ACCESS_TOKEN: { value: "***REDACTED***", isSecret: true } } },
|
|
715
|
+
},
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
const promise = discoverMcpServer(
|
|
719
|
+
{ mcpServerId: "mcp-monday", executionContextId: "ctx-5" },
|
|
720
|
+
{ stigmerClient: mockClient as any, transportPosture: "stdio-forbidden" },
|
|
721
|
+
);
|
|
722
|
+
await expect(promise).rejects.toThrow(CredentialResolutionError);
|
|
723
|
+
await expect(promise).rejects.toThrow(/MONDAY_ACCESS_TOKEN.*delivered redacted|delivered redacted.*MONDAY_ACCESS_TOKEN/s);
|
|
724
|
+
expect(mockInitializeConnections).not.toHaveBeenCalled();
|
|
725
|
+
});
|
|
726
|
+
|
|
603
727
|
it("closes MCP client even when discovery fails", async () => {
|
|
604
728
|
const { discoverMcpServer } = await import("../discover-mcp-server.js");
|
|
605
729
|
|
|
@@ -620,6 +744,85 @@ describe("DiscoverMcpServer activity", () => {
|
|
|
620
744
|
});
|
|
621
745
|
});
|
|
622
746
|
|
|
747
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
748
|
+
// Transport-aware init bounds (issue #239)
|
|
749
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
750
|
+
|
|
751
|
+
describe("transport-aware init bounds", () => {
|
|
752
|
+
it("bounds HTTP endpoints at 30s and stdio at 270s", async () => {
|
|
753
|
+
// HTTP has no cold-start excuse: a healthy endpoint completes the MCP
|
|
754
|
+
// handshake in seconds, so a short bound converts the silent-SSE hang
|
|
755
|
+
// into a fast actionable failure. stdio keeps the cold-start allowance
|
|
756
|
+
// (issue #243). The OSS server's connect-workflow budget (connectTimeout,
|
|
757
|
+
// controller/connect.go — pinned there) is derived from the stdio bound
|
|
758
|
+
// + the classification floor, so both bounds stay reachable under it.
|
|
759
|
+
const { initTimeoutMsFor } = await import("../discover-mcp-server.js");
|
|
760
|
+
expect(initTimeoutMsFor("http")).toBe(30_000);
|
|
761
|
+
expect(initTimeoutMsFor("sse")).toBe(30_000);
|
|
762
|
+
expect(initTimeoutMsFor("stdio")).toBe(270_000);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("names the endpoint URL in the HTTP timeout message", async () => {
|
|
766
|
+
const { initTimeoutMessageFor } = await import("../discover-mcp-server.js");
|
|
767
|
+
const message = initTimeoutMessageFor("monday", {
|
|
768
|
+
slug: "monday",
|
|
769
|
+
connectionType: "http",
|
|
770
|
+
url: "https://mcp.monday.com/mcp",
|
|
771
|
+
toolApprovals: [],
|
|
772
|
+
pinnedToolApprovals: [],
|
|
773
|
+
toolApprovalOverrides: [],
|
|
774
|
+
discoveredCapabilitiesEmpty: true,
|
|
775
|
+
});
|
|
776
|
+
expect(message).toContain("https://mcp.monday.com/mcp");
|
|
777
|
+
expect(message).toContain("30s");
|
|
778
|
+
expect(message).toContain("SSE fallback");
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it("names the command in the stdio timeout message", async () => {
|
|
782
|
+
const { initTimeoutMessageFor } = await import("../discover-mcp-server.js");
|
|
783
|
+
const message = initTimeoutMessageFor("filesystem", {
|
|
784
|
+
slug: "filesystem",
|
|
785
|
+
connectionType: "stdio",
|
|
786
|
+
command: "npx",
|
|
787
|
+
toolApprovals: [],
|
|
788
|
+
pinnedToolApprovals: [],
|
|
789
|
+
toolApprovalOverrides: [],
|
|
790
|
+
discoveredCapabilitiesEmpty: true,
|
|
791
|
+
});
|
|
792
|
+
expect(message).toContain("npx");
|
|
793
|
+
expect(message).toContain("270s");
|
|
794
|
+
expect(message).toContain("cold start");
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
799
|
+
// Heartbeat contract (issue #239)
|
|
800
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
801
|
+
|
|
802
|
+
describe("heartbeat contract", () => {
|
|
803
|
+
it("starts a heartbeat for the activity and stops it on completion", async () => {
|
|
804
|
+
// The connect workflow proxies this activity with a 60s heartbeatTimeout.
|
|
805
|
+
// Without a heartbeat loop, any discovery slower than 60s was killed by
|
|
806
|
+
// Temporal with an opaque heartbeat timeout — never reaching the
|
|
807
|
+
// actionable init-timeout errors (issues #239/#243).
|
|
808
|
+
const { createDiscoverMcpServerActivities } = await import("../discover-mcp-server.js");
|
|
809
|
+
const { startHeartbeat } = await import("../../shared/heartbeat.js");
|
|
810
|
+
|
|
811
|
+
vi.mocked(startHeartbeat).mockClear();
|
|
812
|
+
mockHeartbeatStop.mockClear();
|
|
813
|
+
|
|
814
|
+
const { DiscoverMcpServerCapabilities } = createDiscoverMcpServerActivities(makeConfig());
|
|
815
|
+
try {
|
|
816
|
+
await DiscoverMcpServerCapabilities({ mcpServerId: "test" });
|
|
817
|
+
} catch {
|
|
818
|
+
// Expected — no real backend behind the factory's own client.
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
expect(startHeartbeat).toHaveBeenCalledTimes(1);
|
|
822
|
+
expect(mockHeartbeatStop).toHaveBeenCalledTimes(1);
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
|
|
623
826
|
// ─────────────────────────────────────────────────────────────────────────
|
|
624
827
|
// Idle Watchdog Integration
|
|
625
828
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -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
|
});
|