@deepstrike/sdk 0.2.28 → 0.2.31

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 (59) hide show
  1. package/README.md +45 -21
  2. package/dist/harness/harness.d.ts +3 -2
  3. package/dist/harness/harness.js +15 -35
  4. package/dist/harness/judge.d.ts +42 -0
  5. package/dist/harness/judge.js +58 -0
  6. package/dist/harness/public.d.ts +4 -0
  7. package/dist/harness/public.js +3 -0
  8. package/dist/index.d.ts +21 -80
  9. package/dist/index.js +28 -60
  10. package/dist/kernel.d.ts +7 -1
  11. package/dist/memory/public.d.ts +4 -0
  12. package/dist/memory/public.js +3 -0
  13. package/dist/os/public.d.ts +18 -0
  14. package/dist/os/public.js +14 -0
  15. package/dist/planes/public.d.ts +13 -0
  16. package/dist/planes/public.js +9 -0
  17. package/dist/providers/anthropic-compatible.d.ts +23 -0
  18. package/dist/providers/anthropic-compatible.js +29 -0
  19. package/dist/providers/anthropic.d.ts +11 -2
  20. package/dist/providers/anthropic.js +14 -9
  21. package/dist/providers/catalog.js +5 -53
  22. package/dist/providers/deepseek.d.ts +28 -8
  23. package/dist/providers/deepseek.js +38 -157
  24. package/dist/providers/factories.d.ts +31 -0
  25. package/dist/providers/factories.js +33 -0
  26. package/dist/providers/glm.d.ts +5 -4
  27. package/dist/providers/glm.js +8 -23
  28. package/dist/providers/kimi.d.ts +5 -4
  29. package/dist/providers/kimi.js +8 -22
  30. package/dist/providers/minimax.d.ts +26 -12
  31. package/dist/providers/minimax.js +32 -158
  32. package/dist/providers/openai.d.ts +57 -2
  33. package/dist/providers/openai.js +139 -70
  34. package/dist/providers/public.d.ts +10 -0
  35. package/dist/providers/public.js +11 -0
  36. package/dist/providers/qwen.d.ts +19 -19
  37. package/dist/providers/qwen.js +37 -176
  38. package/dist/providers/registry.d.ts +18 -0
  39. package/dist/providers/registry.js +35 -0
  40. package/dist/providers/vendor-profiles.d.ts +54 -0
  41. package/dist/providers/vendor-profiles.js +62 -0
  42. package/dist/runtime/event-stream.d.ts +44 -0
  43. package/dist/runtime/event-stream.js +39 -0
  44. package/dist/runtime/facade.js +6 -1
  45. package/dist/runtime/reactive-session.d.ts +125 -0
  46. package/dist/runtime/reactive-session.js +127 -0
  47. package/dist/runtime/run-group.d.ts +74 -0
  48. package/dist/runtime/run-group.js +72 -0
  49. package/dist/runtime/runner.d.ts +9 -0
  50. package/dist/runtime/runner.js +79 -46
  51. package/dist/runtime/session-log.d.ts +8 -0
  52. package/dist/runtime/turn-policy.d.ts +33 -0
  53. package/dist/runtime/turn-policy.js +58 -0
  54. package/dist/signals/gateway.d.ts +7 -2
  55. package/dist/signals/gateway.js +13 -3
  56. package/dist/signals/types.d.ts +10 -1
  57. package/dist/workflow/public.d.ts +20 -0
  58. package/dist/workflow/public.js +15 -0
  59. package/package.json +54 -2
