@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
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter test for the foundry backend branch of buildChatModel — the
|
|
3
|
+
* complement of foundry-seam.test.ts, mirroring vertex-adapter.test.ts and
|
|
4
|
+
* bedrock-adapter.test.ts.
|
|
5
|
+
*
|
|
6
|
+
* The seam test pins the REAL AnthropicFoundry client's wire behavior under
|
|
7
|
+
* a hand-built ChatAnthropic. This file pins OUR production wiring: a real
|
|
8
|
+
* `buildChatModel` and a real `ChatAnthropic` drive a mocked
|
|
9
|
+
* `@anthropic-ai/foundry-sdk` (and a mocked `@azure/identity`), so
|
|
10
|
+
* LangChain's lazy `createClient` invocation path is exercised exactly as
|
|
11
|
+
* production does it. Together the two files cover the whole chain without
|
|
12
|
+
* any test-only injection seam in production code.
|
|
13
|
+
*
|
|
14
|
+
* Pinned here:
|
|
15
|
+
* - the factory forwards LangChain's `maxRetries: 0` to the client,
|
|
16
|
+
* - the factory forwards the request timeout (clientOptions.timeout ->
|
|
17
|
+
* factory options -> SDK constructor; how STIGMER_LLM_REQUEST_TIMEOUT_MS
|
|
18
|
+
* bounds this backend),
|
|
19
|
+
* - construction and invocation succeed with NO ANTHROPIC_API_KEY (Foundry
|
|
20
|
+
* auth is Azure's, and ChatAnthropic waives the key when createClient is
|
|
21
|
+
* provided),
|
|
22
|
+
* - the deployment name (strip-date rule, map overrides applied) crosses
|
|
23
|
+
* the wire while the returned apiModelId stays canonical (the
|
|
24
|
+
* canonical-id invariant),
|
|
25
|
+
* - auth selection: ANTHROPIC_FOUNDRY_API_KEY present -> no token provider
|
|
26
|
+
* and @azure/identity never touched; absent -> the Entra chain provider
|
|
27
|
+
* built with the Foundry scope and handed to the client,
|
|
28
|
+
* - maxTokens parity: stripping the date preserves LangChain's per-model
|
|
29
|
+
* default, so the adapter needs NO injection (unlike bedrock's 4096
|
|
30
|
+
* landmine) — pinned so a LangChain bump that diverges the two forms
|
|
31
|
+
* fails here instead of silently capping Foundry deployments,
|
|
32
|
+
* - usage_metadata survives the adapter (what billing reads).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
36
|
+
import { HumanMessage, AIMessage } from "@langchain/core/messages";
|
|
37
|
+
|
|
38
|
+
const { foundryCtorArgs, createRequests, identityCalls } = vi.hoisted(() => ({
|
|
39
|
+
foundryCtorArgs: [] as Array<{
|
|
40
|
+
maxRetries?: number;
|
|
41
|
+
timeout?: number;
|
|
42
|
+
azureADTokenProvider?: () => Promise<string>;
|
|
43
|
+
}>,
|
|
44
|
+
createRequests: [] as Array<Record<string, unknown>>,
|
|
45
|
+
identityCalls: {
|
|
46
|
+
credentialConstructions: 0,
|
|
47
|
+
tokenProviderScopes: [] as string[],
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
vi.mock("@anthropic-ai/foundry-sdk", () => ({
|
|
52
|
+
AnthropicFoundry: class {
|
|
53
|
+
readonly maxRetries: number | undefined;
|
|
54
|
+
readonly messages = {
|
|
55
|
+
create: async (request: Record<string, unknown>) => {
|
|
56
|
+
createRequests.push(request);
|
|
57
|
+
return {
|
|
58
|
+
id: "msg_foundry_adapter_01",
|
|
59
|
+
type: "message",
|
|
60
|
+
role: "assistant",
|
|
61
|
+
model: request.model,
|
|
62
|
+
content: [{ type: "text", text: "Namaste from Foundry." }],
|
|
63
|
+
stop_reason: "end_turn",
|
|
64
|
+
stop_sequence: null,
|
|
65
|
+
usage: { input_tokens: 7, output_tokens: 5 },
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
constructor(opts: {
|
|
71
|
+
maxRetries?: number;
|
|
72
|
+
timeout?: number;
|
|
73
|
+
azureADTokenProvider?: () => Promise<string>;
|
|
74
|
+
}) {
|
|
75
|
+
foundryCtorArgs.push(opts);
|
|
76
|
+
this.maxRetries = opts.maxRetries;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
vi.mock("@azure/identity", () => ({
|
|
82
|
+
DefaultAzureCredential: class {
|
|
83
|
+
constructor() {
|
|
84
|
+
identityCalls.credentialConstructions += 1;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
getBearerTokenProvider: (_credential: unknown, scope: string) => {
|
|
88
|
+
identityCalls.tokenProviderScopes.push(scope);
|
|
89
|
+
return async () => "entra-adapter-token";
|
|
90
|
+
},
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
import { ChatAnthropic } from "@langchain/anthropic";
|
|
94
|
+
|
|
95
|
+
import { buildChatModel } from "../model-client.js";
|
|
96
|
+
import { _resetRegistryCache } from "../model-registry.js";
|
|
97
|
+
|
|
98
|
+
describe("buildChatModel foundry adapter", () => {
|
|
99
|
+
const savedEnv = { ...process.env };
|
|
100
|
+
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
_resetRegistryCache();
|
|
103
|
+
foundryCtorArgs.length = 0;
|
|
104
|
+
createRequests.length = 0;
|
|
105
|
+
identityCalls.credentialConstructions = 0;
|
|
106
|
+
identityCalls.tokenProviderScopes.length = 0;
|
|
107
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
108
|
+
delete process.env.ANTHROPIC_FOUNDRY_BASE_URL;
|
|
109
|
+
delete process.env.STIGMER_FOUNDRY_DEPLOYMENT_MAP;
|
|
110
|
+
process.env.STIGMER_ANTHROPIC_BACKEND = "foundry";
|
|
111
|
+
process.env.ANTHROPIC_FOUNDRY_RESOURCE = "adapter-test-resource";
|
|
112
|
+
process.env.ANTHROPIC_FOUNDRY_API_KEY = "foundry-adapter-test-key";
|
|
113
|
+
// Registry offline → resolveToApiModelId passes the id through, keeping
|
|
114
|
+
// this test free of registry fixtures (that path has its own tests).
|
|
115
|
+
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("registry offline"));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
_resetRegistryCache();
|
|
120
|
+
vi.restoreAllMocks();
|
|
121
|
+
process.env = { ...savedEnv };
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("drives the mocked client through the real createClient path with the invariants intact", async () => {
|
|
125
|
+
const { model, apiModelId } = await buildChatModel({
|
|
126
|
+
modelName: "claude-sonnet-4-5-20250929",
|
|
127
|
+
maxTokens: 1024,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Canonical id returned; the client is not constructed yet (lazy factory).
|
|
131
|
+
expect(apiModelId).toBe("claude-sonnet-4-5-20250929");
|
|
132
|
+
expect(foundryCtorArgs).toHaveLength(0);
|
|
133
|
+
|
|
134
|
+
const result = await model.invoke([new HumanMessage("Weather in Chennai?")]);
|
|
135
|
+
|
|
136
|
+
// First request constructed exactly one (batch) client, with LangChain's
|
|
137
|
+
// maxRetries: 0 honored — LangChain owns retrying.
|
|
138
|
+
expect(foundryCtorArgs).toHaveLength(1);
|
|
139
|
+
expect(foundryCtorArgs[0].maxRetries).toBe(0);
|
|
140
|
+
|
|
141
|
+
// The wire carries the deployment name (date stripped); billing already
|
|
142
|
+
// got the canonical id above.
|
|
143
|
+
expect(createRequests).toHaveLength(1);
|
|
144
|
+
expect(createRequests[0].model).toBe("claude-sonnet-4-5");
|
|
145
|
+
expect(createRequests[0].max_tokens).toBe(1024);
|
|
146
|
+
|
|
147
|
+
// Response and usage flow back through LangChain untouched.
|
|
148
|
+
expect(result).toBeInstanceOf(AIMessage);
|
|
149
|
+
expect(result.text).toBe("Namaste from Foundry.");
|
|
150
|
+
expect((result as AIMessage).usage_metadata).toMatchObject({
|
|
151
|
+
input_tokens: 7,
|
|
152
|
+
output_tokens: 5,
|
|
153
|
+
total_tokens: 12,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("honors a deployment-map override over the strip-date rule", async () => {
|
|
158
|
+
process.env.STIGMER_FOUNDRY_DEPLOYMENT_MAP =
|
|
159
|
+
"claude-sonnet-4-6=my-sonnet-deployment";
|
|
160
|
+
|
|
161
|
+
const { model, apiModelId } = await buildChatModel({
|
|
162
|
+
modelName: "claude-sonnet-4-6",
|
|
163
|
+
maxTokens: 256,
|
|
164
|
+
});
|
|
165
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
166
|
+
|
|
167
|
+
expect(apiModelId).toBe("claude-sonnet-4-6");
|
|
168
|
+
expect(createRequests.at(-1)?.model).toBe("my-sonnet-deployment");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("uses API-key auth when ANTHROPIC_FOUNDRY_API_KEY is set — @azure/identity untouched", async () => {
|
|
172
|
+
const { model } = await buildChatModel({
|
|
173
|
+
modelName: "claude-sonnet-4-6",
|
|
174
|
+
maxTokens: 256,
|
|
175
|
+
});
|
|
176
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
177
|
+
|
|
178
|
+
// The SDK reads the key from its own env var; the adapter must not
|
|
179
|
+
// pass a token provider alongside it (the constructor treats them as
|
|
180
|
+
// mutually exclusive) and must not touch the Azure credential chain.
|
|
181
|
+
expect(foundryCtorArgs.at(-1)?.azureADTokenProvider).toBeUndefined();
|
|
182
|
+
expect(identityCalls.credentialConstructions).toBe(0);
|
|
183
|
+
expect(identityCalls.tokenProviderScopes).toHaveLength(0);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("falls back to the Entra chain with the Foundry scope when no key is set", async () => {
|
|
187
|
+
delete process.env.ANTHROPIC_FOUNDRY_API_KEY;
|
|
188
|
+
|
|
189
|
+
const { model } = await buildChatModel({
|
|
190
|
+
modelName: "claude-sonnet-4-6",
|
|
191
|
+
maxTokens: 256,
|
|
192
|
+
});
|
|
193
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
194
|
+
|
|
195
|
+
// One DefaultAzureCredential, one provider with the ai.azure.com scope,
|
|
196
|
+
// handed to the client so every request authenticates as Bearer.
|
|
197
|
+
expect(identityCalls.credentialConstructions).toBe(1);
|
|
198
|
+
expect(identityCalls.tokenProviderScopes).toEqual(["https://ai.azure.com/.default"]);
|
|
199
|
+
const provider = foundryCtorArgs.at(-1)?.azureADTokenProvider;
|
|
200
|
+
expect(provider).toBeTypeOf("function");
|
|
201
|
+
await expect(provider!()).resolves.toBe("entra-adapter-token");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("treats a whitespace-only key as absent (Entra, not a blank x-api-key)", async () => {
|
|
205
|
+
process.env.ANTHROPIC_FOUNDRY_API_KEY = " ";
|
|
206
|
+
|
|
207
|
+
const { model } = await buildChatModel({
|
|
208
|
+
modelName: "claude-sonnet-4-6",
|
|
209
|
+
maxTokens: 256,
|
|
210
|
+
});
|
|
211
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
212
|
+
|
|
213
|
+
expect(foundryCtorArgs.at(-1)?.azureADTokenProvider).toBeTypeOf("function");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("needs no maxTokens injection: the deployment name inherits the canonical default", async () => {
|
|
217
|
+
// The vertex-style parity pin (contrast with bedrock's 4096 landmine):
|
|
218
|
+
// LangChain's per-model default table prefix-matches the model string,
|
|
219
|
+
// and a date-stripped id is a prefix of its own canonical form, so both
|
|
220
|
+
// resolve identically — probed 2026-08-11 across the full catalog. If a
|
|
221
|
+
// LangChain bump ever adds date-qualified table entries that diverge
|
|
222
|
+
// the two forms, this fails and the adapter needs bedrock's
|
|
223
|
+
// canonical-probe injection.
|
|
224
|
+
const canonicalDefault = new ChatAnthropic({
|
|
225
|
+
model: "claude-sonnet-4-5-20250929",
|
|
226
|
+
apiKey: "probe",
|
|
227
|
+
}).maxTokens;
|
|
228
|
+
const deploymentDefault = new ChatAnthropic({
|
|
229
|
+
model: "claude-sonnet-4-5",
|
|
230
|
+
apiKey: "probe",
|
|
231
|
+
}).maxTokens;
|
|
232
|
+
expect(deploymentDefault).toBe(canonicalDefault);
|
|
233
|
+
|
|
234
|
+
const { model } = await buildChatModel({ modelName: "claude-sonnet-4-5-20250929" });
|
|
235
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
236
|
+
|
|
237
|
+
expect(createRequests.at(-1)?.max_tokens).toBe(canonicalDefault);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("forwards the request timeout to the SDK client (STIGMER_LLM_REQUEST_TIMEOUT_MS path)", async () => {
|
|
241
|
+
// The timeout rides clientOptions -> factory options -> SDK constructor.
|
|
242
|
+
// A factory that drops it silently unbounds the operator's timeout on
|
|
243
|
+
// this backend (the regression T06 repaired for the public path).
|
|
244
|
+
const { model } = await buildChatModel({
|
|
245
|
+
modelName: "claude-sonnet-4-6",
|
|
246
|
+
maxTokens: 256,
|
|
247
|
+
timeoutMs: 5000,
|
|
248
|
+
});
|
|
249
|
+
await model.invoke([new HumanMessage("hi")]);
|
|
250
|
+
|
|
251
|
+
expect(foundryCtorArgs).toHaveLength(1);
|
|
252
|
+
expect(foundryCtorArgs[0].timeout).toBe(5000);
|
|
253
|
+
expect(foundryCtorArgs[0].maxRetries).toBe(0);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("constructs and invokes with no ANTHROPIC_API_KEY anywhere in the environment", async () => {
|
|
257
|
+
expect(process.env.ANTHROPIC_API_KEY).toBeUndefined();
|
|
258
|
+
|
|
259
|
+
const { model } = await buildChatModel({
|
|
260
|
+
modelName: "claude-sonnet-4-6",
|
|
261
|
+
maxTokens: 256,
|
|
262
|
+
});
|
|
263
|
+
const result = await model.invoke([new HumanMessage("hi")]);
|
|
264
|
+
|
|
265
|
+
expect(result).toBeInstanceOf(AIMessage);
|
|
266
|
+
expect(createRequests.at(-1)?.model).toBe("claude-sonnet-4-6");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("fails at dispatch with the catalog message when no endpoint is configured", async () => {
|
|
270
|
+
delete process.env.ANTHROPIC_FOUNDRY_RESOURCE;
|
|
271
|
+
|
|
272
|
+
await expect(
|
|
273
|
+
buildChatModel({ modelName: "claude-sonnet-4-6", maxTokens: 256 }),
|
|
274
|
+
).rejects.toThrow(/ANTHROPIC_FOUNDRY_RESOURCE/);
|
|
275
|
+
});
|
|
276
|
+
});
|