@deepstrike/sdk 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 (148) 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/providers/anthropic-adapter.d.ts +47 -0
  39. package/dist/providers/anthropic-adapter.js +444 -0
  40. package/dist/providers/anthropic-compatible.d.ts +2 -3
  41. package/dist/providers/anthropic-compatible.js +8 -5
  42. package/dist/providers/anthropic.d.ts +19 -23
  43. package/dist/providers/anthropic.js +169 -395
  44. package/dist/providers/base.js +43 -3
  45. package/dist/providers/capability-router.d.ts +29 -0
  46. package/dist/providers/capability-router.js +43 -0
  47. package/dist/providers/catalog.d.ts +16 -4
  48. package/dist/providers/catalog.js +112 -21
  49. package/dist/providers/content-normalization.d.ts +57 -0
  50. package/dist/providers/content-normalization.js +238 -0
  51. package/dist/providers/content-policy.d.ts +16 -0
  52. package/dist/providers/content-policy.js +39 -0
  53. package/dist/providers/credentials.d.ts +83 -0
  54. package/dist/providers/credentials.js +190 -0
  55. package/dist/providers/endpoints.d.ts +137 -0
  56. package/dist/providers/endpoints.js +128 -0
  57. package/dist/providers/factories.js +9 -3
  58. package/dist/providers/gemini-adapter.d.ts +33 -0
  59. package/dist/providers/gemini-adapter.js +272 -0
  60. package/dist/providers/gemini.d.ts +16 -3
  61. package/dist/providers/gemini.js +97 -195
  62. package/dist/providers/model-catalog.d.ts +37 -0
  63. package/dist/providers/model-catalog.js +62 -0
  64. package/dist/providers/model-registry.d.ts +98 -0
  65. package/dist/providers/model-registry.js +315 -0
  66. package/dist/providers/ollama-adapter.d.ts +65 -0
  67. package/dist/providers/ollama-adapter.js +188 -0
  68. package/dist/providers/ollama.d.ts +9 -4
  69. package/dist/providers/ollama.js +96 -109
  70. package/dist/providers/openai-chat-dialects.d.ts +154 -0
  71. package/dist/providers/openai-chat-dialects.js +179 -0
  72. package/dist/providers/openai-chat.d.ts +46 -18
  73. package/dist/providers/openai-chat.js +418 -51
  74. package/dist/providers/openai-responses-adapter.d.ts +42 -0
  75. package/dist/providers/openai-responses-adapter.js +341 -0
  76. package/dist/providers/openai-responses.d.ts +14 -33
  77. package/dist/providers/openai-responses.js +131 -264
  78. package/dist/providers/openai.d.ts +29 -76
  79. package/dist/providers/openai.js +195 -292
  80. package/dist/providers/protocol-adapter.d.ts +34 -0
  81. package/dist/providers/protocol-adapter.js +9 -0
  82. package/dist/providers/protocol-capabilities.d.ts +34 -0
  83. package/dist/providers/protocol-capabilities.js +44 -0
  84. package/dist/providers/provider-error.d.ts +31 -0
  85. package/dist/providers/provider-error.js +149 -0
  86. package/dist/providers/public.d.ts +26 -3
  87. package/dist/providers/public.js +13 -1
  88. package/dist/providers/registry.d.ts +7 -6
  89. package/dist/providers/registry.js +47 -20
  90. package/dist/providers/request-plan.d.ts +86 -0
  91. package/dist/providers/request-plan.js +175 -0
  92. package/dist/providers/usage-normalizer.d.ts +48 -0
  93. package/dist/providers/usage-normalizer.js +96 -0
  94. package/dist/providers/vendor-profiles.d.ts +2 -15
  95. package/dist/providers/vendor-profiles.js +14 -60
  96. package/dist/runtime/canonical-kernel-step.d.ts +1 -2
  97. package/dist/runtime/canonical-kernel-step.js +47 -12
  98. package/dist/runtime/context-policy.d.ts +10 -12
  99. package/dist/runtime/context-policy.js +6 -8
  100. package/dist/runtime/durable-content.d.ts +50 -0
  101. package/dist/runtime/durable-content.js +159 -0
  102. package/dist/runtime/execution-plane.d.ts +2 -2
  103. package/dist/runtime/execution-plane.js +2 -2
  104. package/dist/runtime/kernel-event-log.js +0 -1
  105. package/dist/runtime/kernel-step.d.ts +0 -1
  106. package/dist/runtime/kernel-step.js +4 -2
  107. package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
  108. package/dist/runtime/mcp-proxy-plane.js +44 -6
  109. package/dist/runtime/output-schema.d.ts +1 -2
  110. package/dist/runtime/provider-replay.d.ts +1 -1
  111. package/dist/runtime/provider-replay.js +12 -27
  112. package/dist/runtime/reactive-session.d.ts +1 -1
  113. package/dist/runtime/reactive-session.js +2 -3
  114. package/dist/runtime/run-group.d.ts +1 -1
  115. package/dist/runtime/runner.d.ts +26 -40
  116. package/dist/runtime/runner.js +145 -63
  117. package/dist/runtime/session-log.d.ts +8 -1
  118. package/dist/runtime/session-log.js +42 -2
  119. package/dist/runtime/session-repair.d.ts +1 -1
  120. package/dist/runtime/session-repair.js +1 -1
  121. package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
  122. package/dist/runtime/sub-agent-orchestrator.js +8 -11
  123. package/dist/runtime/workflow-control-flow.d.ts +0 -4
  124. package/dist/runtime/workflow-control-flow.js +0 -16
  125. package/dist/session.d.ts +11 -0
  126. package/dist/session.js +1 -0
  127. package/dist/skill.d.ts +17 -0
  128. package/dist/skill.js +16 -0
  129. package/dist/skills/loader.d.ts +3 -0
  130. package/dist/tools/errors.d.ts +1 -3
  131. package/dist/tools/errors.js +1 -3
  132. package/dist/tools/index.d.ts +3 -0
  133. package/dist/types/agent.d.ts +21 -9
  134. package/dist/types/agent.js +30 -4
  135. package/dist/types.d.ts +128 -13
  136. package/package.json +3 -3
  137. package/dist/providers/deepseek.d.ts +0 -46
  138. package/dist/providers/deepseek.js +0 -97
  139. package/dist/providers/glm.d.ts +0 -25
  140. package/dist/providers/glm.js +0 -48
  141. package/dist/providers/kimi.d.ts +0 -23
  142. package/dist/providers/kimi.js +0 -30
  143. package/dist/providers/minimax.d.ts +0 -49
  144. package/dist/providers/minimax.js +0 -98
  145. package/dist/providers/profiles.d.ts +0 -1992
  146. package/dist/providers/profiles.js +0 -796
  147. package/dist/providers/qwen.d.ts +0 -38
  148. package/dist/providers/qwen.js +0 -97