@@ -0,0 +1,13 @@
1
+ export { WorktreeExecutionPlane, GitWorktreeManager } from "../runtime/worktree-plane.js";
2
+ export type { WorktreeManager } from "../runtime/worktree-plane.js";
3
+ export { FilteredExecutionPlane } from "../runtime/filtered-plane.js";
4
+ export { ProcessSandboxPlane } from "../runtime/process-sandbox-plane.js";
5
+ export type { SandboxOptions } from "../runtime/process-sandbox-plane.js";
6
+ export { McpProxyPlane } from "../runtime/mcp-proxy-plane.js";
7
+ export type { McpServerConfig } from "../runtime/mcp-proxy-plane.js";
8
+ export { RemoteVpcPlane } from "../runtime/remote-vpc-plane.js";
9
+ export type { RemoteVpcOptions } from "../runtime/remote-vpc-plane.js";
10
+ export { NullArchiveStore, FileArchiveStore } from "../runtime/archive.js";
11
+ export type { ArchiveStore } from "../runtime/archive.js";
12
+ export { EnvCredentialVault, InMemoryCredentialVault, ChainedCredentialVault } from "../runtime/credential-vault.js";
13
+ export type { CredentialVault } from "../runtime/credential-vault.js";
@@ -0,0 +1,9 @@
1
+ // `@deepstrike/sdk/planes` — advanced execution planes, archive stores, and credential vaults.
2
+ // The root package exports `LocalExecutionPlane`; specialized planes live here.
3
+ export { WorktreeExecutionPlane, GitWorktreeManager } from "../runtime/worktree-plane.js";
4
+ export { FilteredExecutionPlane } from "../runtime/filtered-plane.js";
5
+ export { ProcessSandboxPlane } from "../runtime/process-sandbox-plane.js";
6
+ export { McpProxyPlane } from "../runtime/mcp-proxy-plane.js";
7
+ export { RemoteVpcPlane } from "../runtime/remote-vpc-plane.js";
8
+ export { NullArchiveStore, FileArchiveStore } from "../runtime/archive.js";
9
+ export { EnvCredentialVault, InMemoryCredentialVault, ChainedCredentialVault } from "../runtime/credential-vault.js";
@@ -0,0 +1,23 @@
1
+ import type { RuntimePolicy } from "../types.js";
2
+ import { AnthropicProvider } from "./anthropic.js";
3
+ import { type AnthropicVendorProfile } from "./vendor-profiles.js";
4
+ /**
5
+ * A vendor that exposes an Anthropic-compatible Messages endpoint (DeepSeek,
6
+ * Kimi, Qwen, GLM, MiniMax, …). All wire behavior is inherited from
7
+ * `AnthropicProvider`; the only per-vendor variation is configuration, supplied
8
+ * as an `AnthropicVendorProfile`. This replaces the family of near-identical
9
+ * `<Vendor>AnthropicProvider` subclasses that existed only to carry that config.
10
+ *
11
+ * Adding a new Anthropic-compatible vendor is now "add a profile" — no new
12
+ * provider class is required (the named `<Vendor>AnthropicProvider` shims are
13
+ * kept only for backward compatibility / `instanceof` checks).
14
+ */
15
+ export declare class AnthropicCompatibleProvider extends AnthropicProvider {
16
+ private readonly vendorProfile;
17
+ constructor(profile: AnthropicVendorProfile, apiKey: string, model?: string, retry?: {
18
+ maxRetries: number;
19
+ baseDelay: number;
20
+ }, baseURL?: string);
21
+ protected providerName(): string;
22
+ runtimePolicy(): RuntimePolicy;
23
+ }
@@ -0,0 +1,29 @@
1
+ import { AnthropicProvider } from "./anthropic.js";
2
+ import { anthropicVendorBaseURL } from "./vendor-profiles.js";
3
+ /**
4
+ * A vendor that exposes an Anthropic-compatible Messages endpoint (DeepSeek,
5
+ * Kimi, Qwen, GLM, MiniMax, …). All wire behavior is inherited from
6
+ * `AnthropicProvider`; the only per-vendor variation is configuration, supplied
7
+ * as an `AnthropicVendorProfile`. This replaces the family of near-identical
8
+ * `<Vendor>AnthropicProvider` subclasses that existed only to carry that config.
9
+ *
10
+ * Adding a new Anthropic-compatible vendor is now "add a profile" — no new
11
+ * provider class is required (the named `<Vendor>AnthropicProvider` shims are
12
+ * kept only for backward compatibility / `instanceof` checks).
13
+ */
14
+ export class AnthropicCompatibleProvider extends AnthropicProvider {
15
+ vendorProfile;
16
+ constructor(profile, apiKey, model, retry, baseURL) {
17
+ super(apiKey, model ?? profile.defaultModel, retry, {
18
+ baseURL: baseURL ?? anthropicVendorBaseURL(profile),
19
+ authMode: "api-key",
20
+ });
21
+ this.vendorProfile = profile;
22
+ }
23
+ providerName() {
24
+ return this.vendorProfile.providerId;
25
+ }
26
+ runtimePolicy() {
27
+ return this.vendorProfile.policies[this.model] ?? {};
28
+ }
29
+ }
@@ -3,14 +3,23 @@ interface AnthropicProviderOptions {
3
3
  baseURL?: string;
4
4
  authMode?: "api-key" | "bearer";
5
5
  }
