@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
@@ -1,137 +1,19 @@
1
1
  import OpenAI from "openai";
2
2
  import { withServerRuntimeGuard } from "../runtime/server.js";
3
- import { CircuitBreaker, omitExtensionKeys } from "./base.js";
4
- import { normalizeToolCall } from "./base.js";
5
- import { UnsupportedModalityError } from "./base.js";
6
- export class OpenAIResponsesAdapter {
7
- buildTools(tools) {
8
- return tools.map(t => ({
9
- type: "function",
10
- name: t.name,
11
- description: t.description,
12
- parameters: JSON.parse(t.parameters),
13
- }));
14
- }
15
- buildInstructions(context) {
16
- return context.systemText || undefined;
17
- }
18
- buildInput(context, state) {
19
- const input = [];
20
- const turns = context.turns;
21
- const uncoveredMessages = state?.previousResponseId
22
- ? turns.slice(state.coveredMessageCount)
23
- : turns;
24
- for (const message of uncoveredMessages) {
25
- if (message.role === "assistant" && message.toolCalls?.length) {
26
- if (message.content || message.contentParts?.length) {
27
- input.push({
28
- role: "assistant",
29
- content: this.buildMessageContent(message),
30
- });
31
- }
32
- for (const tc of message.toolCalls) {
33
- input.push({
34
- type: "function_call",
35
- call_id: tc.id,
36
- name: tc.name,
37
- arguments: tc.arguments,
38
- });
39
- }
40
- continue;
41
- }
42
- if (message.role === "tool") {
43
- for (const part of message.contentParts ?? []) {
44
- if (part.type !== "tool_result")
45
- continue;
46
- input.push({
47
- type: "function_call_output",
48
- call_id: part.callId,
49
- output: part.output,
50
- });
51
- }
52
- continue;
53
- }
54
- input.push({
55
- role: message.role,
56
- content: this.buildMessageContent(message),
57
- });
58
- }
59
- // The volatile State turn is sent every turn (it changes each call and is
60
- // never "covered" by previous_response_id). Absent on un-rebuilt bindings,
61
- // where the state is already inside the covered/uncovered history.
62
- // Rendered through the same assistant-toolCalls / tool-result branches above
63
- // so tool_use blocks are not silently dropped.
64
- if (context.stateTurn) {
65
- const st = context.stateTurn;
66
- if (st.role === "assistant" && st.toolCalls?.length) {
67
- if (st.content || st.contentParts?.length) {
68
- input.push({ role: "assistant", content: this.buildMessageContent(st) });
69
- }
70
- for (const tc of st.toolCalls) {
71
- input.push({ type: "function_call", call_id: tc.id, name: tc.name, arguments: tc.arguments });
72
- }
73
- }
74
- else if (st.role === "tool") {
75
- for (const part of st.contentParts ?? []) {
76
- if (part.type !== "tool_result")
77
- continue;
78
- input.push({ type: "function_call_output", call_id: part.callId, output: part.output });
79
- }
80
- }
81
- else {
82
- input.push({ role: st.role, content: this.buildMessageContent(st) });
83
- }
84
- }
85
- return input;
86
- }
87
- decodeOutput(output) {
88
- let content = "";
89
- const toolCalls = [];
90
- for (const item of output) {
91
- if (item.type === "message") {
92
- for (const part of item.content ?? []) {
93
- if (part.type === "output_text")
94
- content += String(part.text ?? "");
95
- }
96
- }
97
- else if (item.type === "function_call") {
98
- const toolCall = normalizeToolCall(String(item.call_id ?? item.id ?? ""), String(item.name ?? ""), item.arguments ?? "{}");
99
- if (toolCall)
100
- toolCalls.push(toolCall);
101
- }
102
- }
103
- return { content, toolCalls };
104
- }
105
- buildMessageContent(message) {
106
- if (!message.contentParts?.length)
107
- return message.content;
108
- const content = [];
109
- for (const part of message.contentParts) {
110
- if (part.type === "text") {
111
- content.push({ type: "input_text", text: part.text });
112
- continue;
113
- }
114
- if (part.type === "image") {
115
- // Default the MIME type (like every other serializer) so a data-only image is
116
- // not silently dropped; only a part with neither url nor data yields undefined.
117
- const imageUrl = part.url ?? (part.data
118
- ? `data:${part.mediaType ?? "image/png"};base64,${part.data}`
119
- : undefined);
120
- if (imageUrl)
121
- content.push({
122
- type: "input_image",
123
- detail: part.detail ?? "auto",
124
- image_url: imageUrl,
125
- });
126
- continue;
127
- }
128
- if (part.type === "audio") {
129
- throw new UnsupportedModalityError("audio", "openai-responses");
130
- }
131
- }
132
- return content;
133
- }
134
- }
3
+ import { CircuitBreaker } from "./base.js";
4
+ import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
5
+ import { endpointProfiles } from "./endpoints.js";
6
+ import { OpenAIResponsesAdapter, } from "./openai-responses-adapter.js";
7
+ import { circuitOpenError, classifyProviderError } from "./provider-error.js";
8
+ export { OpenAIResponsesAdapter } from "./openai-responses-adapter.js";
9
+ const OFFICIAL_OPENAI_RESPONSES_BASE_URL = "https://api.openai.com/v1";
10
+ /** Params the official input-token count endpoint accepts (SDK `InputTokenCountParams`). The
11
+ * create plan is projected onto this set — remaining keys (max_output_tokens, store, …) cannot
12
+ * change the input token count — rather than maintaining a second serialization. */
13
+ const INPUT_TOKEN_COUNT_PARAM_KEYS = [
14
+ "conversation", "input", "instructions", "model", "parallel_tool_calls",
15
+ "previous_response_id", "reasoning", "text", "tool_choice", "tools", "truncation",
16
+ ];
135
17
  export class OpenAIResponsesProvider {
136
18
  model;
137
19
  client;
@@ -139,167 +21,185 @@ export class OpenAIResponsesProvider {
139
21
  maxRetries;
140
22
  baseDelay;
141
23
  responses = new OpenAIResponsesAdapter();
142
- constructor(apiKey, model = "gpt-4.1", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = "https://api.openai.com/v1") {
24
+ resolvedRuntimePolicy;
25
+ directNativeTokenCounting;
26
+ resolvedRuntime;
27
+ constructor(apiKey, model = "gpt-4.1", retry = { maxRetries: 3, baseDelay: 1000 }, baseURL = "https://api.openai.com/v1", runtimePolicy = {}, authMode = "api_key") {
143
28
  this.model = model;
144
- this.client = withServerRuntimeGuard(() => new OpenAI({ apiKey, baseURL }));
29
+ this.client = withServerRuntimeGuard(() => new OpenAI({
30
+ apiKey,
31
+ baseURL,
32
+ ...(authMode === "bearer" ? { defaultHeaders: { Authorization: `Bearer ${apiKey}` } } : {}),
33
+ }));
145
34
  this.circuit = new CircuitBreaker();
146
35
  this.maxRetries = retry.maxRetries;
147
36
  this.baseDelay = retry.baseDelay;
37
+ this.resolvedRuntimePolicy = runtimePolicy;
38
+ this.directNativeTokenCounting = baseURL.replace(/\/+$/, "") === OFFICIAL_OPENAI_RESPONSES_BASE_URL;
148
39
  }
149
40
  runtimePolicy() {
150
- const table = {
151
- "gpt-5.5": { maxTurns: 60 },
152
- "gpt-5.4": { maxTurns: 50 },
153
- "gpt-5.4-mini": { maxTurns: 25 },
154
- "gpt-5.4-nano": { maxTurns: 15 },
155
- "gpt-5.2": { maxTurns: 50 },
156
- "gpt-5.2-pro": { maxTurns: 60 },
157
- "gpt-5.1": { maxTurns: 50 },
158
- "gpt-4.1": { maxTurns: 35 },
159
- "gpt-4.1-mini": { maxTurns: 20 },
160
- "gpt-4.1-nano": { maxTurns: 15 },
161
- "gpt-5": { maxTurns: 50 },
162
- "gpt-5-pro": { maxTurns: 60 },
163
- "gpt-5-mini": { maxTurns: 25 },
164
- "gpt-5-nano": { maxTurns: 15 },
165
- "o3": { maxTurns: 50 },
166
- "o3-mini": { maxTurns: 25 },
167
- "o4-mini": { maxTurns: 25 },
168
- };
169
- return table[this.model] ?? {};
41
+ return this.resolvedRuntimePolicy;
42
+ }
43
+ bindResolvedRuntime(resolved) {
44
+ if (resolved.identity.protocol !== "openai-responses"
45
+ || resolved.identity.providerId !== "openai"
46
+ || resolved.identity.modelId !== this.model) {
47
+ throw new Error("OpenAIResponsesProvider received a mismatched resolved runtime");
48
+ }
49
+ this.resolvedRuntime = resolved;
170
50
  }
171
51
  createRunState() {
172
52
  return { coveredMessageCount: 0 };
173
53
  }
54
+ adapterInput(context, tools, extensions) {
55
+ const resolved = this.resolvedRuntime ?? {
56
+ identity: {
57
+ providerId: "openai",
58
+ modelId: this.model,
59
+ endpointId: "openai.responses",
60
+ protocol: "openai-responses",
61
+ },
62
+ model: {
63
+ id: `openai/${this.model}`,
64
+ providerId: "openai",
65
+ kind: "generation",
66
+ intrinsic: {},
67
+ },
68
+ endpoint: endpointProfiles["openai.responses"],
69
+ adapter: this,
70
+ effectiveCapabilities: compatibilityCapabilities(),
71
+ };
72
+ return normalizeCanonicalAdapterInput({ context, tools, resolved, extensions });
73
+ }
174
74
  async complete(context, tools, extensions) {
175
75
  if (this.circuit.isOpen())
176
- throw new Error("Circuit breaker open");
177
- let lastErr;
178
- for (let i = 0; i < this.maxRetries; i++) {
76
+ throw circuitOpenError("openai");
77
+ let input;
78
+ let plan;
79
+ try {
80
+ input = this.adapterInput(context, tools, extensions);
81
+ plan = this.responses.buildRequest(input);
82
+ }
83
+ catch (error) {
84
+ throw classifyProviderError("openai", error);
85
+ }
86
+ let lastError;
87
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
179
88
  try {
180
- const instructions = this.responses.buildInstructions(context);
181
- const resp = await this.client.responses.create({
182
- ...this.requestExtensions(extensions),
183
- model: this.model,
184
- input: this.responses.buildInput(context),
185
- ...(instructions ? { instructions } : {}),
186
- ...((t => t ? { tools: t } : {})(this.allTools(tools, extensions))),
187
- });
89
+ const response = await this.client.responses.create(plan.params);
188
90
  this.circuit.recordSuccess();
189
- const decoded = this.responses.decodeOutput(resp.output);
190
- return {
191
- role: "assistant",
192
- content: decoded.content,
193
- toolCalls: decoded.toolCalls,
194
- tokenCount: resp.usage?.output_tokens ?? resp.usage?.total_tokens,
195
- };
91
+ return this.responses.decodeComplete(response, { input }).message;
196
92
  }
197
- catch (err) {
198
- lastErr = err;
93
+ catch (error) {
94
+ lastError = error;
199
95
  this.circuit.recordFailure();
200
- if (i < this.maxRetries - 1)
201
- await new Promise(r => setTimeout(r, this.baseDelay * 2 ** i));
96
+ if (attempt < this.maxRetries - 1) {
97
+ await new Promise(resolve => setTimeout(resolve, this.baseDelay * 2 ** attempt));
98
+ }
202
99
  }
203
100
  }
204
- throw lastErr;
101
+ throw classifyProviderError("openai", lastError);
205
102
  }
206
- async *stream(context, tools, extensions, state) {
207
- const runState = this.asRunState(state);
208
- const functionCalls = new Map();
209
- const instructions = this.responses.buildInstructions(context);
210
- const stream = await this.client.responses.create({
211
- ...this.requestExtensions(extensions),
212
- model: this.model,
213
- input: this.responses.buildInput(context, runState),
214
- ...(instructions ? { instructions } : {}),
215
- ...(runState.previousResponseId ? { previous_response_id: runState.previousResponseId } : {}),
216
- ...((t => t ? { tools: t } : {})(this.allTools(tools, extensions))),
217
- stream: true,
218
- });
219
- for await (const evt of stream) {
220
- if (evt.type === "response.output_text.delta") {
221
- yield { type: "text_delta", delta: evt.delta };
222
- }
223
- else if (evt.type === "response.output_item.added" && evt.item.type === "function_call") {
224
- functionCalls.set(evt.output_index, {
225
- id: evt.item.call_id,
226
- name: evt.item.name,
227
- argsBuf: evt.item.arguments ?? "",
228
- });
229
- }
230
- else if (evt.type === "response.function_call_arguments.delta") {
231
- const call = functionCalls.get(evt.output_index);
232
- if (call)
233
- call.argsBuf += evt.delta;
234
- }
235
- else if (evt.type === "response.function_call_arguments.done") {
236
- const call = functionCalls.get(evt.output_index);
237
- if (call)
238
- call.argsBuf = evt.arguments;
239
- }
240
- else if (evt.type === "response.output_item.done" && evt.item.type === "function_call") {
241
- const call = functionCalls.get(evt.output_index) ?? {
242
- id: evt.item.call_id,
243
- name: evt.item.name,
244
- argsBuf: evt.item.arguments ?? "{}",
245
- };
246
- let args = {};
247
- try {
248
- args = JSON.parse(call.argsBuf || "{}");
249
- }
250
- catch {
251
- args = {};
103
+ /** spc_024-05: native preflight via the official Responses input-token count endpoint. Counts
104
+ * the exact create request plan (stateful `previous_response_id` continuation included)
105
+ * native measurement belongs to the verified official endpoint, not the wire protocol. */
106
+ async countTokens(context, tools, extensions, state) {
107
+ const enabled = this.resolvedRuntime
108
+ ? this.resolvedRuntime.effectiveCapabilities.nativeTokenCounting.state === "supported"
109
+ : this.directNativeTokenCounting;
110
+ const inputTokens = this.client.responses.inputTokens;
111
+ if (!enabled || typeof inputTokens?.count !== "function") {
112
+ throw new Error("Native token counting is unavailable on this OpenAI-compatible endpoint");
113
+ }
114
+ const input = this.adapterInput(context, tools, extensions);
115
+ const plan = this.responses.buildRequest(input, this.asRunState(state));
116
+ const body = Object.fromEntries(INPUT_TOKEN_COUNT_PARAM_KEYS
117
+ .filter(key => key in plan.params)
118
+ .map(key => [key, plan.params[key]]));
119
+ const response = await inputTokens.count(body);
120
+ return {
121
+ inputTokens: response.input_tokens,
122
+ source: { kind: "native", provider: "openai" },
123
+ confidence: "exact",
124
+ };
125
+ }
126
+ async *stream(context, tools, extensions, state, signal) {
127
+ try {
128
+ const runState = this.asRunState(state);
129
+ const input = this.adapterInput(context, tools, extensions);
130
+ const plan = this.responses.buildRequest(input, runState);
131
+ const streamState = this.responses.createStreamState({ input }, runState);
132
+ const stream = await this.client.responses.create({ ...plan.params, stream: true }, signal ? { signal } : undefined);
133
+ for await (const chunk of stream) {
134
+ const output = this.responses.pushStreamChunk(chunk, streamState);
135
+ for (const event of output.events)
136
+ yield event;
137
+ if (output.runStatePatch) {
138
+ Object.assign(runState, output.runStatePatch);
139
+ if (state)
140
+ Object.assign(state, output.runStatePatch);
252
141
  }
253
- yield { type: "tool_call", id: call.id, name: call.name, arguments: args };
254
142
  }
255
- else if (evt.type === "response.completed") {
256
- runState.previousResponseId = evt.response.id;
257
- runState.coveredMessageCount = context.turns.length + 1;
258
- if (evt.response.usage?.total_tokens) {
259
- // Responses API reports prompt-cache hits as input_tokens_details.cached_tokens,
260
- // a subset of input_tokens (the full prompt, kept for accounting).
261
- const cachedTokens = evt.response.usage.input_tokens_details?.cached_tokens ?? 0;
262
- yield {
263
- type: "usage",
264
- totalTokens: evt.response.usage.total_tokens,
265
- ...(evt.response.usage.input_tokens ? { inputTokens: evt.response.usage.input_tokens } : {}),
266
- ...(evt.response.usage.output_tokens ? { outputTokens: evt.response.usage.output_tokens } : {}),
267
- ...(cachedTokens > 0 ? { cacheReadInputTokens: cachedTokens } : {}),
268
- };
269
- }
143
+ const final = this.responses.finishStream(streamState);
144
+ for (const event of final.events)
145
+ yield event;
146
+ if (final.runStatePatch) {
147
+ Object.assign(runState, final.runStatePatch);
148
+ if (state)
149
+ Object.assign(state, final.runStatePatch);
270
150
  }
271
151
  }
152
+ catch (error) {
153
+ throw classifyProviderError("openai", error);
154
+ }
272
155
  }
273
- requestExtensions(extensions) {
274
- return omitExtensionKeys(extensions, [
275
- "model", "input", "instructions", "tools", "stream", "previous_response_id",
276
- "web_search", "builtin_tools",
277
- ]);
278
- }
279
- /** Responses API built-in server tools from extensions (live in the same tools[] as function tools):
280
- * `web_search: true` (or a config object), plus a `builtin_tools` list passed through verbatim for
281
- * file_search / code_interpreter. They run server-side; results return inline. Mirrors py. */
156
+ // White-box test seams. Protocol request shaping belongs to the adapter.
282
157
  builtinTools(extensions) {
283
- const ext = extensions ?? {};
284
- const out = [];
285
- const ws = ext.web_search;
286
- if (ws)
287
- out.push(typeof ws === "object" ? { type: "web_search", ...ws } : { type: "web_search" });
288
- if (Array.isArray(ext.builtin_tools))
289
- out.push(...ext.builtin_tools);
290
- return out;
158
+ return this.responses.builtinTools(extensions);
291
159
  }
292
- /** Function tools + built-in server tools merged into the wire tools[] (undefined when empty). */
293
- allTools(tools, extensions) {
294
- const fnTools = tools.length ? this.responses.buildTools(tools) : [];
295
- const all = [...fnTools, ...this.builtinTools(extensions)];
296
- return all.length ? all : undefined;
160
+ requestExtensions(extensions) {
161
+ return this.responses.requestExtensions(extensions);
297
162
  }
298
163
  asRunState(state) {
299
164
  if (!state)
300
165
  return this.createRunState();
301
- if (typeof state.coveredMessageCount !== "number")
302
- state.coveredMessageCount = 0;
303
- return state;
166
+ return {
167
+ ...state,
168
+ coveredMessageCount: typeof state.coveredMessageCount === "number"
169
+ ? state.coveredMessageCount
170
+ : 0,
171
+ };
304
172
  }
305
173
  }
174
+ function compatibilityCapabilities() {
175
+ const unknown = { state: "unknown", evidence: [] };
176
+ const unsupported = { state: "unsupported", evidence: ["protocol"] };
177
+ return {
178
+ inputModalities: {
179
+ text: unknown,
180
+ image: unknown,
181
+ audio: unsupported,
182
+ video: unsupported,
183
+ file: unknown,
184
+ },
185
+ outputModalities: {
186
+ text: unknown,
187
+ image: unsupported,
188
+ audio: unsupported,
189
+ embedding: unsupported,
190
+ },
191
+ tools: unknown,
192
+ reasoning: unknown,
193
+ parallelToolCalls: unknown,
194
+ structuredOutput: unknown,
195
+ promptCaching: unknown,
196
+ nativeTokenCounting: unknown,
197
+ mediaForms: {
198
+ imageUrl: unknown,
199
+ imageBase64: unknown,
200
+ fileId: unknown,
201
+ audioUrl: unsupported,
202
+ audioBase64: unsupported,
203
+ },
204
+ };
205
+ }
@@ -1,10 +1,10 @@
1
1
  import OpenAI from "openai";
2
- import type { Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
2
+ import type { LLMProvider, Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext, ReplayabilityAssessment, RuntimePolicy, StreamEvent, ToolSchema } from "../types.js";
3
3
  import { CircuitBreaker } from "./base.js";
4
+ import { type CanonicalAdapterInput } from "./content-normalization.js";
4
5
  import { OpenAIChatAdapter } from "./openai-chat.js";
5
- import type { ReplayabilityAssessment } from "./replay-validator.js";
6
- /** Options-object form for `OpenAIProvider` the recommended way to construct an OpenAI-compatible
7
- * provider (custom `baseURL` no longer needs a positional hole). */
6
+ import { type OpenAIChatTurnReasoning, type OpenAIChatWireDialect } from "./openai-chat-dialects.js";
7
+ /** Canonical constructor contract for OpenAI-chat providers. */
8
8
  export interface OpenAIProviderOptions {
9
9
  apiKey: string;
10
10
  model?: string;
@@ -12,23 +12,13 @@ export interface OpenAIProviderOptions {
12
12
  maxRetries: number;
13
13
  baseDelay: number;
14
14
  };
15
- /** Custom OpenAI-compatible endpoint (MiMo, DeepSeek, Kimi, …). Defaults to the OpenAI API. */
16
15
  baseURL?: string;
16
+ runtimePolicy?: RuntimePolicy;
17
+ dialect?: OpenAIChatWireDialect;
18
+ authMode?: "api_key" | "bearer";
17
19
  }
18
- /** Reasoning captured from a single model turn, handed to the replay-remember hooks so an
19
- * OpenAI-compatible subclass can persist whatever replay envelope its wire requires. */
20
- export interface OpenAIChatTurnReasoning {
21
- reasoningContent: string;
22
- reasoningDetails?: unknown;
23
- nativeToolCalls: unknown[];
24
- }
25
- /** Rebuild OpenAI-native `tool_calls` blocks from the streamed buffers — needed by reasoning
26
- * vendors (DeepSeek/MiniMax) that persist the native blocks in their replay envelope. */
27
- export declare function nativeToolCallsFromBuffers(toolCallBufs: Record<number, {
28
- id: string;
29
- name: string;
30
- argsBuf: string;
31
- }>): Array<Record<string, unknown>>;
20
+ export type { OpenAIChatTurnReasoning } from "./openai-chat-dialects.js";
21
+ type ResolvedOpenAIChatRuntime = CanonicalAdapterInput["resolved"];
32
22
  export declare class OpenAIChatProvider implements LLMProvider {
33
23
  protected client: OpenAI;
34
24
  protected circuit: CircuitBreaker;
@@ -36,72 +26,35 @@ export declare class OpenAIChatProvider implements LLMProvider {
36
26
  protected baseDelay: number;
37
27
  protected readonly model: string;
38
28
  protected readonly chat: OpenAIChatAdapter;
39
- constructor(apiKeyOrOptions: string | OpenAIProviderOptions, model?: string, retry?: {
40
- maxRetries: number;
41
- baseDelay: number;
42
- }, baseURL?: string);
29
+ protected readonly dialect: OpenAIChatWireDialect;
30
+ private readonly replayStore;
31
+ private readonly resolvedRuntimePolicy;
32
+ private resolvedRuntime?;
33
+ constructor(options: OpenAIProviderOptions);
43
34
  runtimePolicy(): RuntimePolicy;
44
35
  descriptor(): ProviderDescriptor;
45
- protected requireNonEmptyReasoningReplayForToolTurns(_extensions?: Record<string, unknown>): boolean;
46
- protected degradeMissingReasoningReplay(extensions?: Record<string, unknown>): boolean;
47
- protected buildChatMessages(context: RenderedContext, extensions?: Record<string, unknown>): OpenAI.Chat.Completions.ChatCompletionMessageParam[];
48
- /** Pre-process caller extensions before they reach buildChatMessages + the wire request
49
- * (e.g. set `__deepstrikeThinkingEnabled`). Default: pass through unchanged. */
50
- protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown> | undefined;
51
- /** Extra top-level request-body fields merged into the chat.completions call (vendor thinking
52
- * knobs like `reasoning_effort`, `extra_body`, `reasoning_split`). Default: none. */
53
- protected requestBodyExtras(_extensions?: Record<string, unknown>): Record<string, unknown>;
54
- /** Vendor server tools (e.g. web search) injected into the `tools[]` array alongside the function
55
- * tools, driven by caller `extensions`. These run server-side — the model invokes them and the
56
- * results come back inline, with no client tool-loop round-trip. Default: none. Vendors that ship
57
- * built-in tools (GLM web_search, ) override this and strip the consumed key in `prepareExtensions`
58
- * so it does not also leak into the request body. */
59
- protected serverTools(_extensions?: Record<string, unknown>): unknown[];
60
- /** Merge function tools + vendor server tools into the wire `tools[]` (undefined when empty). Server
61
- * tools (e.g. web_search) are non-standard wire entries, so the array is cast to the SDK tool type. */
62
- protected assembleTools(tools: ToolSchema[], extensions?: Record<string, unknown>): OpenAI.Chat.Completions.ChatCompletionTool[] | undefined;
63
- /** Request-body params controlling prompt caching. Default sends OpenAI's `prompt_cache_key`;
64
- * vendors whose endpoints reject unknown params (e.g. DeepSeek 400s) override to `{}`. */
65
- protected cacheKeyParams(context: RenderedContext, tools: ToolSchema[]): Record<string, unknown>;
66
- /** Whether streamed `content` may carry inline `<thinking>…</thinking>` tags to split out.
67
- * Default true (OpenAI). Reasoning vendors emit reasoning out-of-band, so they return false. */
68
- protected usesInlineThinkingTags(): boolean;
69
- /** Whether to surface streamed `reasoning_content` as thinking_delta events. Default true;
70
- * vendors gate this behind an `exposeReasoning` extension. */
71
- protected exposeReasoningDelta(_extensions?: Record<string, unknown>): boolean;
72
- /** Persist replay after a non-streaming turn. Default: nothing (plain OpenAI has no reasoning
73
- * to replay). Reasoning vendors override to store their envelope. */
36
+ bindResolvedRuntime(resolved: ResolvedOpenAIChatRuntime): void;
37
+ assessReplayability(context: RenderedContext, extensions?: Record<string, unknown>): ReplayabilityAssessment;
38
+ peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
39
+ seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
40
+ private rememberReplay;
41
+ private adapterInput;
42
+ complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
43
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
44
+ protected prepareExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
45
+ protected requestBodyExtras(extensions?: Record<string, unknown>): Record<string, unknown>;
46
+ protected serverTools(extensions?: Record<string, unknown>): unknown[];
47
+ protected requestExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
48
+ protected promptCacheKey(context: RenderedContext, tools: ToolSchema[]): string;
74
49
  protected rememberCompleteReplay(_content: string, _toolCalls: Array<{
75
50
  id: string;
76
51
  name: string;
77
52
  arguments: string;
78
53
  }>, _reasoning: OpenAIChatTurnReasoning): void;
79
- /** Persist replay after a streamed turn. Default: store `{ reasoning_content }` when there is a
80
- * tool-call turn or captured reasoning (the prior base behavior). Vendors override. */
81
- protected rememberStreamReplay(content: string, toolCalls: Array<{
54
+ protected rememberStreamReplay(_content: string, _toolCalls: Array<{
82
55
  id: string;
83
56
  name: string;
84
57
  arguments: string;
85
- }>, reasoning: OpenAIChatTurnReasoning): void;
86
- /**
87
- * Pre-flight query: would this history validate against this provider with the
88
- * given extensions, without sending the request? Lets an embedder route around
89
- * a reasoning-replay failure (keep thinking on, disable it, or skip this
90
- * candidate) before issuing the request. `ok: true` when this provider does
91
- * not require reasoning replay for the current extensions.
92
- */
93
- assessReplayability(context: RenderedContext, extensions?: Record<string, unknown>): ReplayabilityAssessment;
94
- peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
95
- seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
96
- complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
97
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
98
- /**
99
- * Default `prompt_cache_key` derived from the cacheable prefix (system prompt +
100
- * tool names) so requests for the same agent config route to the same cache.
101
- * A caller-supplied `prompt_cache_key` in extensions overrides it (it is spread
102
- * after this default). Unknown to non-OpenAI compatible endpoints, which ignore it.
103
- */
104
- protected promptCacheKey(context: RenderedContext, tools: ToolSchema[]): string;
105
- protected requestExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
58
+ }>, _reasoning: OpenAIChatTurnReasoning): void;
106
59
  }
107
60
  export { OpenAIChatProvider as OpenAIProvider };