@@ -1,12 +1,404 @@
1
1
  import { assistantReplayKey } from "../runtime/provider-replay.js";
2
- import { normalizeToolCall, toOpenAIMessageParams } from "./base.js";
2
+ import { openAICachedPromptTokens, stablePromptCacheKey, ThinkingTagStreamExtractor, } from "./base.js";
3
+ import { normalizeCanonicalContext, projectToolOutputToText } from "./content-normalization.js";
4
+ import { normalizeToolCall } from "./base.js";
3
5
  import { DEGRADED_REASONING_PLACEHOLDER, assessReasoningReplay, validateOpenAIChatReplay, } from "./replay-validator.js";
6
+ import { ProtocolResponseError, } from "./protocol-adapter.js";
7
+ import { OPENAI_CHAT_PROTOCOL_CAPABILITIES } from "./protocol-capabilities.js";
8
+ import { openAIChatDialects, replayForTurn, } from "./openai-chat-dialects.js";
9
+ const COMPATIBILITY_REPLAY = new WeakMap();
10
+ function compatibilityReplayStore(adapter) {
11
+ let store = COMPATIBILITY_REPLAY.get(adapter);
12
+ if (!store) {
13
+ store = new Map();
14
+ COMPATIBILITY_REPLAY.set(adapter, store);
15
+ }
16
+ return store;
17
+ }
18
+ function wireReplay(replay) {
19
+ if (!replay)
20
+ return undefined;
21
+ const fields = {};
22
+ if (typeof replay.reasoning_content === "string")
23
+ fields.reasoning_content = replay.reasoning_content;
24
+ if (replay.reasoning_details !== undefined)
25
+ fields.reasoning_details = replay.reasoning_details;
26
+ return Object.keys(fields).length ? fields : undefined;
27
+ }
28
+ function blockContent(message) {
29
+ const content = [];
30
+ for (const block of message.blocks) {
31
+ if (block.type === "tool_result")
32
+ continue;
33
+ if (block.type === "text")
34
+ content.push({ type: "text", text: block.text });
35
+ else if (block.type === "image") {
36
+ const url = block.source.kind === "url"
37
+ ? block.source.url
38
+ : block.source.kind === "base64"
39
+ ? `data:${block.mediaType ?? "image/png"};base64,${block.source.data}`
40
+ : undefined;
41
+ if (url)
42
+ content.push({
43
+ type: "image_url",
44
+ image_url: {
45
+ url,
46
+ ...(block.providerOptions?.openai_detail
47
+ ? { detail: block.providerOptions.openai_detail }
48
+ : {}),
49
+ },
50
+ });
51
+ }
52
+ else if (block.type === "audio" && block.source.kind === "base64") {
53
+ const subtype = (block.mediaType ?? "audio/wav").split("/")[1] ?? "wav";
54
+ content.push({
55
+ type: "input_audio",
56
+ input_audio: {
57
+ data: block.source.data,
58
+ format: subtype === "mpeg" ? "mp3" : subtype,
59
+ },
60
+ });
61
+ }
62
+ }
63
+ return message.contentForm !== "blocks" && content.length === 1 && content[0].type === "text"
64
+ ? String(content[0].text ?? "")
65
+ : content;
66
+ }
67
+ function validateCanonicalReplay(input, dialect, prepared) {
68
+ let pending;
69
+ let completed = new Set();
70
+ const assertComplete = () => {
71
+ const missing = pending ? [...pending].filter(id => !completed.has(id)) : [];
72
+ if (missing.length)
73
+ throw new Error(`OpenAI-compatible replay has assistant tool_calls with no tool result for ${missing.join(", ")}`);
74
+ };
75
+ for (const message of input.context.turns) {
76
+ if (message.role === "assistant") {
77
+ assertComplete();
78
+ pending = message.toolCalls?.length ? new Set(message.toolCalls.map(call => call.id)) : undefined;
79
+ completed = new Set();
80
+ }
81
+ else if (message.role === "tool") {
82
+ for (const block of message.blocks) {
83
+ if (block.type !== "tool_result")
84
+ continue;
85
+ if (!pending?.has(block.callId))
86
+ throw new Error(`OpenAI-compatible replay has orphan tool result ${block.callId}`);
87
+ if (completed.has(block.callId))
88
+ throw new Error(`OpenAI-compatible replay has duplicate tool result ${block.callId}`);
89
+ completed.add(block.callId);
90
+ }
91
+ }
92
+ else {
93
+ assertComplete();
94
+ pending = undefined;
95
+ completed = new Set();
96
+ }
97
+ }
98
+ assertComplete();
99
+ if (dialect.requireReasoningReplay(prepared) && prepared.degradeMissingReasoningReplay !== true) {
100
+ const missing = input.context.turns.flatMap(message => message.role === "assistant" && message.toolCalls?.length
101
+ && !(typeof message.providerReplay?.reasoning_content === "string" && message.providerReplay.reasoning_content.trim())
102
+ ? message.toolCalls.map(call => call.id)
103
+ : []);
104
+ if (missing.length) {
105
+ throw new Error(`${dialect.providerId}/${input.resolved.identity.modelId} replay requires non-empty reasoning_content for assistant tool call turn ${missing.join(", ")}`);
106
+ }
107
+ }
108
+ }
109
+ function messages(input, dialect, prepared) {
110
+ validateCanonicalReplay(input, dialect, prepared);
111
+ const output = [];
112
+ if (input.context.systemText)
113
+ output.push({ role: "system", content: input.context.systemText });
114
+ const turns = input.context.stateTurn
115
+ ? [...input.context.turns, input.context.stateTurn]
116
+ : input.context.turns;
117
+ for (const message of turns) {
118
+ if (message.role === "tool") {
119
+ for (const block of message.blocks) {
120
+ if (block.type !== "tool_result")
121
+ continue;
122
+ output.push({
123
+ role: "tool",
124
+ tool_call_id: block.callId,
125
+ content: projectToolOutputToText(block.blocks),
126
+ });
127
+ }
128
+ continue;
129
+ }
130
+ const next = { role: message.role, content: blockContent(message) };
131
+ if (message.role === "assistant" && message.toolCalls?.length) {
132
+ next.tool_calls = message.toolCalls.map(call => ({
133
+ id: call.id,
134
+ type: "function",
135
+ function: { name: call.name, arguments: call.arguments },
136
+ }));
137
+ const replay = wireReplay(message.providerReplay)
138
+ ?? (dialect.requireReasoningReplay(prepared) && prepared.degradeMissingReasoningReplay === true
139
+ ? { reasoning_content: DEGRADED_REASONING_PLACEHOLDER }
140
+ : undefined);
141
+ if (replay)
142
+ Object.assign(next, replay);
143
+ }
144
+ output.push(next);
145
+ }
146
+ return output;
147
+ }
148
+ function nativeToolCalls(buffers) {
149
+ return Object.values(buffers).map(call => ({
150
+ id: call.id,
151
+ type: "function",
152
+ function: { name: call.name, arguments: call.argsBuffer || "{}" },
153
+ }));
154
+ }
155
+ function finalToolCalls(state) {
156
+ return Object.values(state.toolCallBuffers).map(call => ({
157
+ id: call.id,
158
+ name: call.name,
159
+ arguments: call.argsBuffer || "{}",
160
+ }));
161
+ }
162
+ function streamReplay(state) {
163
+ return replayForTurn(state.dialect, "stream", state.input.resolved.identity.modelId, state.accumulatedContent, finalToolCalls(state), {
164
+ reasoningContent: state.accumulatedReasoning,
165
+ reasoningDetails: state.accumulatedReasoningDetails,
166
+ nativeToolCalls: nativeToolCalls(state.toolCallBuffers),
167
+ });
168
+ }
169
+ function pendingToolEvents(state) {
170
+ const events = [];
171
+ for (const [rawIndex, call] of Object.entries(state.toolCallBuffers)) {
172
+ const index = Number(rawIndex);
173
+ if (state.emittedToolCallIndexes.has(index))
174
+ continue;
175
+ let args = {};
176
+ try {
177
+ args = JSON.parse(call.argsBuffer || "{}");
178
+ }
179
+ catch {
180
+ args = {};
181
+ }
182
+ state.emittedToolCallIndexes.add(index);
183
+ events.push({ type: "tool_call", id: call.id, name: call.name, arguments: args });
184
+ }
185
+ return events;
186
+ }
187
+ function numberField(raw, key) {
188
+ const value = raw[key];
189
+ if (value === undefined || value === null)
190
+ return undefined;
191
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
192
+ throw new ProtocolResponseError("openai-chat", `usage.${key} must be a non-negative finite number`);
193
+ }
194
+ return value;
195
+ }
4
196
  export class OpenAIChatAdapter {
5
- replayFields = new Map();
197
+ // Responses are decoded from the official Chat Completions message/chunk shapes. The adapter
198
+ // preserves pre-A-09 raw finish reasons; canonical stop mapping is exposed separately.
199
+ // Source: https://platform.openai.com/docs/api-reference/chat/create
200
+ protocol = "openai-chat";
201
+ protocolCapabilities = OPENAI_CHAT_PROTOCOL_CAPABILITIES;
202
+ buildRequest(input, dialect = openAIChatDialects.openai) {
203
+ const prepared = dialect.prepareExtensions(input.extensions);
204
+ const tools = [
205
+ ...this.buildTools(input.tools),
206
+ ...(dialect.serverTools?.(input.extensions) ?? []),
207
+ ];
208
+ const cache = dialect.cacheKey === "openai"
209
+ ? { prompt_cache_key: stablePromptCacheKey([
210
+ input.context.systemText,
211
+ input.tools.map(tool => tool.name).join(","),
212
+ ]) }
213
+ : {};
214
+ return {
215
+ dialect,
216
+ preparedExtensions: prepared,
217
+ params: {
218
+ ...cache,
219
+ ...Object.fromEntries(Object.entries(prepared).filter(([key]) => key !== "__deepstrikeThinkingEnabled" && key !== "degradeMissingReasoningReplay")),
220
+ model: input.resolved.identity.modelId,
221
+ messages: messages(input, dialect, prepared),
222
+ ...(tools.length ? { tools } : {}),
223
+ },
224
+ };
225
+ }
226
+ decodeComplete(raw, input, dialect = openAIChatDialects.openai) {
227
+ const choice = raw.choices?.[0]?.message ?? {};
228
+ const nativeCalls = choice.tool_calls ?? [];
229
+ const toolCalls = this.normalizeToolCalls(nativeCalls);
230
+ const content = choice.content ?? "";
231
+ const usage = raw.usage;
232
+ if (usage)
233
+ this.normalizeUsage(usage);
234
+ const message = {
235
+ role: "assistant",
236
+ content,
237
+ ...(usage ? {
238
+ tokenCount: numberField(usage, "completion_tokens") ?? numberField(usage, "total_tokens"),
239
+ } : {}),
240
+ toolCalls,
241
+ };
242
+ const replay = replayForTurn(dialect, "complete", input.input.resolved.identity.modelId, content, toolCalls, {
243
+ reasoningContent: typeof choice.reasoning_content === "string" ? choice.reasoning_content : "",
244
+ reasoningDetails: choice.reasoning_details,
245
+ nativeToolCalls: nativeCalls,
246
+ });
247
+ return { message, ...(replay ? { replay } : {}) };
248
+ }
249
+ createStreamState(input, dialect = openAIChatDialects.openai) {
250
+ return {
251
+ input: input.input,
252
+ dialect,
253
+ toolCallBuffers: {},
254
+ emittedToolCallIndexes: new Set(),
255
+ extractor: new ThinkingTagStreamExtractor(),
256
+ accumulatedReasoning: "",
257
+ accumulatedContent: "",
258
+ totalTokens: 0,
259
+ inputTokens: 0,
260
+ outputTokens: 0,
261
+ cacheReadTokens: 0,
262
+ };
263
+ }
264
+ pushStreamChunk(chunk, state) {
265
+ if (chunk.usage) {
266
+ state.totalTokens = chunk.usage.total_tokens;
267
+ state.inputTokens = chunk.usage.prompt_tokens ?? 0;
268
+ state.outputTokens = chunk.usage.completion_tokens ?? 0;
269
+ state.cacheReadTokens = openAICachedPromptTokens(chunk.usage);
270
+ state.rawUsage = chunk.usage;
271
+ return { events: [] };
272
+ }
273
+ const choice = chunk.choices?.[0];
274
+ if (!choice)
275
+ return { events: [] };
276
+ if (choice.finish_reason)
277
+ state.finishReason = choice.finish_reason;
278
+ const delta = choice.delta;
279
+ if (!delta)
280
+ return { events: [] };
281
+ const events = [];
282
+ if (delta.reasoning_content) {
283
+ state.accumulatedReasoning += String(delta.reasoning_content);
284
+ if (state.dialect.exposeReasoning(state.input.extensions)) {
285
+ events.push({ type: "thinking_delta", delta: String(delta.reasoning_content) });
286
+ }
287
+ }
288
+ if (delta.reasoning_details !== undefined && delta.reasoning_details !== null) {
289
+ state.accumulatedReasoningDetails = delta.reasoning_details;
290
+ }
291
+ if (delta.content) {
292
+ if (state.dialect.inlineThinkingTags) {
293
+ for (const part of state.extractor.feed(String(delta.content))) {
294
+ if (part.type === "thinking") {
295
+ state.accumulatedReasoning += part.content;
296
+ events.push({ type: "thinking_delta", delta: part.content });
297
+ }
298
+ else {
299
+ state.accumulatedContent += part.content;
300
+ events.push({ type: "text_delta", delta: part.content });
301
+ }
302
+ }
303
+ }
304
+ else {
305
+ state.accumulatedContent += String(delta.content);
306
+ events.push({ type: "text_delta", delta: String(delta.content) });
307
+ }
308
+ }
309
+ for (const call of delta.tool_calls ?? []) {
310
+ const index = call.index;
311
+ if (!state.toolCallBuffers[index]) {
312
+ state.toolCallBuffers[index] = { id: call.id ?? "", name: "", argsBuffer: "" };
313
+ }
314
+ if (call.function?.name)
315
+ state.toolCallBuffers[index].name += call.function.name;
316
+ state.toolCallBuffers[index].argsBuffer += call.function?.arguments ?? "";
317
+ }
318
+ if (choice.finish_reason === "tool_calls") {
319
+ events.push(...pendingToolEvents(state));
320
+ const replay = streamReplay(state);
321
+ return { events, ...(replay ? { replay } : {}) };
322
+ }
323
+ return { events };
324
+ }
325
+ finishStream(state) {
326
+ const events = [];
327
+ if (state.dialect.inlineThinkingTags) {
328
+ for (const part of state.extractor.flush()) {
329
+ if (part.type === "thinking") {
330
+ state.accumulatedReasoning += part.content;
331
+ events.push({ type: "thinking_delta", delta: part.content });
332
+ }
333
+ else {
334
+ state.accumulatedContent += part.content;
335
+ events.push({ type: "text_delta", delta: part.content });
336
+ }
337
+ }
338
+ }
339
+ events.push(...pendingToolEvents(state));
340
+ if (state.totalTokens > 0) {
341
+ const providerUsage = this.normalizeUsage(state.rawUsage);
342
+ const stopReason = this.normalizeStopReason(state.finishReason);
343
+ events.push({
344
+ type: "usage",
345
+ totalTokens: state.totalTokens,
346
+ inputTokens: state.inputTokens,
347
+ outputTokens: state.outputTokens,
348
+ ...(state.cacheReadTokens > 0 ? { cacheReadInputTokens: state.cacheReadTokens } : {}),
349
+ ...(stopReason ? { stopReason } : {}),
350
+ ...(state.finishReason ? { rawStopReason: state.finishReason } : {}),
351
+ ...(providerUsage ? { providerUsage } : {}),
352
+ });
353
+ }
354
+ const replay = streamReplay(state);
355
+ return { events, ...(replay ? { replay } : {}) };
356
+ }
357
+ normalizeUsage(raw) {
358
+ if (raw === undefined || raw === null)
359
+ return undefined;
360
+ if (typeof raw !== "object" || Array.isArray(raw)) {
361
+ throw new ProtocolResponseError("openai-chat", "usage must be an object");
362
+ }
363
+ const usage = raw;
364
+ const inputTokens = numberField(usage, "prompt_tokens");
365
+ const outputTokens = numberField(usage, "completion_tokens");
366
+ numberField(usage, "total_tokens");
367
+ if (inputTokens === undefined && outputTokens === undefined)
368
+ return undefined;
369
+ const cacheReadInputTokens = openAICachedPromptTokens(usage);
370
+ const details = usage.completion_tokens_details;
371
+ const reasoningTokens = details && typeof details === "object"
372
+ ? numberField(details, "reasoning_tokens")
373
+ : undefined;
374
+ return {
375
+ inputTokens: inputTokens ?? 0,
376
+ outputTokens: outputTokens ?? 0,
377
+ ...(cacheReadInputTokens > 0 ? { cacheReadInputTokens } : {}),
378
+ ...(reasoningTokens !== undefined ? { reasoningTokens } : {}),
379
+ };
380
+ }
381
+ normalizeStopReason(raw) {
382
+ if (raw === undefined)
383
+ return undefined;
384
+ if (raw === "length")
385
+ return "max_tokens";
386
+ if (raw === "stop")
387
+ return "end_turn";
388
+ if (raw === "tool_calls" || raw === "function_call")
389
+ return "tool_use";
390
+ if (raw === "content_filter")
391
+ return "content_filter";
392
+ return "other";
393
+ }
6
394
  buildTools(tools) {
7
- return tools.map(t => ({
395
+ return tools.map(tool => ({
8
396
  type: "function",
9
- function: { name: t.name, description: t.description, parameters: JSON.parse(t.parameters) },
397
+ function: {
398
+ name: tool.name,
399
+ description: tool.description,
400
+ parameters: JSON.parse(tool.parameters),
401
+ },
10
402
  }));
11
403
  }
12
404
  buildMessages(context, options = {}) {
@@ -14,64 +406,39 @@ export class OpenAIChatAdapter {
14
406
  descriptor: options.descriptor,
15
407
  requireNonEmptyReasoningForToolCalls: options.requireNonEmptyReasoningForToolCalls,
16
408
  degradeMissingReasoning: options.degradeMissingReasoning,
17
- replayForAssistant: message => this.replayFields.get(assistantReplayKey(message)),
409
+ replayForAssistant: message => compatibilityReplayStore(this).get(assistantReplayKey(message)),
18
410
  });
19
- const degradeReasoning = Boolean(options.requireNonEmptyReasoningForToolCalls && options.degradeMissingReasoning);
20
- // toOpenAIMessageParams emits [system?, ...history, stateTurn?] — the State
21
- // turn is APPENDED last, so the cursor only skips the system message to align
22
- // with context.turns (history) when writing reasoning-replay fields.
23
- const serialized = toOpenAIMessageParams(context);
24
- let cursor = context.systemText ? 1 : 0;
25
- for (const source of context.turns) {
26
- if (source.role === "tool") {
27
- cursor += (source.contentParts ?? []).filter(p => p.type === "tool_result").length;
28
- continue;
29
- }
30
- if (source.role === "assistant") {
31
- const replay = this.replayFields.get(assistantReplayKey(source));
32
- let wireReplay = openAIChatWireReplayFields(replay);
33
- if (!wireReplay && degradeReasoning && source.toolCalls?.length) {
34
- // Reasoning-requiring provider, no stored reasoning for this tool-call
35
- // turn, caller opted into degradation: inject a placeholder so the
36
- // wire message stays well-formed instead of failing the whole request.
37
- wireReplay = { reasoning_content: DEGRADED_REASONING_PLACEHOLDER };
38
- }
39
- if (wireReplay)
40
- serialized[cursor] = { ...serialized[cursor], ...wireReplay };
41
- }
42
- cursor += 1;
43
- }
44
- return serialized;
411
+ const canonical = normalizeCanonicalContext(context, message => compatibilityReplayStore(this).get(assistantReplayKey(message)));
412
+ const dialect = {
413
+ ...openAIChatDialects.openai,
414
+ descriptor: { reasoning: options.descriptor?.reasoning ?? openAIChatDialects.openai.descriptor.reasoning },
415
+ requireReasoningReplay: () => options.requireNonEmptyReasoningForToolCalls === true,
416
+ };
417
+ return messages({
418
+ context: canonical,
419
+ tools: [],
420
+ resolved: { identity: { modelId: options.descriptor?.model ?? "compat" } },
421
+ extensions: options.degradeMissingReasoning ? { degradeMissingReasoningReplay: true } : {},
422
+ }, dialect, options.degradeMissingReasoning ? { degradeMissingReasoningReplay: true } : {});
45
423
  }
46
- /**
47
- * Throw-free pre-flight check: which assistant tool-call turns in `context`
48
- * lack the non-empty reasoning replay a reasoning-requiring provider needs.
49
- */
50
424
  assessReasoning(context) {
51
425
  return assessReasoningReplay(context.turns, {
52
- replayForAssistant: message => this.replayFields.get(assistantReplayKey(message)),
426
+ replayForAssistant: message => compatibilityReplayStore(this).get(assistantReplayKey(message)),
53
427
  });
54
428
  }
55
429
  normalizeToolCalls(toolCalls = []) {
56
430
  return toolCalls
57
- .filter((tc) => tc.type === "function")
58
- .map(tc => normalizeToolCall(tc.id, tc.function.name, tc.function.arguments))
59
- .filter(Boolean);
431
+ .filter((call) => call.type === "function")
432
+ .map(call => normalizeToolCall(call.id, call.function.name, call.function.arguments))
433
+ .filter((call) => call !== null);
60
434
  }
61
435
  rememberReplayFields(message, fields) {
62
- this.replayFields.set(assistantReplayKey(message), fields);
436
+ compatibilityReplayStore(this).set(assistantReplayKey(message), {
437
+ protocol: "openai-chat",
438
+ ...fields,
439
+ });
63
440
  }
64
441
  peekReplayFields(message) {
65
- return this.replayFields.get(assistantReplayKey(message));
442
+ return compatibilityReplayStore(this).get(assistantReplayKey(message));
66
443
  }
67
444
  }
68
- function openAIChatWireReplayFields(replay) {
69
- if (!replay)
70
- return undefined;
71
- const fields = {};
72
- if (typeof replay.reasoning_content === "string")
73
- fields.reasoning_content = replay.reasoning_content;
74
- if (replay.reasoning_details !== undefined)
75
- fields.reasoning_details = replay.reasoning_details;
76
- return Object.keys(fields).length ? fields : undefined;
77
- }
@@ -0,0 +1,42 @@
1
+ import type { Message, ProviderUsage, RenderedContext, ToolCall, ToolSchema } from "../types.js";
2
+ import type { CanonicalAdapterInput } from "./content-normalization.js";
3
+ import { type AdapterDecodeInput, type AdapterOutput, type AdapterStreamInput, type CanonicalStopReason, type ProtocolAdapter } from "./protocol-adapter.js";
4
+ export interface OpenAIResponsesRunState {
5
+ previousResponseId?: string;
6
+ coveredMessageCount: number;
7
+ [key: string]: unknown;
8
+ }
9
+ export interface OpenAIResponsesRequestPlan {
10
+ params: Record<string, unknown>;
11
+ }
12
+ export type OpenAIResponsesStreamChunk = Record<string, any>;
13
+ export interface OpenAIResponsesStreamState {
14
+ readonly input: CanonicalAdapterInput;
15
+ readonly functionCalls: Map<number, {
16
+ id: string;
17
+ name: string;
18
+ argsBuffer: string;
19
+ }>;
20
+ }
21
+ export declare class OpenAIResponsesAdapter implements ProtocolAdapter<OpenAIResponsesRequestPlan, Record<string, any>, OpenAIResponsesStreamChunk, OpenAIResponsesStreamState, undefined> {
22
+ readonly protocol: "openai-responses";
23
+ readonly protocolCapabilities: import("./protocol-capabilities.js").ProtocolRuntimeCapabilities;
24
+ buildRequest(input: CanonicalAdapterInput, state?: OpenAIResponsesRunState): OpenAIResponsesRequestPlan;
25
+ decodeComplete(raw: Record<string, any>, _input: AdapterDecodeInput): {
26
+ message: Message;
27
+ };
28
+ createStreamState(input: AdapterStreamInput, _state?: OpenAIResponsesRunState): OpenAIResponsesStreamState;
29
+ pushStreamChunk(chunk: OpenAIResponsesStreamChunk, state: OpenAIResponsesStreamState): AdapterOutput;
30
+ finishStream(_state: OpenAIResponsesStreamState): AdapterOutput;
31
+ normalizeUsage(raw: unknown): ProviderUsage | undefined;
32
+ normalizeStopReason(raw: string | undefined): CanonicalStopReason | undefined;
33
+ buildTools(tools: readonly ToolSchema[]): Array<Record<string, unknown>>;
34
+ buildInstructions(context: RenderedContext): string | undefined;
35
+ buildInput(context: RenderedContext, state?: OpenAIResponsesRunState): Array<Record<string, unknown>>;
36
+ decodeOutput(output: Array<Record<string, unknown>>): {
37
+ content: string;
38
+ toolCalls: ToolCall[];
39
+ };
40
+ builtinTools(extensions?: Record<string, unknown>): Record<string, unknown>[];
41
+ requestExtensions(extensions?: Record<string, unknown>): Record<string, unknown>;
42
+ }