@deepstrike/sdk 0.2.52 → 0.2.61

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.
Files changed (151) hide show
  1. package/README.md +28 -28
  2. package/dist/agent-ir.d.ts +103 -0
  3. package/dist/agent-ir.js +134 -0
  4. package/dist/agent.d.ts +67 -0
  5. package/dist/agent.js +36 -0
  6. package/dist/collaboration/harness.js +1 -1
  7. package/dist/collaboration/modes/creator-verifier.d.ts +2 -7
  8. package/dist/collaboration/modes/creator-verifier.js +4 -6
  9. package/dist/collaboration/pool.d.ts +8 -20
  10. package/dist/collaboration/pool.js +27 -97
  11. package/dist/compat/anthropic/mcp.d.ts +15 -0
  12. package/dist/compat/anthropic/mcp.js +10 -0
  13. package/dist/compat/openai/agent.d.ts +34 -0
  14. package/dist/compat/openai/agent.js +24 -0
  15. package/dist/governance.d.ts +1 -17
  16. package/dist/governance.js +1 -34
  17. package/dist/guardrail.d.ts +6 -0
  18. package/dist/guardrail.js +1 -0
  19. package/dist/handoff-target.d.ts +12 -0
  20. package/dist/handoff-target.js +1 -0
  21. package/dist/harness/manifest.js +0 -4
  22. package/dist/index.d.ts +19 -7
  23. package/dist/index.js +8 -5
  24. package/dist/kernel.d.ts +2 -20
  25. package/dist/knowledge/public.d.ts +29 -0
  26. package/dist/knowledge/public.js +1 -0
  27. package/dist/mcp-server.d.ts +28 -0
  28. package/dist/mcp-server.js +1 -0
  29. package/dist/memory/agent.d.ts +2 -2
  30. package/dist/memory/agent.js +2 -2
  31. package/dist/memory/durable.d.ts +16 -0
  32. package/dist/memory/durable.js +46 -0
  33. package/dist/memory/in-memory-store.d.ts +9 -7
  34. package/dist/memory/in-memory-store.js +8 -2
  35. package/dist/memory/protocols.d.ts +22 -4
  36. package/dist/memory/public.d.ts +4 -3
  37. package/dist/memory/public.js +3 -2
  38. package/dist/os/public.d.ts +1 -1
  39. package/dist/os/public.js +1 -1
  40. package/dist/providers/anthropic-adapter.d.ts +59 -0
  41. package/dist/providers/anthropic-adapter.js +530 -0
  42. package/dist/providers/anthropic-compatible.d.ts +2 -3
  43. package/dist/providers/anthropic-compatible.js +8 -5
  44. package/dist/providers/anthropic.d.ts +20 -23
  45. package/dist/providers/anthropic.js +176 -395
  46. package/dist/providers/base.d.ts +2 -2
  47. package/dist/providers/base.js +50 -8
  48. package/dist/providers/capability-router.d.ts +29 -0
  49. package/dist/providers/capability-router.js +43 -0
  50. package/dist/providers/catalog.d.ts +16 -4
  51. package/dist/providers/catalog.js +112 -36
  52. package/dist/providers/content-normalization.d.ts +57 -0
  53. package/dist/providers/content-normalization.js +238 -0
  54. package/dist/providers/content-policy.d.ts +16 -0
  55. package/dist/providers/content-policy.js +39 -0
  56. package/dist/providers/credentials.d.ts +83 -0
  57. package/dist/providers/credentials.js +190 -0
  58. package/dist/providers/endpoints.d.ts +137 -0
  59. package/dist/providers/endpoints.js +128 -0
  60. package/dist/providers/factories.js +25 -9
  61. package/dist/providers/gemini-adapter.d.ts +33 -0
  62. package/dist/providers/gemini-adapter.js +264 -0
  63. package/dist/providers/gemini.d.ts +16 -3
  64. package/dist/providers/gemini.js +97 -195
  65. package/dist/providers/model-catalog.d.ts +37 -0
  66. package/dist/providers/model-catalog.js +62 -0
  67. package/dist/providers/model-registry.d.ts +119 -0
  68. package/dist/providers/model-registry.js +379 -0
  69. package/dist/providers/ollama-adapter.d.ts +65 -0
  70. package/dist/providers/ollama-adapter.js +188 -0
  71. package/dist/providers/ollama.d.ts +9 -4
  72. package/dist/providers/ollama.js +96 -109
  73. package/dist/providers/openai-chat-dialects.d.ts +154 -0
  74. package/dist/providers/openai-chat-dialects.js +179 -0
  75. package/dist/providers/openai-chat.d.ts +46 -18
  76. package/dist/providers/openai-chat.js +416 -51
  77. package/dist/providers/openai-responses-adapter.d.ts +42 -0
  78. package/dist/providers/openai-responses-adapter.js +343 -0
  79. package/dist/providers/openai-responses.d.ts +19 -33
  80. package/dist/providers/openai-responses.js +164 -264
  81. package/dist/providers/openai.d.ts +29 -76
  82. package/dist/providers/openai.js +195 -292
  83. package/dist/providers/protocol-adapter.d.ts +39 -0
  84. package/dist/providers/protocol-adapter.js +13 -0
  85. package/dist/providers/protocol-capabilities.d.ts +34 -0
  86. package/dist/providers/protocol-capabilities.js +44 -0
  87. package/dist/providers/provider-error.d.ts +31 -0
  88. package/dist/providers/provider-error.js +153 -0
  89. package/dist/providers/public.d.ts +26 -3
  90. package/dist/providers/public.js +13 -1
  91. package/dist/providers/registry.d.ts +7 -6
  92. package/dist/providers/registry.js +47 -20
  93. package/dist/providers/request-plan.d.ts +89 -0
  94. package/dist/providers/request-plan.js +199 -0
  95. package/dist/providers/usage-normalizer.d.ts +48 -0
  96. package/dist/providers/usage-normalizer.js +139 -0
  97. package/dist/providers/vendor-profiles.d.ts +2 -15
  98. package/dist/providers/vendor-profiles.js +14 -60
  99. package/dist/runtime/canonical-kernel-step.d.ts +1 -2
  100. package/dist/runtime/canonical-kernel-step.js +47 -12
  101. package/dist/runtime/context-policy.d.ts +10 -12
  102. package/dist/runtime/context-policy.js +6 -8
  103. package/dist/runtime/durable-content.d.ts +50 -0
  104. package/dist/runtime/durable-content.js +159 -0
  105. package/dist/runtime/execution-plane.d.ts +2 -2
  106. package/dist/runtime/execution-plane.js +2 -2
  107. package/dist/runtime/kernel-event-log.js +0 -1
  108. package/dist/runtime/kernel-step.d.ts +0 -1
  109. package/dist/runtime/kernel-step.js +4 -2
  110. package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
  111. package/dist/runtime/mcp-proxy-plane.js +44 -6
  112. package/dist/runtime/output-schema.d.ts +1 -2
  113. package/dist/runtime/provider-replay.d.ts +5 -1
  114. package/dist/runtime/provider-replay.js +26 -27
  115. package/dist/runtime/reactive-session.d.ts +1 -1
  116. package/dist/runtime/reactive-session.js +2 -3
  117. package/dist/runtime/run-group.d.ts +1 -1
  118. package/dist/runtime/runner.d.ts +31 -45
  119. package/dist/runtime/runner.js +178 -63
  120. package/dist/runtime/session-log.d.ts +8 -1
  121. package/dist/runtime/session-log.js +42 -2
  122. package/dist/runtime/session-repair.d.ts +1 -1
  123. package/dist/runtime/session-repair.js +1 -1
  124. package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
  125. package/dist/runtime/sub-agent-orchestrator.js +8 -11
  126. package/dist/runtime/workflow-control-flow.d.ts +0 -4
  127. package/dist/runtime/workflow-control-flow.js +0 -16
  128. package/dist/session.d.ts +11 -0
  129. package/dist/session.js +1 -0
  130. package/dist/skill.d.ts +17 -0
  131. package/dist/skill.js +16 -0
  132. package/dist/skills/loader.d.ts +3 -0
  133. package/dist/tools/errors.d.ts +1 -3
  134. package/dist/tools/errors.js +1 -3
  135. package/dist/tools/index.d.ts +3 -0
  136. package/dist/types/agent.d.ts +21 -9
  137. package/dist/types/agent.js +30 -4
  138. package/dist/types.d.ts +135 -17
  139. package/package.json +4 -4
  140. package/dist/providers/deepseek.d.ts +0 -46
  141. package/dist/providers/deepseek.js +0 -97
  142. package/dist/providers/glm.d.ts +0 -25
  143. package/dist/providers/glm.js +0 -48
  144. package/dist/providers/kimi.d.ts +0 -23
  145. package/dist/providers/kimi.js +0 -30
  146. package/dist/providers/minimax.d.ts +0 -49
  147. package/dist/providers/minimax.js +0 -98
  148. package/dist/providers/profiles.d.ts +0 -1992
  149. package/dist/providers/profiles.js +0 -796
  150. package/dist/providers/qwen.d.ts +0 -38
  151. package/dist/providers/qwen.js +0 -97
