@deepstrike/wasm 0.2.52 → 0.2.60

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 (54) hide show
  1. package/README.md +15 -14
  2. package/dist/agent-ir.d.ts +81 -0
  3. package/dist/agent-ir.js +115 -0
  4. package/dist/agent.d.ts +140 -0
  5. package/dist/agent.js +34 -0
  6. package/dist/governance.d.ts +0 -25
  7. package/dist/governance.js +1 -53
  8. package/dist/index.d.ts +23 -8
  9. package/dist/index.js +11 -4
  10. package/dist/memory/durable.d.ts +13 -0
  11. package/dist/memory/durable.js +42 -0
  12. package/dist/memory/in-memory-store.d.ts +8 -6
  13. package/dist/memory/in-memory-store.js +8 -2
  14. package/dist/memory/index.d.ts +18 -2
  15. package/dist/providers/anthropic.d.ts +9 -0
  16. package/dist/providers/anthropic.js +12 -33
  17. package/dist/providers/content-policy.d.ts +16 -0
  18. package/dist/providers/content-policy.js +39 -0
  19. package/dist/providers/openai.d.ts +36 -17
  20. package/dist/providers/openai.js +86 -46
  21. package/dist/providers/provider-error.d.ts +22 -0
  22. package/dist/providers/provider-error.js +90 -0
  23. package/dist/providers/request-plan.d.ts +87 -0
  24. package/dist/providers/request-plan.js +153 -0
  25. package/dist/providers/stop-reason.d.ts +5 -0
  26. package/dist/providers/stop-reason.js +24 -0
  27. package/dist/runtime/canonical-kernel-step.d.ts +2 -5
  28. package/dist/runtime/canonical-kernel-step.js +40 -58
  29. package/dist/runtime/context-policy.d.ts +10 -12
  30. package/dist/runtime/context-policy.js +6 -8
  31. package/dist/runtime/durable-content.d.ts +44 -0
  32. package/dist/runtime/durable-content.js +119 -0
  33. package/dist/runtime/execution-plane.d.ts +2 -2
  34. package/dist/runtime/execution-plane.js +2 -2
  35. package/dist/runtime/kernel-event-log.js +0 -1
  36. package/dist/runtime/kernel-step.d.ts +1 -2
  37. package/dist/runtime/kernel-step.js +1 -1
  38. package/dist/runtime/provider-replay.d.ts +1 -1
  39. package/dist/runtime/provider-replay.js +12 -20
  40. package/dist/runtime/runner.d.ts +22 -18
  41. package/dist/runtime/runner.js +115 -55
  42. package/dist/runtime/session-log.d.ts +8 -1
  43. package/dist/runtime/session-repair.d.ts +1 -1
  44. package/dist/runtime/session-repair.js +1 -1
  45. package/dist/runtime/sha256.d.ts +2 -0
  46. package/dist/runtime/sha256.js +38 -0
  47. package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
  48. package/dist/runtime/sub-agent-orchestrator.js +3 -3
  49. package/dist/runtime/types/agent.d.ts +17 -7
  50. package/dist/runtime/types/agent.js +30 -4
  51. package/dist/runtime/workflow-control-flow.d.ts +0 -4
  52. package/dist/runtime/workflow-control-flow.js +0 -16
  53. package/dist/types.d.ts +58 -5
  54. package/package.json +2 -2