6
+ /** Options-object form for `AnthropicProvider` — the recommended constructor shape. */
7
+ export interface AnthropicProviderConfig extends AnthropicProviderOptions {
8
+ apiKey: string;
9
+ model?: string;
10
+ retry?: {
11
+ maxRetries: number;
12
+ baseDelay: number;
13
+ };
14
+ }
6
15
  export declare class AnthropicProvider implements LLMProvider {
7
- protected readonly model: string;
8
16
  private client;
9
17
  private circuit;
10
18
  private maxRetries;
11
19
  private baseDelay;
20
+ protected readonly model: string;
12
21
  private nativeAssistantBlocks;
13
- constructor(apiKey: string, model?: string, retry?: {
22
+ constructor(apiKeyOrConfig: string | AnthropicProviderConfig, model?: string, retry?: {
14
23
  maxRetries: number;
15
24
  baseDelay: number;
16
25
  }, options?: AnthropicProviderOptions);
@@ -13,23 +13,28 @@ const CLAUDE_POLICIES = {
13
13
  "claude-3-5-haiku-latest": { maxTurns: 15 },
14
14
  };
15
15
  export class AnthropicProvider {
16
- model;
17
16
  client;
18
17
  circuit;
19
18
  maxRetries;
20
19
  baseDelay;
20
+ model;
21
21
  nativeAssistantBlocks = new Map();
22
- constructor(apiKey, model = "claude-sonnet-4-6", retry = { maxRetries: 3, baseDelay: 1000 }, options = {}) {
23
- this.model = model;
22
+ // Accepts the options object (`new AnthropicProvider({ apiKey, model, baseURL })`) or the legacy
23
+ // positional form (still used by the Anthropic-compatible backend subclasses' `super(...)` calls).
24
+ constructor(apiKeyOrConfig, model = "claude-sonnet-4-6", retry = { maxRetries: 3, baseDelay: 1000 }, options = {}) {
25
+ const c = typeof apiKeyOrConfig === "string"
26
+ ? { apiKey: apiKeyOrConfig, model, retry, ...options }
27
+ : { model: "claude-sonnet-4-6", retry: { maxRetries: 3, baseDelay: 1000 }, ...apiKeyOrConfig };
28
+ this.model = c.model ?? "claude-sonnet-4-6";
24
29
  this.client = withServerRuntimeGuard(() => new Anthropic({
25
- ...(options.authMode === "bearer"
26
- ? { authToken: apiKey, apiKey: null }
27
- : { apiKey, authToken: null }),
28
- ...(options.baseURL ? { baseURL: options.baseURL } : {}),
30
+ ...(c.authMode === "bearer"
31
+ ? { authToken: c.apiKey, apiKey: null }
32
+ : { apiKey: c.apiKey, authToken: null }),
33
+ ...(c.baseURL ? { baseURL: c.baseURL } : {}),
29
34
  }));
30
35
  this.circuit = new CircuitBreaker();
31
- this.maxRetries = retry.maxRetries;
32
- this.baseDelay = retry.baseDelay;
36
+ this.maxRetries = c.retry?.maxRetries ?? 3;
37
+ this.baseDelay = c.retry?.baseDelay ?? 1000;
33
38
  }
34
39
  runtimePolicy() {
35
40
  return CLAUDE_POLICIES[this.model] ?? {};
@@ -1,12 +1,4 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
- import { OpenAIChatProvider } from "./openai.js";
3
- import { DeepSeekProvider, DeepSeekAnthropicProvider } from "./deepseek.js";
4
- import { KimiProvider, KimiAnthropicProvider } from "./kimi.js";
5
- import { OpenAIResponsesProvider } from "./openai-responses.js";
6
- import { MiniMaxAnthropicProvider, MiniMaxOpenAIProvider } from "./minimax.js";
7
- import { QwenProvider, QwenAnthropicProvider } from "./qwen.js";
8
- import { GeminiProvider } from "./gemini.js";
9
- import { GLMProvider, GLMAnthropicProvider } from "./glm.js";
1
+ import { PROVIDER_REGISTRY, providerRegistryKey } from "./registry.js";
10
2
  import { endpointProfiles, getModelProfile, modelProfiles } from "./profiles.js";
11
3
  export function createProvider(options) {
12
4
  const profile = isModelProfileId(options.model) ? getModelProfile(options.model) : undefined;
@@ -31,50 +23,10 @@ export function createProvider(options) {
31
23
  }
32
24
  const model = modelNameForProvider(options.model, providerId);
33
25
  const baseURL = options.baseURL ?? endpoint.baseURL;
34
- if (providerId === "anthropic" && endpoint.protocol === "anthropic-messages") {
35
- return new AnthropicProvider(options.apiKey, model, options.retry, { baseURL });
36
- }
37
- if (providerId === "openai") {
38
- if (endpoint.protocol === "openai-chat") {
39
- return new OpenAIChatProvider(options.apiKey, model, options.retry, baseURL);
40
- }
41
- if (endpoint.protocol === "openai-responses") {
42
- return new OpenAIResponsesProvider(options.apiKey, model, options.retry, baseURL);
43
- }
44
- }
45
- if (providerId === "minimax" && endpoint.protocol === "anthropic-messages") {
46
- return new MiniMaxAnthropicProvider(options.apiKey, model, options.retry, baseURL);
47
- }
48
- if (providerId === "minimax" && endpoint.protocol === "openai-chat") {
49
- return new MiniMaxOpenAIProvider(options.apiKey, model, options.retry, baseURL);
50
- }
51
- if (providerId === "deepseek" && endpoint.protocol === "anthropic-messages") {
52
- return new DeepSeekAnthropicProvider(options.apiKey, model, options.retry, baseURL);
53
- }
54
- if (providerId === "deepseek" && endpoint.protocol === "openai-chat") {
55
- return new DeepSeekProvider(options.apiKey, model, options.retry, baseURL);
56
- }
57
- if (providerId === "kimi" && endpoint.protocol === "anthropic-messages") {
58
- return new KimiAnthropicProvider(options.apiKey, model, options.retry, baseURL);
59
- }
60
- if (providerId === "kimi" && endpoint.protocol === "openai-chat") {
61
- return new KimiProvider(options.apiKey, model, options.retry, baseURL);
62
- }
63
- if (providerId === "qwen" && endpoint.protocol === "anthropic-messages") {
64
- return new QwenAnthropicProvider(options.apiKey, model, options.retry, baseURL);
65
- }
66
- if (providerId === "qwen" && endpoint.protocol === "openai-chat") {
67
- return new QwenProvider(options.apiKey, model, options.retry, baseURL);
68
- }
69
- if (providerId === "gemini" && endpoint.protocol === "gemini") {
70
- return new GeminiProvider(options.apiKey, model, options.retry, baseURL);
71
- }
72
- if (providerId === "glm" && endpoint.protocol === "anthropic-messages") {
73
- return new GLMAnthropicProvider(options.apiKey, model, options.retry, baseURL);
74
- }
75
- if (providerId === "glm" && endpoint.protocol === "openai-chat") {
76
- return new GLMProvider(options.apiKey, model, options.retry, baseURL);
77
- }
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);
78
30
  throw new Error(`No Node provider factory for ${options.model} on ${endpoint.id}`);
79
31
  }
80
32
  function isModelProfileId(model) {
@@ -1,17 +1,25 @@
1
- import type { Message, ProviderDescriptor, RenderedContext, ToolSchema, StreamEvent, RuntimePolicy } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
- import { OpenAIChatProvider } from "./openai.js";
1
+ import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
+ import { OpenAIChatProvider, type OpenAIChatTurnReasoning } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * DeepSeek over its Anthropic-compatible endpoint.
6
+ * @deprecated Prefer `deepseek({ protocol: "anthropic" })`. Behavior is now fully
7
+ * data-driven via `anthropicVendorProfiles.deepseek`; this thin shim is kept for
8
+ * backward compatibility and `instanceof` checks.
6
9
  */
7
- export declare class DeepSeekAnthropicProvider extends AnthropicProvider {
10
+ export declare class DeepSeekAnthropicProvider extends AnthropicCompatibleProvider {
8
11
  constructor(apiKey: string, model?: string, retry?: {
9
12
  maxRetries: number;
10
13
  baseDelay: number;
11
14
  }, baseURL?: string);
12
- protected providerName(): string;
13
- runtimePolicy(): RuntimePolicy;
14
15
  }
16
+ /**
17
+ * DeepSeek over its OpenAI-compatible endpoint. Reasoning is carried out-of-band as
18
+ * `reasoning_content`; replay persists DeepSeek's schema_version-2 envelope (with the
19
+ * native `tool_calls` blocks). Request shaping (`reasoning_effort` + `extra_body.thinking`)
20
+ * and replay are supplied via the OpenAIChatProvider Template-Method hooks; the streaming /
21
+ * tool-call machinery is inherited from the base class.
22
+ */
15
23
  export declare class DeepSeekProvider extends OpenAIChatProvider {
16
24
  constructor(apiKey: string, model?: string, retry?: {
17
25
  maxRetries: number;
@@ -20,7 +28,19 @@ export declare class DeepSeekProvider extends OpenAIChatProvider {
20
28
  runtimePolicy(): RuntimePolicy;
21
29
  descriptor(): ProviderDescriptor;
22
30
  protected requireNonEmptyReasoningReplayForToolTurns(extensions?: Record<string, unknown>): boolean;
23
- complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
24
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
31
+ protected cacheKeyParams(): Record<string, unknown>;
32
+ protected usesInlineThinkingTags(): boolean;
33
+ protected exposeReasoningDelta(extensions?: Record<string, unknown>): boolean;
34
+ protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
35
+ protected rememberCompleteReplay(content: string, toolCalls: Array<{
36
+ id: string;
37
+ name: string;
38
+ arguments: string;
39
+ }>, r: OpenAIChatTurnReasoning): void;
40
+ protected rememberStreamReplay(content: string, toolCalls: Array<{
41
+ id: string;
42
+ name: string;
43
+ arguments: string;
44
+ }>, r: OpenAIChatTurnReasoning): void;
25
45
  private rememberDeepSeekReplay;
26
46
  }
@@ -1,30 +1,26 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
1
  import { OpenAIChatProvider } from "./openai.js";
2
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
3
3
  import { endpointProfiles } from "./profiles.js";
4
- import { omitExtensionKeys, openAICachedPromptTokens } from "./base.js";
5
- const DEEPSEEK_POLICIES = {
6
- "deepseek-chat": { maxTurns: 25 },
7
- "deepseek-reasoner": { maxTurns: 50 },
8
- "deepseek-v4-flash": { maxTurns: 20 },
9
- "deepseek-v4-pro": { maxTurns: 35 },
10
- };
4
+ import { omitExtensionKeys } from "./base.js";
5
+ import { DEEPSEEK_POLICIES, anthropicVendorProfiles } from "./vendor-profiles.js";
11
6
  /**
12
7
  * DeepSeek over its Anthropic-compatible endpoint.
8
+ * @deprecated Prefer `deepseek({ protocol: "anthropic" })`. Behavior is now fully
9
+ * data-driven via `anthropicVendorProfiles.deepseek`; this thin shim is kept for
10
+ * backward compatibility and `instanceof` checks.
13
11
  */
14
- export class DeepSeekAnthropicProvider extends AnthropicProvider {
15
- constructor(apiKey, model = "deepseek-v4-flash", retry, baseURL = endpointProfiles["deepseek.anthropic"].baseURL) {
16
- super(apiKey, model, retry, {
17
- baseURL,
18
- authMode: "api-key",
19
- });
20
- }
21
- providerName() {
22
- return "deepseek";
23
- }
24
- runtimePolicy() {
25
- return DEEPSEEK_POLICIES[this.model] ?? {};
12
+ export class DeepSeekAnthropicProvider extends AnthropicCompatibleProvider {
13
+ constructor(apiKey, model, retry, baseURL) {
14
+ super(anthropicVendorProfiles.deepseek, apiKey, model, retry, baseURL);
26
15
  }
27
16
  }
17
+ /**
18
+ * DeepSeek over its OpenAI-compatible endpoint. Reasoning is carried out-of-band as
19
+ * `reasoning_content`; replay persists DeepSeek's schema_version-2 envelope (with the
20
+ * native `tool_calls` blocks). Request shaping (`reasoning_effort` + `extra_body.thinking`)
21
+ * and replay are supplied via the OpenAIChatProvider Template-Method hooks; the streaming /
22
+ * tool-call machinery is inherited from the base class.
23
+ */
28
24
  export class DeepSeekProvider extends OpenAIChatProvider {
29
25
  constructor(apiKey, model = "deepseek-v4-flash", retry, baseURL = endpointProfiles["deepseek.openai"].baseURL) {
30
26
  super(apiKey, model, retry, baseURL);
@@ -53,146 +49,38 @@ export class DeepSeekProvider extends OpenAIChatProvider {
53
49
  return false;
54
50
  return extensions?.thinking !== false;
55
51
  }
56
- async complete(context, tools, extensions) {
52
+ // DeepSeek strictly validates the request body and 400s on unknown params, so never send
53
+ // OpenAI's `prompt_cache_key` (DeepSeek auto prefix-caches anyway).
54
+ // Ref: https://api-docs.deepseek.com/quick_start/error_codes
55
+ cacheKeyParams() {
56
+ return {};
57
+ }
58
+ // Reasoning arrives out-of-band as `reasoning_content`, never as inline <thinking> tags.
59
+ usesInlineThinkingTags() {
60
+ return false;
61
+ }
62
+ exposeReasoningDelta(extensions) {
63
+ return (extensions?.exposeReasoning ?? false);
64
+ }
65
+ prepareExtensions(extensions) {
57
66
  const thinking = extensions?.thinking === false ? "disabled" : "enabled";
58
67
  const thinkingEnabled = thinking !== "disabled";
59
68
  const reasoningEffort = extensions?.reasoningEffort === "max" ? "max" : "high";
60
- const requestExtensions = {
69
+ return {
61
70
  ...omitExtensionKeys(extensions, ["thinking", "reasoningEffort", "exposeReasoning", "extra_body", "reasoning_effort"]),
62
71
  __deepstrikeThinkingEnabled: thinkingEnabled,
63
- // Re-thread the degrade control flag (omitExtensionKeys strips internal
64
- // keys) so buildChatMessages can honor it; the wire-request omit drops it.
72
+ // Re-thread the degrade control flag (omitExtensionKeys strips internal keys) so
73
+ // buildChatMessages can honor it; the base requestExtensions omit keeps it off nothing.
65
74
  ...(extensions?.degradeMissingReasoningReplay === true ? { degradeMissingReasoningReplay: true } : {}),
66
75
  reasoning_effort: reasoningEffort,
67
76
  extra_body: { thinking: { type: thinking } },
68
77
  };
69
- if (this.circuit.isOpen())
70
- throw new Error("Circuit breaker open");
71
- const msgs = this.buildChatMessages(context, requestExtensions);
72
- let lastErr;
73
- for (let i = 0; i < this.maxRetries; i++) {
74
- try {
75
- const resp = await this.client.chat.completions.create({
76
- ...this.requestExtensions(requestExtensions),
77
- model: this.model,
78
- messages: msgs,
79
- ...(tools.length ? { tools: this.chat.buildTools(tools) } : {}),
80
- });
81
- this.circuit.recordSuccess();
82
- const choice = resp.choices[0].message;
83
- const nativeToolCalls = choice.tool_calls ?? [];
84
- const toolCalls = this.chat.normalizeToolCalls(nativeToolCalls);
85
- const content = choice.content ?? "";
86
- this.rememberDeepSeekReplay(content, toolCalls, choice.reasoning_content, nativeToolCalls);
87
- return { role: "assistant", content, tokenCount: resp.usage?.completion_tokens ?? resp.usage?.total_tokens, toolCalls };
88
- }
89
- catch (err) {
90
- lastErr = err;
91
- this.circuit.recordFailure();
92
- if (i < this.maxRetries - 1)
93
- await new Promise(r => setTimeout(r, this.baseDelay * 2 ** i));
94
- }
95
- }
96
- throw lastErr;
97
78
  }
98
- async *stream(context, tools, extensions) {
99
- const exposeReasoning = extensions?.exposeReasoning ?? false;
100
- const thinking = extensions?.thinking === false ? "disabled" : "enabled";
101
- const reasoningEffort = extensions?.reasoningEffort === "max" ? "max" : "high";
102
- const msgs = this.buildChatMessages(context, extensions);
103
- const toolCallBufs = {};
104
- const emittedToolCallIndexes = new Set();
105
- let reasoningContent = "";
106
- let finalText = "";
107
- const stream = await this.client.chat.completions.create({
108
- ...omitExtensionKeys(extensions, [
109
- "model", "messages", "tools", "stream", "stream_options", "extra_body", "reasoning_effort",
110
- "exposeReasoning", "thinking", "reasoningEffort", "__deepstrikeThinkingEnabled",
111
- ]),
112
- model: this.model,
113
- messages: msgs,
114
- ...(tools.length ? { tools: this.chat.buildTools(tools) } : {}),
115
- stream: true,
116
- stream_options: { include_usage: true },
117
- reasoning_effort: reasoningEffort,
118
- extra_body: { thinking: { type: thinking } },
119
- });
120
- let totalTokens = 0;
121
- let inputTokens = 0;
122
- let outputTokens = 0;
123
- let cacheReadTokens = 0;
124
- for await (const chunk of stream) {
125
- if (chunk.usage) {
126
- totalTokens = chunk.usage.total_tokens;
127
- inputTokens = chunk.usage.prompt_tokens ?? 0;
128
- outputTokens = chunk.usage.completion_tokens ?? 0;
129
- cacheReadTokens = openAICachedPromptTokens(chunk.usage);
130
- continue;
131
- }
132
- const choice = chunk.choices[0];
133
- if (!choice)
134
- continue;
135
- const delta = choice.delta;
136
- if (!delta)
137
- continue;
138
- if (exposeReasoning && delta.reasoning_content) {
139
- yield { type: "thinking_delta", delta: delta.reasoning_content };
140
- }
141
- if (delta.reasoning_content)
142
- reasoningContent += String(delta.reasoning_content);
143
- if (delta.content) {
144
- finalText += String(delta.content);
145
- yield { type: "text_delta", delta: delta.content };
146
- }
147
- for (const tc of delta.tool_calls ?? []) {
148
- const idx = tc.index;
149
- if (!toolCallBufs[idx])
150
- toolCallBufs[idx] = { id: tc.id ?? "", name: "", argsBuf: "" };
151
- if (tc.function?.name)
152
- toolCallBufs[idx].name += tc.function.name;
153
- toolCallBufs[idx].argsBuf += tc.function?.arguments ?? "";
154
- }
155
- if (choice.finish_reason === "tool_calls") {
156
- const toolCalls = Object.values(toolCallBufs).map(tb => ({
157
- id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}",
158
- }));
159
- this.rememberDeepSeekReplay(finalText, toolCalls, reasoningContent, nativeToolCallsFromBuffers(toolCallBufs));
160
- for (const [index, tb] of Object.entries(toolCallBufs)) {
161
- const idx = Number(index);
162
- if (emittedToolCallIndexes.has(idx))
163
- continue;
164
- let args = {};
165
- try {
166
- args = JSON.parse(tb.argsBuf || "{}");
167
- }
168
- catch {
169
- args = {};
170
- }
171
- emittedToolCallIndexes.add(idx);
172
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
173
- }
174
- }
175
- }
176
- const toolCalls = Object.values(toolCallBufs).map(tb => ({
177
- id: tb.id, name: tb.name, arguments: tb.argsBuf || "{}",
178
- }));
179
- this.rememberDeepSeekReplay(finalText, toolCalls, reasoningContent, nativeToolCallsFromBuffers(toolCallBufs));
180
- for (const [index, tb] of Object.entries(toolCallBufs)) {
181
- const idx = Number(index);
182
- if (emittedToolCallIndexes.has(idx))
183
- continue;
184
- let args = {};
185
- try {
186
- args = JSON.parse(tb.argsBuf || "{}");
187
- }
188
- catch {
189
- args = {};
190
- }
191
- emittedToolCallIndexes.add(idx);
192
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
193
- }
194
- if (totalTokens > 0)
195
- yield { type: "usage", totalTokens, inputTokens, outputTokens, ...(cacheReadTokens > 0 ? { cacheReadInputTokens: cacheReadTokens } : {}) };
79
+ rememberCompleteReplay(content, toolCalls, r) {
80
+ this.rememberDeepSeekReplay(content, toolCalls, r.reasoningContent, r.nativeToolCalls);
81
+ }
82
+ rememberStreamReplay(content, toolCalls, r) {
83
+ this.rememberDeepSeekReplay(content, toolCalls, r.reasoningContent, r.nativeToolCalls);
196
84
  }
197
85
  rememberDeepSeekReplay(content, toolCalls, reasoningContent, nativeToolCalls) {
198
86
  if (typeof reasoningContent !== "string" || !reasoningContent.trim())
@@ -207,10 +95,3 @@ export class DeepSeekProvider extends OpenAIChatProvider {
207
95
  });
208
96
  }
209
97
  }
210
- function nativeToolCallsFromBuffers(toolCallBufs) {
211
- return Object.values(toolCallBufs).map(tb => ({
212
- id: tb.id,
213
- type: "function",
214
- function: { name: tb.name, arguments: tb.argsBuf || "{}" },
215
- }));
216
- }
@@ -0,0 +1,31 @@
1
+ import type { LLMProvider } from "../types.js";
2
+ /** Options for a backend provider factory. `protocol` only applies to backends with both wires. */
3
+ export interface BackendProviderOptions {
4
+ apiKey: string;
5
+ model?: string;
6
+ /** Override the endpoint base URL (defaults to the backend's profile for the chosen protocol). */
7
+ baseURL?: string;
8
+ retry?: {
9
+ maxRetries: number;
10
+ baseDelay: number;
11
+ };
12
+ /** Wire protocol for dual-protocol backends. Defaults per backend (see each factory). */
13
+ protocol?: "openai" | "anthropic";
14
+ }
15
+ /** DeepSeek. Defaults to the OpenAI-compatible wire (richer reasoning-replay handling). */
16
+ export declare function deepseek(o: BackendProviderOptions): LLMProvider;
17
+ /** Moonshot Kimi. Defaults to the OpenAI-compatible wire. */
18
+ export declare function kimi(o: BackendProviderOptions): LLMProvider;
19
+ /** Alibaba Qwen / DashScope. Defaults to the OpenAI-compatible (DashScope) wire. */
20
+ export declare function qwen(o: BackendProviderOptions): LLMProvider;
21
+ /** Zhipu GLM. Defaults to the OpenAI-compatible wire. */
22
+ export declare function glm(o: BackendProviderOptions): LLMProvider;
23
+ /** MiniMax. Defaults to the Anthropic-compatible wire (the primary M2.x path). */
24
+ export declare function minimax(o: BackendProviderOptions): LLMProvider;
25
+ /** Google Gemini (single wire). */
26
+ export declare function gemini(o: Omit<BackendProviderOptions, "protocol">): LLMProvider;
27
+ /** Local Ollama (single wire, no API key). */
28
+ export declare function ollama(o?: {
29
+ model?: string;
30
+ baseURL?: string;
31
+ }): LLMProvider;
@@ -0,0 +1,33 @@
1
+ import { PROVIDER_REGISTRY } from "./registry.js";
2
+ import { OllamaProvider } from "./ollama.js";
3
+ function build(providerId, protocol, o) {
4
+ return PROVIDER_REGISTRY[`${providerId}:${protocol}`](o.apiKey, o.model, o.retry, o.baseURL);
5
+ }
6
+ /** DeepSeek. Defaults to the OpenAI-compatible wire (richer reasoning-replay handling). */
7
+ export function deepseek(o) {
8
+ return build("deepseek", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
9
+ }
10
+ /** Moonshot Kimi. Defaults to the OpenAI-compatible wire. */
11
+ export function kimi(o) {
12
+ return build("kimi", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
13
+ }
14
+ /** Alibaba Qwen / DashScope. Defaults to the OpenAI-compatible (DashScope) wire. */
15
+ export function qwen(o) {
16
+ return build("qwen", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
17
+ }
18
+ /** Zhipu GLM. Defaults to the OpenAI-compatible wire. */
19
+ export function glm(o) {
20
+ return build("glm", o.protocol === "anthropic" ? "anthropic-messages" : "openai-chat", o);
21
+ }
22
+ /** MiniMax. Defaults to the Anthropic-compatible wire (the primary M2.x path). */
23
+ export function minimax(o) {
24
+ return build("minimax", o.protocol === "openai" ? "openai-chat" : "anthropic-messages", o);
25
+ }
26
+ /** Google Gemini (single wire). */
27
+ export function gemini(o) {
28
+ return PROVIDER_REGISTRY["gemini:gemini"](o.apiKey, o.model, o.retry, o.baseURL);
29
+ }
30
+ /** Local Ollama (single wire, no API key). */
31
+ export function ollama(o = {}) {
32
+ return new OllamaProvider(o.model, o.baseURL);
33
+ }
@@ -1,16 +1,17 @@
1
1
  import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
2
  import { OpenAIChatProvider } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * GLM over its Anthropic-compatible endpoint.
6
+ * @deprecated Prefer `glm({ protocol: "anthropic" })`. Behavior is now fully
7
+ * data-driven via `anthropicVendorProfiles.glm`; this thin shim is kept for
8
+ * backward compatibility and `instanceof` checks.
6
9
  */
7
- export declare class GLMAnthropicProvider extends AnthropicProvider {
10
+ export declare class GLMAnthropicProvider extends AnthropicCompatibleProvider {
8
11
  constructor(apiKey: string, model?: string, retry?: {
9
12
  maxRetries: number;
10
13
  baseDelay: number;
11
14
  }, baseURL?: string);
12
- protected providerName(): string;
13
- runtimePolicy(): RuntimePolicy;
14
15
  }
15
16
  export declare class GLMProvider extends OpenAIChatProvider {
16
17
  constructor(apiKey: string, model?: string, retry?: {
@@ -1,31 +1,16 @@
1
- import { AnthropicProvider } from "./anthropic.js";
2
1
  import { OpenAIChatProvider } from "./openai.js";
2
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
3
3
  import { endpointProfiles } from "./profiles.js";
4
- const GLM_POLICIES = {
5
- "glm-5.1": { maxTurns: 50 },
6
- "glm/glm-5.1": { maxTurns: 50 },
7
- "glm-4-plus": { maxTurns: 35 },
8
- "glm/glm-4-plus": { maxTurns: 35 },
9
- "glm-4-flash": { maxTurns: 15 },
10
- "glm/glm-4-flash": { maxTurns: 15 },
11
- "glm-4-air": { maxTurns: 20 },
12
- "glm/glm-4-air": { maxTurns: 20 },
13
- };
4
+ import { GLM_POLICIES, anthropicVendorProfiles } from "./vendor-profiles.js";
14
5
  /**
15
6
  * GLM over its Anthropic-compatible endpoint.
7
+ * @deprecated Prefer `glm({ protocol: "anthropic" })`. Behavior is now fully
8
+ * data-driven via `anthropicVendorProfiles.glm`; this thin shim is kept for
9
+ * backward compatibility and `instanceof` checks.
16
10
  */
17
- export class GLMAnthropicProvider extends AnthropicProvider {
18
- constructor(apiKey, model = "glm-5.1", retry, baseURL = endpointProfiles["glm.anthropic"].baseURL) {
19
- super(apiKey, model, retry, {
20
- baseURL,
21
- authMode: "api-key",
22
- });
23
- }
24
- providerName() {
25
- return "glm";
26
- }
27
- runtimePolicy() {
28
- return GLM_POLICIES[this.model] ?? {};
11
+ export class GLMAnthropicProvider extends AnthropicCompatibleProvider {
12
+ constructor(apiKey, model, retry, baseURL) {
13
+ super(anthropicVendorProfiles.glm, apiKey, model, retry, baseURL);
29
14
  }
30
15
  }
31
16
  export class GLMProvider extends OpenAIChatProvider {
@@ -1,16 +1,17 @@
1
1
  import type { ProviderDescriptor, RuntimePolicy } from "../types.js";
2
- import { AnthropicProvider } from "./anthropic.js";
3
2
  import { OpenAIChatProvider } from "./openai.js";
3
+ import { AnthropicCompatibleProvider } from "./anthropic-compatible.js";
4
4
  /**
5
5
  * Kimi over its Anthropic-compatible endpoint.
6
+ * @deprecated Prefer `kimi({ protocol: "anthropic" })`. Behavior is now fully
7
+ * data-driven via `anthropicVendorProfiles.kimi`; this thin shim is kept for
8
+ * backward compatibility and `instanceof` checks.
6
9
  */
7
- export declare class KimiAnthropicProvider extends AnthropicProvider {
10
+ export declare class KimiAnthropicProvider extends AnthropicCompatibleProvider {
8
11
  constructor(apiKey: string, model?: string, retry?: {
9
12
  maxRetries: number;
10
13
  baseDelay: number;
11
14
  }, baseURL?: string);
12
- protected providerName(): string;
13
- runtimePolicy(): RuntimePolicy;
14
15
  }
15
16
  export declare class KimiProvider extends OpenAIChatProvider {
16
17
  constructor(apiKey: string, model?: string, retry?: {