@@ -18,8 +18,8 @@ export declare const INTERNAL_EXTENSION_KEYS: readonly string[];
18
18
  export declare function omitExtensionKeys(extensions: Record<string, unknown> | undefined, keys: readonly string[]): Record<string, unknown>;
19
19
  /**
20
20
  * Cached-prompt-token count from an OpenAI-compatible usage object. Covers the
21
- * standard `prompt_tokens_details.cached_tokens` (OpenAI, Qwen, MiniMax, GLM,
22
- * Kimi) and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
21
+ * Chat `prompt_tokens_details.cached_tokens`, Responses
22
+ * `input_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
23
23
  * so there is no separate cache-creation count. The figure is a subset of
24
24
  * `prompt_tokens` (the full prompt), surfaced for cost visibility — it must not
25
25
  * be subtracted from the input count the kernel uses for context accounting.
@@ -1,3 +1,4 @@
1
+ import { normalizeToolResultPart, projectToolOutputToText } from "./content-normalization.js";
1
2
  export class CircuitBreaker {
2
3
  openAfter;
3
4
  resetAfter;
@@ -43,8 +44,8 @@ export function omitExtensionKeys(extensions, keys) {
43
44
  }
44
45
  /**
45
46
  * Cached-prompt-token count from an OpenAI-compatible usage object. Covers the
46
- * standard `prompt_tokens_details.cached_tokens` (OpenAI, Qwen, MiniMax, GLM,
47
- * Kimi) and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
47
+ * Chat `prompt_tokens_details.cached_tokens`, Responses
48
+ * `input_tokens_details.cached_tokens`, and DeepSeek's `prompt_cache_hit_tokens`. These caches bill reads only,
48
49
  * so there is no separate cache-creation count. The figure is a subset of
49
50
  * `prompt_tokens` (the full prompt), surfaced for cost visibility — it must not
50
51
  * be subtracted from the input count the kernel uses for context accounting.
@@ -53,10 +54,12 @@ export function openAICachedPromptTokens(usage) {
53
54
  if (!usage || typeof usage !== "object")
54
55
  return 0;
55
56
  const u = usage;
56
- const details = u.prompt_tokens_details;
57
- const standard = typeof details?.cached_tokens === "number" ? details.cached_tokens : 0;
57
+ const promptDetails = u.prompt_tokens_details;
58
+ const inputDetails = u.input_tokens_details;
59
+ const standard = typeof promptDetails?.cached_tokens === "number" ? promptDetails.cached_tokens : 0;
60
+ const responses = typeof inputDetails?.cached_tokens === "number" ? inputDetails.cached_tokens : 0;
58
61
  const deepseek = typeof u.prompt_cache_hit_tokens === "number" ? u.prompt_cache_hit_tokens : 0;
59
- return Math.max(standard, deepseek);
62
+ return Math.max(standard, responses, deepseek);
60
63
  }
61
64
  /**
62
65
  * Prompt-cache hit rate for one usage record: the fraction of the full prompt
@@ -140,11 +143,40 @@ export function toAnthropicContent(msg) {
140
143
  throw new UnsupportedModalityError("audio", "anthropic");
141
144
  }
142
145
  if (p.type === "tool_result") {
143
- return { type: "tool_result", tool_use_id: p.callId, content: p.output, is_error: p.isError };
146
+ return { type: "tool_result", tool_use_id: p.callId, content: toolResultAnthropicContent(p), is_error: p.isError };
144
147
  }
145
148
  return { type: "text", text: "" };
146
149
  });
147
150
  }
151
+ /**
152
+ * spc_012-N-03: a `ContentBlock` inside a structured tool result → Anthropic wire block.
153
+ * Anthropic's `tool_result.content` natively accepts text and image blocks. Anything else
154
+ * (audio/video/file, `fileId`/`object` sources with no Anthropic wire form) degrades to an
155
+ * explicit `[modality]` placeholder visible to the model, never silently dropped (INV-012-01).
156
+ */
157
+ function contentBlockToAnthropic(block) {
158
+ if (block.type === "text")
159
+ return { type: "text", text: block.text };
160
+ if (block.type === "image") {
161
+ const src = block.source;
162
+ if (src.kind === "base64") {
163
+ return { type: "image", source: { type: "base64", media_type: block.mediaType ?? "image/png", data: src.data } };
164
+ }
165
+ if (src.kind === "url") {
166
+ return { type: "image", source: { type: "url", url: src.url } };
167
+ }
168
+ return { type: "text", text: "[image]" };
169
+ }
170
+ return { type: "text", text: `[${block.type}]` };
171
+ }
172
+ /**
173
+ * Structured `contentParts` win when present; otherwise `output` is the text form.
174
+ */
175
+ function toolResultAnthropicContent(p) {
176
+ if (p.contentParts !== undefined)
177
+ return normalizeToolResultPart(p).blocks.map(contentBlockToAnthropic);
178
+ return p.output;
179
+ }
148
180
  /**
149
181
  * History turns with the volatile State turn appended as the latest turn, for
150
182
  * providers that render it inline (OpenAI-family, Gemini, Ollama). Appending
@@ -166,7 +198,7 @@ export function toAnthropicMessages(turns, nativeReplay) {
166
198
  if (msg.role === "tool") {
167
199
  const parts = (msg.contentParts ?? [])
168
200
  .filter((p) => p.type === "tool_result")
169
- .map(p => ({ type: "tool_result", tool_use_id: p.callId, content: p.output, is_error: p.isError }));
201
+ .map(p => ({ type: "tool_result", tool_use_id: p.callId, content: toolResultAnthropicContent(p), is_error: p.isError }));
170
202
  if (parts.length)
171
203
  result.push({ role: "user", content: parts });
172
204
  continue;
@@ -246,10 +278,20 @@ export function toOpenAIMessageParams(context) {
246
278
  // bindings, where the state is already inside `turns`.
247
279
  for (const msg of turnsWithStateAppended(context)) {
248
280
  if (msg.role === "tool") {
281
+ // spc_012-N-04: OpenAI **chat completions** tool-role messages accept text only — no
282
+ // structured tool_result content exists on this wire. Explicit text-only degradation:
283
+ // `output` is the constructor's text projection, which carries a visible `[modality]`
284
+ // placeholder for any structured block (INV-012-01 — degraded, not silently dropped).
285
+ // The Responses API (openai-responses.ts) does have native structured support and uses it.
249
286
  const parts = (msg.contentParts ?? [])
250
287
  .filter((p) => p.type === "tool_result");
251
288
  for (const p of parts) {
252
- result.push({ role: "tool", tool_call_id: p.callId, content: p.output });
289
+ const canonical = normalizeToolResultPart(p);
290
+ result.push({
291
+ role: "tool",
292
+ tool_call_id: p.callId,
293
+ content: projectToolOutputToText(canonical.blocks),
294
+ });
253
295
  }
254
296
  continue;
255
297
  }
@@ -0,0 +1,29 @@
1
+ import type { CreateProviderOptions } from "./catalog.js";
2
+ import type { InputModality, ResolvedProviderRuntime } from "./model-registry.js";
3
+ export interface CapabilityRequirement {
4
+ requiredInputModalities?: readonly InputModality[];
5
+ tools?: boolean;
6
+ reasoning?: boolean;
7
+ minimumContextWindow?: number;
8
+ }
9
+ export type CapabilityRouteResult = {
10
+ ok: true;
11
+ runtime: ResolvedProviderRuntime;
12
+ } | {
13
+ ok: false;
14
+ error: {
15
+ code: "no_capable_model";
16
+ requirement: CapabilityRequirement;
17
+ candidates: Array<{
18
+ model: string;
19
+ rejectedBy: string[];
20
+ }>;
21
+ };
22
+ };
23
+ /**
24
+ * Deliberately policy-free first-match router. Unknown capability evidence stays eligible for
25
+ * forward compatibility; only evidence of unsupported capability rejects a candidate.
26
+ */
27
+ export declare class CapabilityRouter {
28
+ route(requirement: CapabilityRequirement, candidates: readonly CreateProviderOptions[]): Promise<CapabilityRouteResult>;
29
+ }
@@ -0,0 +1,43 @@
1
+ import { resolveProviderRuntimeAsync } from "./catalog.js";
2
+ /**
3
+ * Deliberately policy-free first-match router. Unknown capability evidence stays eligible for
4
+ * forward compatibility; only evidence of unsupported capability rejects a candidate.
5
+ */
6
+ export class CapabilityRouter {
7
+ async route(requirement, candidates) {
8
+ const rejected = [];
9
+ for (const candidate of candidates) {
10
+ let runtime;
11
+ try {
12
+ runtime = await resolveProviderRuntimeAsync(candidate);
13
+ }
14
+ catch {
15
+ rejected.push({ model: candidate.model, rejectedBy: ["unavailable"] });
16
+ continue;
17
+ }
18
+ const rejectedBy = rejects(requirement, runtime);
19
+ if (rejectedBy.length === 0)
20
+ return { ok: true, runtime };
21
+ rejected.push({ model: candidate.model, rejectedBy });
22
+ }
23
+ return { ok: false, error: { code: "no_capable_model", requirement, candidates: rejected } };
24
+ }
25
+ }
26
+ function rejects(requirement, runtime) {
27
+ const result = [];
28
+ for (const modality of requirement.requiredInputModalities ?? []) {
29
+ if (runtime.effectiveCapabilities.inputModalities[modality].state === "unsupported") {
30
+ result.push(`input:${modality}`);
31
+ }
32
+ }
33
+ if (requirement.tools && runtime.effectiveCapabilities.tools.state === "unsupported")
34
+ result.push("tools");
35
+ if (requirement.reasoning && runtime.effectiveCapabilities.reasoning.state === "unsupported")
36
+ result.push("reasoning");
37
+ if (requirement.minimumContextWindow !== undefined
38
+ && runtime.model.contextWindow !== undefined
39
+ && runtime.model.contextWindow < requirement.minimumContextWindow) {
40
+ result.push("context");
41
+ }
42
+ return result;
43
+ }
@@ -1,9 +1,17 @@
1
1
  import type { LLMProvider } from "../types.js";