@@ -0,0 +1,42 @@
1
+ /** Binds host storage to the public durable Memory contract for one agent and namespace. */
2
+ export class DurableMemory {
3
+ store;
4
+ agentId;
5
+ scope;
6
+ namespace;
7
+ constructor(store, agentId, scope) {
8
+ this.store = store;
9
+ this.agentId = agentId;
10
+ this.scope = scope;
11
+ this.namespace = scope.namespace;
12
+ }
13
+ async search(query, options = {}) {
14
+ const request = {
15
+ scope: this.scope,
16
+ query,
17
+ top_k: options.topK ?? 5,
18
+ kinds: options.kinds ?? [],
19
+ ...(options.minScore === undefined ? {} : { min_score: options.minScore }),
20
+ };
21
+ return (await this.store.search(this.agentId, request))
22
+ .map(hit => hit.record)
23
+ .filter(record => sameScope(record.scope, this.scope));
24
+ }
25
+ async get(recordId) {
26
+ const record = await this.store.get(this.agentId, recordId);
27
+ return record !== null && sameScope(record.scope, this.scope) ? record : null;
28
+ }
29
+ async put(record) {
30
+ if (!sameScope(record.scope, this.scope)) {
31
+ throw new Error("memory record scope must match the bound Memory scope");
32
+ }
33
+ await this.store.put(this.agentId, record);
34
+ }
35
+ async delete(recordId) {
36
+ if (await this.get(recordId) !== null)
37
+ await this.store.delete(this.agentId, recordId);
38
+ }
39
+ }
40
+ function sameScope(left, right) {
41
+ return left.tenant_id === right.tenant_id && left.namespace === right.namespace;
42
+ }
@@ -1,27 +1,29 @@
1
1
  /**
2
- * `InMemoryDreamStore` — a lightweight `DreamStore` implementation backed by per-agent `Map`s.
2
+ * `InMemoryMemoryStore` — a lightweight `MemoryStore` implementation backed by per-agent `Map`s.
3
3
  * WASM port of node/src/memory/in-memory-store.ts. See that file for the full design.
4
4
  *
5
5
  * Search returns a genuine relevance score (never stored confidence); the store bounds itself by
6
6
  * value-ordered retention eviction (M3) and mirrors recall lifecycle and pin state (M3/M4).
7
7
  */