2
- import { endpointProfiles, type ModelProfileId, type ProviderId } from "./profiles.js";
3
- export type EndpointProfileId = keyof typeof endpointProfiles;
2
+ import { type ResolvedProviderRuntime } from "./model-registry.js";
3
+ import { type EndpointProfileId, type ProviderId } from "./endpoints.js";
4
+ import { type CredentialOptions } from "./credentials.js";
5
+ import type { ModelCatalog } from "./model-catalog.js";
6
+ export type { EndpointProfileId } from "./endpoints.js";
4
7
  export interface CreateProviderOptions {
5
- model: ModelProfileId | string;
6
- apiKey: string;
8
+ model: string;
9
+ /** Explicit credential; resolver-backed construction can omit it. */
10
+ apiKey?: string;
11
+ bearerToken?: string;
12
+ credentialResolver?: CredentialOptions["credentialResolver"];
13
+ /** Host-provided catalog used only for lookup; it never carries routing policy. */
14
+ modelCatalog?: ModelCatalog;
7
15
  provider?: ProviderId;
8
16
  endpoint?: EndpointProfileId;
9
17
  retry?: {
@@ -13,3 +21,7 @@ export interface CreateProviderOptions {
13
21
  baseURL?: string;
14
22
  }
15
23
  export declare function createProvider(options: CreateProviderOptions): LLMProvider;
24
+ export declare function resolveProviderRuntime(options: CreateProviderOptions): ResolvedProviderRuntime;
25
+ /** I/O-capable equivalent for host credential resolvers and dynamic catalogs. */
26
+ export declare function resolveProviderRuntimeAsync(options: CreateProviderOptions): Promise<ResolvedProviderRuntime>;
27
+ export declare function createProviderAsync(options: CreateProviderOptions): Promise<LLMProvider>;
@@ -1,9 +1,46 @@
1
- import { PROVIDER_REGISTRY, providerRegistryKey } from "./registry.js";
2
- import { endpointProfiles, getModelProfile, modelProfiles } from "./profiles.js";
1
+ import { PROVIDER_REGISTRY, providerRegistryKey, supportsBearerCredential } from "./registry.js";
2
+ import { defaultEndpointForProvider, endpointCapabilitiesFor, generationProtocol, modelRegistry, normalizeModelId, resolveEffectiveModelCapabilities, } from "./model-registry.js";
3
+ import { endpointProfiles } from "./endpoints.js";
4
+ import { resolveCredential, resolveCredentialSync, CredentialResolutionError, } from "./credentials.js";
3
5
  export function createProvider(options) {
4
- const profile = isModelProfileId(options.model) ? getModelProfile(options.model) : undefined;
6
+ return resolveProviderRuntime(options).adapter;
7
+ }
8
+ export function resolveProviderRuntime(options) {
9
+ const draft = resolveRuntimeDraft(options);
10
+ const credential = draft.providerId === "ollama"
11
+ ? { type: "api_key", value: "" }
12
+ : resolveCredentialSync(draft.credentialRequest, options);
13
+ return constructResolvedRuntime(draft, credential, options);
14
+ }
15
+ /** I/O-capable equivalent for host credential resolvers and dynamic catalogs. */
16
+ export async function resolveProviderRuntimeAsync(options) {
17
+ const draft = await resolveRuntimeDraftAsync(options);
18
+ const credential = draft.providerId === "ollama"
19
+ ? { type: "api_key", value: "" }
20
+ : await resolveCredential(draft.credentialRequest, options);
21
+ return constructResolvedRuntime(draft, credential, options);
22
+ }
23
+ export async function createProviderAsync(options) {
24
+ return (await resolveProviderRuntimeAsync(options)).adapter;
25
+ }
26
+ function resolveRuntimeDraft(options) {
5
27
  const parsedProviderId = providerPrefix(options.model);
6
- const endpointId = (options.endpoint ?? profile?.defaultEndpointId ?? defaultEndpointForProvider(options.provider ?? parsedProviderId));
28
+ const providerHint = options.provider ?? parsedProviderId;
29
+ const initialRegistration = modelRegistry.resolve(options.model, providerHint);
30
+ return resolveRuntimeDraftWithRegistration(options, parsedProviderId, initialRegistration);
31
+ }
32
+ async function resolveRuntimeDraftAsync(options) {
33
+ const parsedProviderId = providerPrefix(options.model);
34
+ const providerHint = options.provider ?? parsedProviderId;
35
+ const fromCatalog = options.modelCatalog ? await options.modelCatalog.get(options.model) : undefined;
36
+ const initialRegistration = fromCatalog ?? modelRegistry.resolve(options.model, providerHint);
37
+ return resolveRuntimeDraftWithRegistration(options, parsedProviderId, initialRegistration);
38
+ }
39
+ function resolveRuntimeDraftWithRegistration(options, parsedProviderId, initialRegistration) {
40
+ const providerHint = options.provider ?? parsedProviderId;
41
+ const endpointId = (options.endpoint
42
+ ?? initialRegistration?.defaultEndpointId
43
+ ?? (providerHint ? defaultEndpointForProvider(providerHint) : undefined));
7
44
  if (!endpointId) {
8
45
  throw new Error(`Unknown model profile: ${options.model}. Pass provider or endpoint for custom model names.`);
9
46
  }
@@ -11,26 +48,84 @@ export function createProvider(options) {
11
48
  if (!endpoint) {
12
49
  throw new Error(`Unknown endpoint profile: ${endpointId}`);
13
50
  }
14
- const providerId = profile?.providerId ?? options.provider ?? parsedProviderId ?? endpoint.providerId;
15
- if (profile && options.provider && options.provider !== profile.providerId) {
16
- throw new Error(`Model ${profile.id} belongs to provider ${profile.providerId}, not ${options.provider}`);
17
- }
51
+ const providerId = (initialRegistration?.descriptor.providerId ?? options.provider ?? parsedProviderId ?? endpoint.providerId);
52
+ const registration = initialRegistration ?? modelRegistry.resolve(options.model, providerId);
53
+ if (!registration)
54
+ throw new Error(`Unable to resolve model ${options.model} for provider ${providerId}`);
18
55
  if (parsedProviderId && options.provider && parsedProviderId !== options.provider) {
19
56
  throw new Error(`Model ${options.model} uses provider prefix ${parsedProviderId}, not ${options.provider}`);
20
57
  }
21
58
  if (endpoint.providerId !== providerId) {
22
59
  throw new Error(`Endpoint ${endpoint.id} does not belong to provider ${providerId}`);
23
60
  }
24
- const model = modelNameForProvider(options.model, providerId);
25
- const baseURL = options.baseURL ?? endpoint.baseURL;
26
- // Single data-driven dispatch: one registry keyed by (providerId, protocol).
27
- const make = PROVIDER_REGISTRY[providerRegistryKey(providerId, endpoint.protocol)];
28
- if (make)
29
- return make(options.apiKey, model, options.retry, baseURL);
30
- throw new Error(`No Node provider factory for ${options.model} on ${endpoint.id}`);
61
+ const model = normalizeModelId(providerId, options.model);
62
+ return {
63
+ providerId,
64
+ model,
65
+ endpointId,
66
+ endpoint,
67
+ registration,
68
+ credentialRequest: { providerId, modelId: model, endpointId, protocol: endpoint.protocol },
69
+ };
70
+ }
71
+ function constructResolvedRuntime(draft, credential, options) {
72
+ if (credential.type === "bearer" && !supportsBearerCredential(draft.providerId, draft.endpoint.protocol)) {
73
+ throw new CredentialResolutionError("credential_auth_mode_unsupported", draft.providerId);
74
+ }
75
+ const make = PROVIDER_REGISTRY[providerRegistryKey(draft.providerId, draft.endpoint.protocol)];
76
+ if (!make)
77
+ throw new Error(`No Node provider factory for ${draft.model} on ${draft.endpoint.id}`);
78
+ const protocol = generationProtocol(draft.endpoint.protocol);
79
+ if (!protocol)
80
+ throw new Error(`No Node provider factory for ${draft.model} on ${draft.endpoint.id}`);
81
+ const adapter = make(credential.value, draft.model, options.retry, options.baseURL ?? draft.endpoint.baseURL, draft.registration.recommendedRuntimePolicy, credential.type);
82
+ const resolved = {
83
+ identity: { providerId: draft.providerId, modelId: draft.model, endpointId: draft.endpointId, protocol },
84
+ model: draft.registration.descriptor,
85
+ endpoint: draft.endpoint,
86
+ adapter,
87
+ effectiveCapabilities: resolveEffectiveModelCapabilities({
88
+ model: draft.registration.descriptor,
89
+ protocol,
90
+ endpointCapabilities: endpointCapabilitiesFor(draft.endpointId, options.baseURL === undefined || options.endpoint !== undefined, options.baseURL === undefined),
91
+ }),
92
+ ...(draft.registration.recommendedRuntimePolicy ? { runtimePolicy: draft.registration.recommendedRuntimePolicy } : {}),
93
+ };
94
+ const bind = adapter.bindResolvedRuntime;
95
+ bind?.call(adapter, resolved);
96
+ const requestPlanIdentity = {
97
+ providerId: resolved.identity.providerId,
98
+ modelId: resolved.identity.modelId,
99
+ endpoint: {
100
+ id: resolved.identity.endpointId,
101
+ protocol: resolved.identity.protocol,
102
+ baseURL: safeEndpointBaseURL(options.baseURL ?? draft.endpoint.baseURL),
103
+ },
104
+ ...(resolved.effectiveCapabilities.nativeTokenCounting.state === "supported"
105
+ && resolved.effectiveCapabilities.nativeTokenCounting.value === true
106
+ ? { nativeTokenCounting: true }
107
+ : {}),
108
+ };
109
+ adapter
110
+ .requestPlanIdentity = () => ({
111
+ ...requestPlanIdentity,
112
+ endpoint: { ...requestPlanIdentity.endpoint },
113
+ });
114
+ return resolved;
31
115
  }
32
- function isModelProfileId(model) {
33
- return Object.prototype.hasOwnProperty.call(modelProfiles, model);
116
+ function safeEndpointBaseURL(baseURL) {
117
+ try {
118
+ const parsed = new URL(baseURL);
119
+ parsed.username = "";
120
+ parsed.password = "";
121
+ parsed.search = "";
122
+ parsed.hash = "";
123
+ return parsed.toString().replace(/\/$/, "");
124
+ }
125
+ catch {
126
+ // Provider construction has already accepted this opaque endpoint. Do not invent a replacement.
127
+ return baseURL;
128
+ }
34
129
  }
35
130
  function providerPrefix(model) {
36
131
  const [prefix] = model.split("/", 1);
@@ -39,22 +134,3 @@ function providerPrefix(model) {
39
134
  function providerIds() {
40
135
  return Array.from(new Set(Object.values(endpointProfiles).map(endpoint => endpoint.providerId)));
41
136
  }
42
- function defaultEndpointForProvider(providerId) {
43
- if (!providerId)
44
- return undefined;
45
- const defaults = {
46
- anthropic: "anthropic.messages",
47
- openai: "openai.chat",
48
- minimax: "minimax.anthropic",
49
- deepseek: "deepseek.anthropic",
50
- kimi: "kimi.anthropic",
51
- qwen: "qwen.anthropic",
52
- gemini: "gemini.google",
53
- glm: "glm.anthropic",
54
- };
55
- return defaults[providerId];
56
- }
57
- function modelNameForProvider(model, providerId) {
58
- const prefix = `${providerId}/`;
59
- return model.startsWith(prefix) ? model.slice(prefix.length) : model;
60
- }
@@ -0,0 +1,57 @@
1
+ import type { Message, RenderedContext, ToolOutputBlock, ToolResultPart, ToolSchema, ToolCall, ProviderReplay } from "../types.js";
2
+ import type { ResolvedProviderRuntime } from "./model-registry.js";
3
+ export declare class ContentValidationError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ export declare class ToolResultProjectionConflictError extends ContentValidationError {
7
+ constructor(callId: string);
8
+ }
9
+ export interface CanonicalToolResult {
10
+ readonly type: "tool_result";
11
+ readonly callId: string;
12
+ readonly blocks: readonly ToolOutputBlock[];
13
+ readonly isError: boolean;
14
+ /** Preserves the caller's explicit text-vs-block wire intent without duplicating content. */
15
+ readonly contentForm?: "text" | "blocks";
16
+ }
17
+ export type CanonicalMessageBlock = ToolOutputBlock | CanonicalToolResult;
18
+ export interface CanonicalMessage {
19
+ readonly role: Message["role"];
20
+ readonly blocks: readonly CanonicalMessageBlock[];
21
+ /** `blocks` remains authoritative; this only preserves an observable wire-shape distinction. */
22
+ readonly contentForm?: "text" | "blocks";
23
+ readonly toolCalls?: readonly ToolCall[];
24
+ readonly tokenCount?: number;
25
+ readonly providerReplay?: ProviderReplay;
26
+ }
27
+ export interface CanonicalRenderedContext {
28
+ readonly systemText: string;
29
+ readonly systemStable?: string;
30
+ readonly systemKnowledge?: string;
31
+ readonly turns: readonly CanonicalMessage[];
32
+ readonly stateTurn?: CanonicalMessage;
33
+ readonly frozenPrefixLen?: number;
34
+ readonly budgetOverflow?: RenderedContext["budgetOverflow"];
35
+ }
36
+ export interface CanonicalAdapterInput {
37
+ readonly context: CanonicalRenderedContext;
38
+ readonly tools: readonly ToolSchema[];
39
+ readonly resolved: ResolvedProviderRuntime<unknown>;
40
+ readonly extensions: Readonly<Record<string, unknown>>;
41
+ }
42
+ export declare function validateToolOutputBlocks(blocks: readonly ToolOutputBlock[]): void;
43
+ export declare function projectToolOutputToText(blocks: readonly ToolOutputBlock[]): string;
44
+ export declare function normalizeToolResultPart(part: ToolResultPart): CanonicalToolResult;
45
+ /** Attach process-local blocks only within the operation that produced them. */
46
+ export declare function attachToolOutputOverlay(context: RenderedContext, overlay: ReadonlyMap<string, readonly ToolOutputBlock[]>): RenderedContext;
47
+ export declare function normalizeCanonicalContext(context: RenderedContext, replayForMessage?: (message: Message) => ProviderReplay | undefined): CanonicalRenderedContext;
48
+ /** Full-tree validation uses the resolved operation capabilities. Unknown stays unknown/fail-open. */
49
+ export declare function validateCanonicalAdapterInput(input: CanonicalAdapterInput): void;
50
+ export declare function normalizeCanonicalAdapterInput(input: {
51
+ context: RenderedContext;
52
+ tools: readonly ToolSchema[];
53
+ resolved: ResolvedProviderRuntime<unknown>;
54
+ extensions?: Readonly<Record<string, unknown>>;
55
+ replayForMessage?: (message: Message) => ProviderReplay | undefined;
56
+ }): CanonicalAdapterInput;
57
+ export declare function normalizeToolResultContent(callId: string, output: string, isError: boolean, contentParts: readonly ToolOutputBlock[] | undefined): CanonicalToolResult;