8
- import type { DreamStore, MemoryQuery, MemoryRecall, MemoryRecallLifecycle, MemoryRecord, SessionData } from "./index.js";
9
- export interface InMemoryDreamStoreOptions {
8
+ import type { MemoryStore, MemoryQuery, MemoryRecall, MemoryRecallLifecycle, MemoryRecord, SessionData } from "./index.js";
9
+ export interface InMemoryMemoryStoreOptions {
10
10
  maxRecords?: number;
11
11
  staleWarningDays?: number;
12
12
  now?: () => number;
13
13
  }
14
- export declare class InMemoryDreamStore implements DreamStore {
14
+ export declare class InMemoryMemoryStore implements MemoryStore {
15
15
  private readonly initialMemories;
16
16
  private memories;
17
17
  readonly savedSessions: SessionData[];
18
18
  private readonly maxRecords?;
19
19
  private readonly staleWarningDays;
20
20
  private readonly now;
21
- constructor(initialMemories?: MemoryRecord[], options?: InMemoryDreamStoreOptions);
21
+ constructor(initialMemories?: MemoryRecord[], options?: InMemoryMemoryStoreOptions);
22
22
  private recordsFor;
23
23
  private evictToCapacity;
24
- upsert(agentId: string, incoming: MemoryRecord): Promise<void>;
24
+ put(agentId: string, incoming: MemoryRecord): Promise<void>;
25
+ get(agentId: string, recordId: string): Promise<MemoryRecord | null>;
26
+ delete(agentId: string, recordId: string): Promise<void>;
25
27
  search(agentId: string, query: MemoryQuery): Promise<MemoryRecall[]>;
26
28
  saveSession(data: SessionData): Promise<void>;
27
29
  recordRecall(agentId: string, recalls: MemoryRecallLifecycle[]): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { rankMemories } from "./ranking.js";
2
2
  import { memoryRetentionScore } from "./retention.js";
3
- export class InMemoryDreamStore {
3
+ export class InMemoryMemoryStore {
4
4
  initialMemories;
5
5
  memories = new Map();
6
6
  savedSessions = [];
@@ -34,7 +34,7 @@ export class InMemoryDreamStore {
34
34
  scored.sort((a, b) => b.score - a.score || a.insertionIndex - b.insertionIndex);
35
35
  return scored.slice(0, this.maxRecords).map(entry => entry.record);
36
36
  }
37
- async upsert(agentId, incoming) {
37
+ async put(agentId, incoming) {
38
38
  const kept = [...this.recordsFor(agentId)];
39
39
  const index = kept.findIndex(record => record.scope.tenant_id === incoming.scope.tenant_id
40
40
  && record.scope.namespace === incoming.scope.namespace
@@ -45,6 +45,12 @@ export class InMemoryDreamStore {
45
45
  kept.push(incoming);
46
46
  this.memories.set(agentId, this.evictToCapacity(kept));
47
47
  }
48
+ async get(agentId, recordId) {
49
+ return this.recordsFor(agentId).find(record => record.record_id === recordId) ?? null;
50
+ }
51
+ async delete(agentId, recordId) {
52
+ this.memories.set(agentId, this.recordsFor(agentId).filter(record => record.record_id !== recordId));
53
+ }
48
54
  async search(agentId, query) {
49
55
  const all = this.recordsFor(agentId);
50
56
  const candidates = all.filter(record => record.scope.tenant_id === query.scope.tenant_id
@@ -72,8 +72,24 @@ export interface MemoryRecallLifecycle {
72
72
  recall_count: number;
73
73
  last_recalled_at: number;
74
74
  }
75
- export interface DreamStore {
76
- upsert(agentId: string, record: MemoryRecord): Promise<void>;
75
+ export interface MemorySearchOptions {
76
+ topK?: number;
77
+ kinds?: MemoryKind[];
78
+ minScore?: number;
79
+ }
80
+ /** Public durable memory bound to one agent and scope, distinct from `WorkingMemory`. */
81
+ export interface Memory {
82
+ readonly namespace?: string;
83
+ search(query: string, options?: MemorySearchOptions): Promise<MemoryRecord[]>;
84
+ get(recordId: string): Promise<MemoryRecord | null>;
85
+ put(record: MemoryRecord): Promise<void>;
86
+ delete(recordId: string): Promise<void>;
87
+ }
88
+ /** Host-owned durable storage behind an agent-bound public `Memory` descriptor. */
89
+ export interface MemoryStore {
90
+ put(agentId: string, record: MemoryRecord): Promise<void>;
91
+ get(agentId: string, recordId: string): Promise<MemoryRecord | null>;
92
+ delete(agentId: string, recordId: string): Promise<void>;
77
93
  search(agentId: string, query: MemoryQuery): Promise<MemoryRecall[]>;
78
94
  /** Persist a completed session before the runner's one extraction pass. */
79
95
  saveSession(data: SessionData): Promise<void>;
@@ -6,6 +6,15 @@ export declare class AnthropicProvider implements LLMProvider {
6
6
  private nativeAssistantBlocks;
7
7
  constructor(apiKey: string, model?: string, maxTokens?: number);
8
8
  descriptor(): ProviderDescriptor;
9
+ requestPlanIdentity(): {
10
+ providerId: string;
11
+ modelId: string;
12
+ endpoint: {
13
+ id: string;
14
+ protocol: "anthropic-messages";
15
+ baseURL: string;
16
+ };
17
+ };
9
18
  peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
10
19
  seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
11
20
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
@@ -141,20 +141,21 @@ export class AnthropicProvider {
141
141
  },
142
142
  };
143
143
  }
144
+ requestPlanIdentity() {
145
+ return {
146
+ providerId: "anthropic",
147
+ modelId: this.model,
148
+ endpoint: { id: "anthropic.messages", protocol: "anthropic-messages", baseURL: "https://api.anthropic.com" },
149
+ };
150
+ }
144
151
  peekProviderReplay(message) {
145
152
  const blocks = this.nativeAssistantBlocks.get(assistantReplayKey(message));
146
- return blocks?.length ? { native_blocks: blocks } : undefined;
153
+ return blocks?.length ? { protocol: "anthropic-messages", native_blocks: blocks } : undefined;
147
154
  }
148
155
  seedProviderReplay(message, replay) {
149
- if (replay.native_blocks?.length) {
156
+ if (replay.protocol === "anthropic-messages" && replay.native_blocks?.length) {
150
157
  this.nativeAssistantBlocks.set(assistantReplayKey(message), replay.native_blocks);
151
- return;
152
158
  }
153
- // Legacy log without persisted native blocks: reconstruct neutral
154
- // text + tool_use blocks so a tool-use turn can be replayed.
155
- const blocks = reconstructAnthropicBlocks(message);
156
- if (blocks.length)
157
- this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
158
159
  }
159
160
  async complete(context, tools, extensions) {
160
161
  return collectStreamMessage(this.stream(context, tools, extensions));
@@ -208,8 +209,9 @@ export class AnthropicProvider {
208
209
  body: JSON.stringify(body),
209
210
  ...(signal ? { signal } : {}), // #2-B-ii: a preempt aborts the in-flight request at the socket.
210
211
  });
211
- if (!resp.ok)
212
- throw new Error(`Anthropic ${resp.status}: ${await resp.text()}`);
212
+ if (!resp.ok) {
213
+ throw Object.assign(new Error(`Anthropic ${resp.status}: ${await resp.text()}`), { status: resp.status });
214
+ }
213
215
  const toolBlocks = {};
214
216
  const nativeBlocks = {};
215
217
  let finalText = "";
@@ -322,26 +324,3 @@ export class AnthropicProvider {
322
324
  this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
323
325
  }
324
326
  }
325
- /**
326
- * Reconstruct Anthropic assistant content blocks from a neutral transcript when
327
- * no provider replay was persisted. Only meaningful for tool-use turns.
328
- */
329
- function reconstructAnthropicBlocks(message) {
330
- const toolCalls = message.toolCalls ?? [];
331
- if (!toolCalls.length)
332
- return [];
333
- const blocks = [];
334
- if (message.content)
335
- blocks.push({ type: "text", text: message.content });
336
- for (const tc of toolCalls) {
337
- let input = {};
338
- try {
339
- input = JSON.parse(tc.arguments || "{}");
340
- }
341
- catch {
342
- input = {};
343
- }
344
- blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input });
345
- }
346
- return blocks;
347
- }
@@ -0,0 +1,16 @@
1
+ export type ContentPlacement = "message" | "tool_result";
2
+ export type ContentDisposition = "native" | "bridge" | "unsupported";
3
+ export type InputModality = "text" | "image" | "audio" | "video" | "file";
4
+ /**
5
+ * Provider serialization policy for canonical content. A bridge preserves the deterministic
6
+ * visible text projection; it never permits a serializer to drop the block or replace it with
7
+ * empty content.
8
+ */
9
+ export declare function contentDispositionFor(protocol: string, modality: InputModality, placement: ContentPlacement): ContentDisposition;
10
+ export declare class ContentPolicyError extends Error {
11
+ readonly protocol: string;
12
+ readonly modality: InputModality;
13
+ readonly placement: ContentPlacement;
14
+ constructor(protocol: string, modality: InputModality, placement: ContentPlacement);
15
+ }
16
+ export declare function requireContentDisposition(protocol: string, modality: InputModality, placement: ContentPlacement): ContentDisposition;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Provider serialization policy for canonical content. A bridge preserves the deterministic
3
+ * visible text projection; it never permits a serializer to drop the block or replace it with
4
+ * empty content.
5
+ */
6
+ export function contentDispositionFor(protocol, modality, placement) {
7
+ if (modality === "text" || modality === "image") {
8
+ if (protocol === "ollama-chat" && modality === "image" && placement === "tool_result")
9
+ return "bridge";
10
+ return "native";
11
+ }
12
+ if (protocol === "openai-responses" && modality === "file" && placement === "message")
13
+ return "native";
14
+ if (protocol === "openai-responses" && modality === "file" && placement === "tool_result")
15
+ return "bridge";
16
+ if (protocol === "openai-chat" && (modality === "audio" || modality === "file" || modality === "video"))
17
+ return "bridge";
18
+ if (protocol === "gemini" && (modality === "audio" || modality === "file" || modality === "video"))
19
+ return "bridge";
20
+ return "unsupported";
21
+ }
22
+ export class ContentPolicyError extends Error {
23
+ protocol;
24
+ modality;
25
+ placement;
26
+ constructor(protocol, modality, placement) {
27
+ super(`Unsupported content policy: ${modality} ${placement} is not supported by ${protocol}`);
28
+ this.protocol = protocol;
29
+ this.modality = modality;
30
+ this.placement = placement;
31
+ this.name = "ContentPolicyError";
32
+ }
33
+ }
34
+ export function requireContentDisposition(protocol, modality, placement) {
35
+ const disposition = contentDispositionFor(protocol, modality, placement);
36
+ if (disposition === "unsupported")
37
+ throw new ContentPolicyError(protocol, modality, placement);
38
+ return disposition;
39
+ }
@@ -1,9 +1,38 @@
1
- import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message } from "../types.js";
1
+ import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message, ProviderDescriptor } from "../types.js";
2
+ type OpenAIProviderDialect = "openai" | "qwen" | "deepseek" | "minimax";
3
+ export interface OpenAIProviderOptions {
4
+ apiKey: string;
5
+ model?: string;
6
+ baseURL?: string;
7
+ provider?: string;
8
+ endpointId?: string;
9
+ dialect?: OpenAIProviderDialect;
10
+ }
11
+ export interface BackendProviderOptions {
12
+ apiKey: string;
13
+ model?: string;
14
+ baseURL?: string;
15
+ }
2
16
  export declare class OpenAIProvider implements LLMProvider {
3
17
  protected readonly apiKey: string;
4
18
  protected readonly model: string;
5
19
  protected readonly baseUrl: string;
6
- constructor(apiKey: string, model?: string, baseUrl?: string);
20
+ private readonly provider;
21
+ private readonly endpoint;
22
+ private readonly dialect;
23
+ constructor(options: OpenAIProviderOptions);
24
+ protected providerId(): string;
25
+ protected endpointId(): string;
26
+ descriptor(): ProviderDescriptor;
27
+ requestPlanIdentity(): {
28
+ providerId: string;
29
+ modelId: string;
30
+ endpoint: {
31
+ id: string;
32
+ protocol: "openai-chat";
33
+ baseURL: string;
34
+ };
35
+ };
7
36
  protected buildTools(tools: ToolSchema[]): {
8
37
  type: string;
9
38
  function: {
@@ -16,18 +45,8 @@ export declare class OpenAIProvider implements LLMProvider {
16
45
  protected streamInner(context: RenderedContext, tools: ToolSchema[], extraBody: Record<string, unknown>, exposeReasoning?: boolean, signal?: AbortSignal): AsyncIterable<StreamEvent>;
17
46
  stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
18
47
  }
19
- export declare class QwenProvider extends OpenAIProvider {
20
- constructor(apiKey: string, model?: string);
21
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
22
- }
23
- export declare class DeepSeekProvider extends OpenAIProvider {
24
- constructor(apiKey: string, model?: string);
25
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
26
- }
27
- export declare class MiniMaxProvider extends OpenAIProvider {
28
- constructor(apiKey: string, model?: string);
29
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
30
- }
31
- export declare class KimiProvider extends OpenAIProvider {
32
- constructor(apiKey: string, model?: string);
33
- }
48
+ export declare function qwen(options: BackendProviderOptions): LLMProvider;
49
+ export declare function deepseek(options: BackendProviderOptions): LLMProvider;
50
+ export declare function minimax(options: BackendProviderOptions): LLMProvider;
51
+ export declare function kimi(options: BackendProviderOptions): LLMProvider;
52
+ export {};
@@ -6,10 +6,35 @@ export class OpenAIProvider {
6
6
  apiKey;
7
7
  model;
8
8
  baseUrl;
9
- constructor(apiKey, model = "gpt-4o", baseUrl = "https://api.openai.com/v1") {
10
- this.apiKey = apiKey;
11
- this.model = model;
12
- this.baseUrl = baseUrl;
9
+ provider;
10
+ endpoint;
11
+ dialect;
12
+ constructor(options) {
13
+ this.apiKey = options.apiKey;
14
+ this.model = options.model ?? "gpt-4o";
15
+ this.baseUrl = options.baseURL ?? "https://api.openai.com/v1";
16
+ this.provider = options.provider ?? "openai";
17
+ this.endpoint = options.endpointId ?? "openai.chat";
18
+ this.dialect = options.dialect ?? "openai";
19
+ }
20
+ providerId() { return this.provider; }
21
+ endpointId() { return this.endpoint; }
22
+ descriptor() {
23
+ const reasoning = DEEPSEEK_REASONERS.has(this.model) || MINIMAX_REASONERS.has(this.model);
24
+ return {
25
+ provider: this.providerId(),
26
+ protocol: "openai-chat",
27
+ model: this.model,
28
+ reasoning: { supported: reasoning, preserveAcrossToolTurns: reasoning },
29
+ toolCalls: { supported: true, requiresStrictPairing: true },
30
+ };
31
+ }
32
+ requestPlanIdentity() {
33
+ return {
34
+ providerId: this.providerId(),
35
+ modelId: this.model,
36
+ endpoint: { id: this.endpointId(), protocol: "openai-chat", baseURL: this.baseUrl },
37
+ };
13
38
  }
14
39
  buildTools(tools) {
15
40
  return tools.map(t => ({ type: "function", function: { name: t.name, description: t.description, parameters: JSON.parse(t.parameters) } }));
@@ -31,8 +56,9 @@ export class OpenAIProvider {
31
56
  body: JSON.stringify(body),
32
57
  ...(signal ? { signal } : {}), // #2-B-ii: a preempt aborts the in-flight request at the socket.
33
58
  });
34
- if (!resp.ok)
35
- throw new Error(`OpenAI ${resp.status}: ${await resp.text()}`);
59
+ if (!resp.ok) {
60
+ throw Object.assign(new Error(`OpenAI ${resp.status}: ${await resp.text()}`), { status: resp.status });
61
+ }
36
62
  const toolAccum = {};
37
63
  const reader = resp.body.getReader();
38
64
  const decoder = new TextDecoder();
@@ -91,51 +117,65 @@ export class OpenAIProvider {
91
117
  }
92
118
  }
93
119
  async *stream(context, tools, extensions, _state, signal) {
120
+ if (this.dialect === "qwen") {
121
+ const enableThinking = Boolean(extensions?.enableThinking);
122
+ const thinkingBudget = extensions?.thinkingBudget;
123
+ const { enableThinking: _, thinkingBudget: __, expose_reasoning: ___, exposeReasoning: ____, ...passthrough } = extensions ?? {};
124
+ yield* this.streamInner(context, tools, {
125
+ ...passthrough,
126
+ ...(enableThinking ? { enable_thinking: true, ...(thinkingBudget ? { thinking_budget: thinkingBudget } : {}) } : {}),
127
+ }, enableThinking, signal);
128
+ return;
129
+ }
130
+ if (this.dialect === "deepseek" || this.dialect === "minimax") {
131
+ const exposeReasoning = Boolean(extensions?.exposeReasoning);
132
+ const isReasoner = this.dialect === "deepseek"
133
+ ? DEEPSEEK_REASONERS.has(this.model)
134
+ : MINIMAX_REASONERS.has(this.model);
135
+ const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
136
+ yield* this.streamInner(context, isReasoner ? [] : tools, passthrough, exposeReasoning, signal);
137
+ return;
138
+ }
94
139
  const { expose_reasoning: _, exposeReasoning: __, ...passthrough } = extensions ?? {};
95
140
  yield* this.streamInner(context, tools, passthrough, false, signal);
96
141
  }
97
142
  }
98
- export class QwenProvider extends OpenAIProvider {
99
- constructor(apiKey, model = "qwen-max") {
100
- super(apiKey, model, "https://dashscope.aliyuncs.com/compatible-mode/v1");
101
- }
102
- async *stream(context, tools, extensions, _state, signal) {
103
- const enableThinking = Boolean(extensions?.enableThinking);
104
- const thinkingBudget = extensions?.thinkingBudget;
105
- const { enableThinking: _, thinkingBudget: __, expose_reasoning: ___, exposeReasoning: ____, ...passthrough } = extensions ?? {};
106
- const extra = {
107
- ...passthrough,
108
- ...(enableThinking ? { enable_thinking: true, ...(thinkingBudget ? { thinking_budget: thinkingBudget } : {}) } : {}),
109
- };
110
- yield* this.streamInner(context, tools, extra, enableThinking, signal);
111
- }
143
+ export function qwen(options) {
144
+ return new OpenAIProvider({
145
+ ...options,
146
+ model: options.model ?? "qwen-max",
147
+ baseURL: options.baseURL ?? "https://dashscope.aliyuncs.com/compatible-mode/v1",
148
+ provider: "qwen",
149
+ endpointId: "qwen.dashscope",
150
+ dialect: "qwen",
151
+ });
112
152
  }
113
- export class DeepSeekProvider extends OpenAIProvider {
114
- constructor(apiKey, model = "deepseek-chat") {
115
- super(apiKey, model, "https://api.deepseek.com/v1");
116
- }
117
- async *stream(context, tools, extensions, _state, signal) {
118
- const exposeReasoning = Boolean(extensions?.exposeReasoning);
119
- const isReasoner = DEEPSEEK_REASONERS.has(this.model);
120
- const filteredTools = isReasoner ? [] : tools;
121
- const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
122
- yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning, signal);
123
- }
153
+ export function deepseek(options) {
154
+ return new OpenAIProvider({
155
+ ...options,
156
+ model: options.model ?? "deepseek-chat",
157
+ baseURL: options.baseURL ?? "https://api.deepseek.com/v1",
158
+ provider: "deepseek",
159
+ endpointId: "deepseek.openai",
160
+ dialect: "deepseek",
161
+ });
124
162
  }
125
- export class MiniMaxProvider extends OpenAIProvider {
126
- constructor(apiKey, model = "MiniMax-Text-01") {
127
- super(apiKey, model, "https://api.minimax.chat/v1");
128
- }
129
- async *stream(context, tools, extensions, _state, signal) {
130
- const exposeReasoning = Boolean(extensions?.exposeReasoning);
131
- const isReasoner = MINIMAX_REASONERS.has(this.model);
132
- const filteredTools = isReasoner ? [] : tools;
133
- const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
134
- yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning, signal);
135
- }
163
+ export function minimax(options) {
164
+ return new OpenAIProvider({
165
+ ...options,
166
+ model: options.model ?? "MiniMax-Text-01",
167
+ baseURL: options.baseURL ?? "https://api.minimax.chat/v1",
168
+ provider: "minimax",
169
+ endpointId: "minimax.openai",
170
+ dialect: "minimax",
171
+ });
136
172
  }
137
- export class KimiProvider extends OpenAIProvider {
138
- constructor(apiKey, model = "moonshot-v1-8k") {
139
- super(apiKey, model, "https://api.moonshot.cn/v1");
140
- }
173
+ export function kimi(options) {
174
+ return new OpenAIProvider({
175
+ ...options,
176
+ model: options.model ?? "moonshot-v1-8k",
177
+ baseURL: options.baseURL ?? "https://api.moonshot.cn/v1",
178
+ provider: "kimi",
179
+ endpointId: "kimi.openai",
180
+ });
141
181
  }
@@ -0,0 +1,22 @@
1
+ export type ProviderErrorKind = "transport" | "auth" | "rate_limit" | "context_overflow" | "invalid_request" | "modality" | "model_unavailable" | "protocol" | "unknown";
2
+ export interface ProviderErrorOptions {
3
+ provider: string;
4
+ kind: ProviderErrorKind;
5
+ retryable: boolean;
6
+ message: string;
7
+ httpStatus?: number;
8
+ providerCode?: string;
9
+ cause?: unknown;
10
+ }
11
+ /** The only provider-failure shape allowed across the runner/kernel boundary. */
12
+ export declare class ProviderError extends Error {
13
+ readonly provider: string;
14
+ readonly kind: ProviderErrorKind;
15
+ readonly retryable: boolean;
16
+ readonly httpStatus?: number;
17
+ readonly providerCode?: string;
18
+ readonly cause: unknown;
19
+ constructor(options: ProviderErrorOptions);
20
+ }
21
+ export declare function classifyProviderError(provider: string, error: unknown): ProviderError;
22
+ export declare function providerErrorEventFields(error: ProviderError): Record<string, unknown>;
@@ -0,0 +1,90 @@
1
+ /** The only provider-failure shape allowed across the runner/kernel boundary. */
2
+ export class ProviderError extends Error {
3
+ provider;
4
+ kind;
5
+ retryable;
6
+ httpStatus;
7
+ providerCode;
8
+ cause;
9
+ constructor(options) {
10
+ super(options.message, options.cause === undefined ? undefined : { cause: options.cause });
11
+ this.name = "ProviderError";
12
+ this.provider = options.provider;
13
+ this.kind = options.kind;
14
+ this.retryable = options.retryable;
15
+ this.httpStatus = options.httpStatus;
16
+ this.providerCode = options.providerCode;
17
+ this.cause = options.cause;
18
+ }
19
+ }
20
+ const CONTEXT_OVERFLOW_CODES = new Set(["context_length_exceeded", "prompt_too_long"]);
21
+ const NETWORK_CODES = new Set(["ECONNABORTED", "ECONNREFUSED", "ECONNRESET", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT"]);
22
+ function object(value) {
23
+ return value && typeof value === "object" ? value : undefined;
24
+ }
25
+ function scalarString(value) {
26
+ return typeof value === "string" && value.length > 0 ? value : undefined;
27
+ }
28
+ function scalarStatus(value) {
29
+ return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599 ? value : undefined;
30
+ }
31
+ function status(error) {
32
+ const outer = object(error);
33
+ const response = object(outer?.response);
34
+ return scalarStatus(outer?.httpStatus) ?? scalarStatus(outer?.status) ?? scalarStatus(response?.status);
35
+ }
36
+ function code(error) {
37
+ const outer = object(error);
38
+ const nested = object(outer?.error);
39
+ const bodyError = object(object(outer?.body)?.error);
40
+ return scalarString(outer?.providerCode)
41
+ ?? scalarString(outer?.code)
42
+ ?? scalarString(outer?.error_code)
43
+ ?? scalarString(nested?.code)
44
+ ?? scalarString(nested?.error_code)
45
+ ?? scalarString(bodyError?.code)
46
+ ?? scalarString(bodyError?.error_code);
47
+ }
48
+ function kind(error, httpStatus, providerCode) {
49
+ if (httpStatus === 413 || (providerCode && CONTEXT_OVERFLOW_CODES.has(providerCode.toLowerCase())))
50
+ return "context_overflow";
51
+ if (httpStatus === 401 || httpStatus === 403)
52
+ return "auth";
53
+ if (httpStatus === 429)
54
+ return "rate_limit";
55
+ if (providerCode === "model_not_found" || httpStatus === 404 || (httpStatus !== undefined && httpStatus >= 500))
56
+ return "model_unavailable";
57
+ if (httpStatus === 408 || httpStatus === 409)
58
+ return "transport";
59
+ if (httpStatus === 400 || httpStatus === 422)
60
+ return "invalid_request";
61
+ if (providerCode && NETWORK_CODES.has(providerCode.toUpperCase()))
62
+ return "transport";
63
+ if (error instanceof TypeError && httpStatus === undefined)
64
+ return "transport";
65
+ return "unknown";
66
+ }
67
+ export function classifyProviderError(provider, error) {
68
+ if (error instanceof ProviderError)
69
+ return error;
70
+ const httpStatus = status(error);
71
+ const providerCode = code(error);
72
+ const classified = kind(error, httpStatus, providerCode);
73
+ return new ProviderError({
74
+ provider,
75
+ kind: classified,
76
+ retryable: classified === "transport" || classified === "rate_limit" || classified === "model_unavailable",
77
+ message: error instanceof Error && error.message ? error.message : typeof error === "string" ? error : "Provider request failed",
78
+ ...(httpStatus !== undefined ? { httpStatus } : {}),
79
+ ...(providerCode !== undefined ? { providerCode } : {}),
80
+ cause: error,
81
+ });
82
+ }
83
+ export function providerErrorEventFields(error) {
84
+ return {
85
+ error_kind: error.kind,
86
+ retryable: error.retryable,
87
+ ...(error.httpStatus !== undefined ? { http_status: error.httpStatus } : {}),
88
+ ...(error.providerCode !== undefined ? { provider_code: error.providerCode } : {}),
89
+ };
90
+ }