praxis-agent 0.53.1 → 0.55.0

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.
@@ -0,0 +1,742 @@
1
+ import { ModelProviderError, malformedModelToolCall, } from '../core/runtime.js';
2
+ const DEFAULT_MAX_STREAM = 1024 * 1024;
3
+ const DEFAULT_MAX_ARGUMENTS = 1024 * 1024;
4
+ const DEFAULT_MAX_CALLS = 32;
5
+ const DEFAULT_MAX_METADATA = 1024 * 1024;
6
+ const DEFAULT_MAX_REASONING = 1024 * 1024;
7
+ function isRecord(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
9
+ }
10
+ function positiveInteger(value, fallback) {
11
+ if (value === undefined)
12
+ return fallback;
13
+ if (!Number.isSafeInteger(value) || value <= 0)
14
+ throw new Error('Responses codec limits must be positive integers');
15
+ return value;
16
+ }
17
+ function bytes(value) {
18
+ return Buffer.byteLength(value, 'utf8');
19
+ }
20
+ function invalid(message) {
21
+ return new ModelProviderError(message, {
22
+ kind: 'invalid_request',
23
+ retryable: false,
24
+ });
25
+ }
26
+ function providerError(kind, retryable, status) {
27
+ return new ModelProviderError(status === undefined
28
+ ? 'Responses provider request failed'
29
+ : `Responses provider request failed with HTTP ${status}`, { kind, retryable, ...(status === undefined ? {} : { status }) });
30
+ }
31
+ function dataUrl(image) {
32
+ return `data:${image.mediaType};base64,${image.data}`;
33
+ }
34
+ function appendMedia(blocks, legacy, role) {
35
+ const output = [];
36
+ const seenImages = new Set();
37
+ const addImage = (image) => {
38
+ const url = dataUrl(image);
39
+ if (seenImages.has(url))
40
+ return;
41
+ seenImages.add(url);
42
+ output.push({ type: 'input_image', image_url: url });
43
+ };
44
+ let hasDocument = false;
45
+ for (const block of blocks ?? []) {
46
+ if (block.type === 'text') {
47
+ if (block.text.length > 0)
48
+ output.push({ type: 'input_text', text: block.text });
49
+ }
50
+ else if (block.type === 'image') {
51
+ addImage(block);
52
+ }
53
+ else {
54
+ hasDocument = true;
55
+ }
56
+ }
57
+ if (hasDocument)
58
+ throw invalid(`Codex subscription provider does not support ${role} documents`);
59
+ for (const image of legacy ?? [])
60
+ addImage(image);
61
+ return output;
62
+ }
63
+ function userItem(message) {
64
+ const content = appendMedia(message.contentBlocks, message.images, 'user');
65
+ if (content.length === 0 && message.content.length > 0)
66
+ content.push({ type: 'input_text', text: message.content });
67
+ return { role: 'user', content };
68
+ }
69
+ function serializeMessages(messages) {
70
+ const instructions = [];
71
+ const input = [];
72
+ for (const message of messages) {
73
+ if (message.role === 'system') {
74
+ instructions.push(message.content);
75
+ continue;
76
+ }
77
+ if (message.role === 'user') {
78
+ input.push(userItem(message));
79
+ continue;
80
+ }
81
+ if (message.role === 'assistant') {
82
+ for (const block of message.thinkingBlocks ?? []) {
83
+ if (block.type !== 'thinking' || block.signature.length === 0)
84
+ throw invalid('Codex subscription provider cannot replay unsigned thinking');
85
+ input.push({
86
+ type: 'reasoning',
87
+ summary: [{ type: 'summary_text', text: block.thinking }],
88
+ encrypted_content: block.signature,
89
+ });
90
+ }
91
+ if (message.content.length > 0)
92
+ input.push({
93
+ role: 'assistant',
94
+ content: [{ type: 'output_text', text: message.content }],
95
+ });
96
+ for (const call of message.toolCalls ?? []) {
97
+ input.push({
98
+ type: 'function_call',
99
+ call_id: call.id,
100
+ name: call.name,
101
+ arguments: JSON.stringify(call.input),
102
+ });
103
+ }
104
+ continue;
105
+ }
106
+ if (message.documents?.length)
107
+ throw invalid('Codex subscription provider does not support tool documents');
108
+ const content = appendMedia(message.contentBlocks, message.images, 'tool');
109
+ const output = message.contentBlocks
110
+ ? message.contentBlocks
111
+ .filter((block) => block.type === 'text')
112
+ .map((block) => block.text)
113
+ .join('')
114
+ : message.content;
115
+ input.push({
116
+ type: 'function_call_output',
117
+ call_id: message.toolCallId,
118
+ output,
119
+ });
120
+ const images = content.filter((block) => block.type === 'input_image');
121
+ if (images.length > 0)
122
+ input.push({ role: 'user', content: images });
123
+ }
124
+ return { instructions: instructions.join('\n\n'), input };
125
+ }
126
+ function serializeResponsesRequest(request, model, configuredThinking) {
127
+ if (request.webSearch !== undefined)
128
+ throw invalid('Codex subscription provider does not support web search');
129
+ if (request.betas !== undefined)
130
+ throw invalid('Codex subscription provider does not support beta features');
131
+ const thinking = request.thinking ?? configuredThinking;
132
+ if (thinking !== undefined &&
133
+ thinking.mode !== 'disabled' &&
134
+ thinking.mode !== 'enabled' &&
135
+ thinking.mode !== 'adaptive')
136
+ throw invalid('Codex subscription provider received an invalid thinking mode');
137
+ if (thinking?.maxTokens !== undefined)
138
+ throw invalid('Codex subscription provider does not support thinking token budgets');
139
+ if (request.messages.some((message) => message.role === 'user' && message.documents?.length))
140
+ throw invalid('Codex subscription provider does not support user documents');
141
+ if (request.messages.some((message) => message.role === 'tool' && message.documents?.length))
142
+ throw invalid('Codex subscription provider does not support tool documents');
143
+ const { instructions, input } = serializeMessages(request.messages);
144
+ const body = {
145
+ model,
146
+ store: false,
147
+ stream: true,
148
+ include: ['reasoning.encrypted_content'],
149
+ instructions,
150
+ input,
151
+ };
152
+ if (request.tools?.length) {
153
+ body.tools = request.tools.map((tool) => ({
154
+ type: 'function',
155
+ name: tool.name,
156
+ description: tool.description,
157
+ parameters: tool.inputSchema,
158
+ strict: false,
159
+ }));
160
+ }
161
+ if (thinking && thinking.mode !== 'disabled') {
162
+ body.reasoning = {
163
+ ...(request.effort ? { effort: request.effort } : {}),
164
+ summary: 'auto',
165
+ };
166
+ }
167
+ return body;
168
+ }
169
+ function usageFrom(value) {
170
+ if (!isRecord(value))
171
+ return undefined;
172
+ const input = value.input_tokens;
173
+ const output = value.output_tokens;
174
+ if (input === undefined && output === undefined) {
175
+ throw invalid('Responses provider returned invalid usage');
176
+ }
177
+ if (typeof input !== 'number' ||
178
+ typeof output !== 'number' ||
179
+ !Number.isSafeInteger(input) ||
180
+ input < 0 ||
181
+ !Number.isSafeInteger(output) ||
182
+ output < 0)
183
+ throw invalid('Responses provider returned invalid usage');
184
+ const details = isRecord(value.input_tokens_details)
185
+ ? value.input_tokens_details
186
+ : undefined;
187
+ const cached = details?.cached_tokens;
188
+ if (cached !== undefined &&
189
+ (typeof cached !== 'number' || !Number.isSafeInteger(cached) || cached < 0))
190
+ throw invalid('Responses provider returned invalid usage');
191
+ return {
192
+ type: 'usage',
193
+ usage: {
194
+ inputTokens: input,
195
+ outputTokens: output,
196
+ ...(typeof cached === 'number' && cached > 0
197
+ ? { cacheReadInputTokens: cached }
198
+ : {}),
199
+ },
200
+ };
201
+ }
202
+ function responsePayload(value) {
203
+ if (isRecord(value.response))
204
+ return value.response;
205
+ return value;
206
+ }
207
+ function callKey(value) {
208
+ if (value.item_id !== undefined && typeof value.item_id !== 'string')
209
+ throw invalid('Responses provider returned an invalid function item identity');
210
+ const id = typeof value.item_id === 'string' ? value.item_id : undefined;
211
+ const outputIndex = typeof value.output_index === 'number' ? value.output_index : undefined;
212
+ if (value.output_index !== undefined &&
213
+ typeof value.output_index !== 'number')
214
+ throw invalid('Responses provider returned an invalid output index');
215
+ if (outputIndex !== undefined &&
216
+ (!Number.isSafeInteger(outputIndex) || outputIndex < 0))
217
+ throw invalid('Responses provider returned an invalid output index');
218
+ if (outputIndex !== undefined)
219
+ return { key: `index:${outputIndex}`, index: outputIndex };
220
+ if (id)
221
+ return { key: `id:${id}`, index: Number.MAX_SAFE_INTEGER };
222
+ throw invalid('Responses provider returned a function call without an identity');
223
+ }
224
+ function finishReason(reason) {
225
+ if (reason === 'max_output_tokens')
226
+ return 'max_tokens';
227
+ throw invalid('Responses provider returned an unsupported incomplete response');
228
+ }
229
+ function finishReasonFor(value) {
230
+ const response = responsePayload(value);
231
+ if (value.type === 'response.incomplete' ||
232
+ response.status === 'incomplete') {
233
+ const details = isRecord(response.incomplete_details)
234
+ ? response.incomplete_details
235
+ : undefined;
236
+ return finishReason(details?.reason ?? response.reason);
237
+ }
238
+ return 'tool_use';
239
+ }
240
+ function reasoningBytes(reasoning) {
241
+ return (bytes(reasoning.summary) +
242
+ (reasoning.signature === undefined ? 0 : bytes(reasoning.signature)));
243
+ }
244
+ function completedReasoning(state) {
245
+ const reasoning = state.reasoning;
246
+ if (!reasoning ||
247
+ reasoning.activePart ||
248
+ !reasoning.itemDone ||
249
+ !reasoning.signature)
250
+ throw invalid('Responses provider returned an incomplete reasoning block');
251
+ const events = [];
252
+ if (reasoning.signature.length > 0)
253
+ events.push({
254
+ type: 'thinking-signature-delta',
255
+ delta: reasoning.signature,
256
+ });
257
+ events.push({
258
+ type: 'thinking-stop',
259
+ block: {
260
+ type: 'thinking',
261
+ thinking: reasoning.summary,
262
+ signature: reasoning.signature,
263
+ },
264
+ });
265
+ state.reasoning = undefined;
266
+ return events;
267
+ }
268
+ function parseEvent(value, state, limits) {
269
+ if (state.terminal)
270
+ throw invalid('Responses provider emitted data after terminal response');
271
+ const type = typeof value.type === 'string' ? value.type : '';
272
+ const events = [];
273
+ if (type === 'response.output_text.delta') {
274
+ const delta = value.delta;
275
+ if (typeof delta !== 'string')
276
+ throw invalid('Responses provider returned invalid text delta');
277
+ if (delta.length)
278
+ events.push({ type: 'text-delta', delta });
279
+ return events;
280
+ }
281
+ if (type === 'response.reasoning_summary_part.added') {
282
+ const itemId = value.item_id;
283
+ if (typeof itemId !== 'string' || itemId.length === 0)
284
+ throw invalid('Responses provider returned reasoning without an item identity');
285
+ const part = isRecord(value.part) ? value.part : value;
286
+ if (state.reasoning) {
287
+ if (state.reasoning.itemId !== itemId || state.reasoning.itemDone)
288
+ throw invalid('Responses provider interleaved reasoning blocks');
289
+ if (state.reasoning.activePart)
290
+ throw invalid('Responses provider interleaved reasoning summary parts');
291
+ if (state.reasoning.summary.length > 0) {
292
+ state.reasoning.summary += '\n';
293
+ if (reasoningBytes(state.reasoning) > limits.reasoning)
294
+ throw invalid('Responses provider exceeded the reasoning limit');
295
+ events.push({ type: 'thinking-delta', delta: '\n' });
296
+ }
297
+ state.reasoning.activePart = true;
298
+ state.reasoning.partSeen = true;
299
+ }
300
+ else {
301
+ state.reasoning = {
302
+ itemId,
303
+ summary: '',
304
+ activePart: true,
305
+ partSeen: true,
306
+ itemDone: false,
307
+ };
308
+ events.push({
309
+ type: 'thinking-start',
310
+ block: { type: 'thinking', thinking: '' },
311
+ });
312
+ }
313
+ if (isRecord(part) && typeof part.text === 'string' && part.text.length) {
314
+ state.reasoning.summary += part.text;
315
+ if (reasoningBytes(state.reasoning) > limits.reasoning)
316
+ throw invalid('Responses provider exceeded the reasoning limit');
317
+ events.push({ type: 'thinking-delta', delta: part.text });
318
+ }
319
+ return events;
320
+ }
321
+ if (type === 'response.reasoning_summary_text.delta') {
322
+ if (!state.reasoning)
323
+ throw invalid('Responses provider returned reasoning text without a block');
324
+ if (!state.reasoning.activePart)
325
+ throw invalid('Responses provider returned reasoning text without an active part');
326
+ if (value.item_id !== state.reasoning.itemId)
327
+ throw invalid('Responses provider reasoning item identity mismatch');
328
+ const delta = value.delta;
329
+ if (typeof delta !== 'string')
330
+ throw invalid('Responses provider returned invalid reasoning delta');
331
+ state.reasoning.summary += delta;
332
+ if (reasoningBytes(state.reasoning) > limits.reasoning)
333
+ throw invalid('Responses provider exceeded the reasoning limit');
334
+ if (delta.length)
335
+ events.push({ type: 'thinking-delta', delta });
336
+ return events;
337
+ }
338
+ if (type === 'response.reasoning_summary_part.done') {
339
+ if (!state.reasoning)
340
+ throw invalid('Responses provider completed unknown reasoning block');
341
+ if (value.item_id !== state.reasoning.itemId)
342
+ throw invalid('Responses provider reasoning item identity mismatch');
343
+ if (!state.reasoning.activePart)
344
+ throw invalid('Responses provider duplicated a reasoning summary part');
345
+ state.reasoning.activePart = false;
346
+ if (state.reasoning.itemDone)
347
+ events.push(...completedReasoning(state));
348
+ return events;
349
+ }
350
+ if (type === 'response.function_call_arguments.delta') {
351
+ const { key, index } = callKey(value);
352
+ let call = value.item_id === undefined
353
+ ? undefined
354
+ : state.calls.get(`id:${String(value.item_id)}`);
355
+ if (!call)
356
+ call = state.calls.get(key);
357
+ if (!call) {
358
+ if (state.toolCallCount >= limits.calls)
359
+ throw invalid('Responses provider exceeded the tool call limit');
360
+ call = {
361
+ key,
362
+ index,
363
+ id: '',
364
+ name: '',
365
+ arguments: '',
366
+ done: false,
367
+ emitted: false,
368
+ hadArgumentDelta: false,
369
+ };
370
+ state.calls.set(key, call);
371
+ state.toolCallCount++;
372
+ }
373
+ if (call.done)
374
+ throw invalid('Responses provider duplicated a function call');
375
+ const delta = value.delta;
376
+ if (typeof delta !== 'string')
377
+ throw invalid('Responses provider returned invalid function arguments');
378
+ call.arguments += delta;
379
+ call.hadArgumentDelta = true;
380
+ state.argumentBytes += bytes(delta);
381
+ if (state.argumentBytes > limits.arguments)
382
+ throw invalid('Responses provider exceeded the function argument limit');
383
+ if (typeof value.item_id === 'string')
384
+ state.metadataBytes += bytes(value.item_id);
385
+ if (state.metadataBytes > limits.metadata)
386
+ throw invalid('Responses provider exceeded function metadata limits');
387
+ return events;
388
+ }
389
+ if (type === 'response.output_item.done') {
390
+ const item = isRecord(value.item) ? value.item : undefined;
391
+ if (!item || typeof item.type !== 'string')
392
+ throw invalid('Responses provider returned an invalid output item');
393
+ if (item.type === 'reasoning') {
394
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
395
+ if (!itemId)
396
+ throw invalid('Responses provider returned reasoning without an item identity');
397
+ const signature = item.encrypted_content;
398
+ if (typeof signature !== 'string' || signature.length === 0)
399
+ throw invalid('Responses provider returned unsigned reasoning');
400
+ if (!state.reasoning) {
401
+ const summary = item.summary;
402
+ if (!Array.isArray(summary) || summary.length === 0)
403
+ throw invalid('Responses provider returned an invalid reasoning summary');
404
+ const texts = [];
405
+ for (const entry of summary) {
406
+ if (!isRecord(entry) ||
407
+ entry.type !== 'summary_text' ||
408
+ typeof entry.text !== 'string' ||
409
+ entry.text.length === 0)
410
+ throw invalid('Responses provider returned an invalid reasoning summary');
411
+ texts.push(entry.text);
412
+ }
413
+ state.reasoning = {
414
+ itemId,
415
+ summary: texts.join('\n'),
416
+ activePart: false,
417
+ partSeen: true,
418
+ itemDone: true,
419
+ signature,
420
+ };
421
+ if (reasoningBytes(state.reasoning) > limits.reasoning)
422
+ throw invalid('Responses provider exceeded the reasoning limit');
423
+ events.push({
424
+ type: 'thinking-start',
425
+ block: { type: 'thinking', thinking: '' },
426
+ });
427
+ events.push({ type: 'thinking-delta', delta: state.reasoning.summary });
428
+ events.push(...completedReasoning(state));
429
+ return events;
430
+ }
431
+ if (state.reasoning.itemId !== itemId)
432
+ throw invalid('Responses provider reasoning item identity mismatch');
433
+ if (state.reasoning.itemDone)
434
+ throw invalid('Responses provider duplicated a reasoning item');
435
+ if (item.summary !== undefined) {
436
+ if (!Array.isArray(item.summary) || item.summary.length === 0)
437
+ throw invalid('Responses provider returned an invalid reasoning summary');
438
+ for (const entry of item.summary) {
439
+ if (!isRecord(entry) ||
440
+ entry.type !== 'summary_text' ||
441
+ typeof entry.text !== 'string' ||
442
+ entry.text.length === 0)
443
+ throw invalid('Responses provider returned an invalid reasoning summary');
444
+ }
445
+ }
446
+ state.reasoning.signature = signature;
447
+ state.reasoning.itemDone = true;
448
+ if (reasoningBytes(state.reasoning) > limits.reasoning)
449
+ throw invalid('Responses provider exceeded the reasoning limit');
450
+ if (!state.reasoning.activePart)
451
+ events.push(...completedReasoning(state));
452
+ return events;
453
+ }
454
+ if (item.type !== 'function_call')
455
+ return events;
456
+ const id = typeof item.call_id === 'string' ? item.call_id : '';
457
+ const name = typeof item.name === 'string' ? item.name : '';
458
+ if (!id || !name)
459
+ throw invalid('Responses provider returned an invalid function call');
460
+ const outputIndex = typeof value.output_index === 'number'
461
+ ? value.output_index
462
+ : typeof item.output_index === 'number'
463
+ ? item.output_index
464
+ : undefined;
465
+ if ((value.output_index !== undefined &&
466
+ typeof value.output_index !== 'number') ||
467
+ (item.output_index !== undefined && typeof item.output_index !== 'number'))
468
+ throw invalid('Responses provider returned an invalid output index');
469
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
470
+ if (outputIndex !== undefined &&
471
+ (!Number.isSafeInteger(outputIndex) || outputIndex < 0))
472
+ throw invalid('Responses provider returned an invalid output index');
473
+ const key = outputIndex !== undefined
474
+ ? `index:${outputIndex}`
475
+ : itemId
476
+ ? `id:${itemId}`
477
+ : undefined;
478
+ if (!key)
479
+ throw invalid('Responses provider returned a function call without an identity');
480
+ let call = itemId === undefined ? undefined : state.calls.get(`id:${itemId}`);
481
+ if (!call)
482
+ call = state.calls.get(key);
483
+ if (!call && outputIndex !== undefined) {
484
+ call = [...state.calls.values()].find((candidate) => candidate.index === outputIndex);
485
+ }
486
+ if (!call) {
487
+ if (state.toolCallCount >= limits.calls)
488
+ throw invalid('Responses provider exceeded the tool call limit');
489
+ call = {
490
+ key,
491
+ index: outputIndex ?? state.callOrder++,
492
+ id: '',
493
+ name: '',
494
+ arguments: '',
495
+ done: false,
496
+ emitted: false,
497
+ hadArgumentDelta: false,
498
+ };
499
+ state.calls.set(key, call);
500
+ state.toolCallCount++;
501
+ }
502
+ if (call.done || call.emitted)
503
+ throw invalid('Responses provider duplicated a function call');
504
+ call.id = id;
505
+ call.name = name;
506
+ const fullArguments = item.arguments;
507
+ if (call.arguments.length === 0) {
508
+ if (typeof fullArguments !== 'string' || fullArguments.length === 0)
509
+ throw invalid('Responses provider returned a function call without arguments');
510
+ call.arguments = fullArguments;
511
+ state.argumentBytes += bytes(fullArguments);
512
+ if (state.argumentBytes > limits.arguments)
513
+ throw invalid('Responses provider exceeded the function argument limit');
514
+ }
515
+ else if (typeof fullArguments === 'string' &&
516
+ call.hadArgumentDelta &&
517
+ fullArguments !== call.arguments) {
518
+ throw invalid('Responses provider returned mismatched function arguments');
519
+ }
520
+ state.metadataBytes +=
521
+ bytes(id) + bytes(name) + (itemId === undefined ? 0 : bytes(itemId));
522
+ if (state.metadataBytes > limits.metadata)
523
+ throw invalid('Responses provider exceeded function metadata limits');
524
+ call.done = true;
525
+ let parsed;
526
+ try {
527
+ parsed = JSON.parse(call.arguments || '{}');
528
+ }
529
+ catch {
530
+ if (!id || !name)
531
+ throw invalid('Responses provider returned an invalid tool call');
532
+ call.emitted = true;
533
+ state.toolCallEmitted = true;
534
+ events.push({ type: 'tool-call', call: malformedModelToolCall(id, name) });
535
+ return events;
536
+ }
537
+ if (!isRecord(parsed))
538
+ throw invalid('Responses provider returned non-object function arguments');
539
+ call.emitted = true;
540
+ state.toolCallEmitted = true;
541
+ const toolCall = { id, name, input: parsed };
542
+ events.push({ type: 'tool-call', call: toolCall });
543
+ return events;
544
+ }
545
+ if (type === 'response.completed' || type === 'response.incomplete') {
546
+ if (state.reasoning)
547
+ throw invalid('Responses provider ended with incomplete reasoning');
548
+ for (const call of state.calls.values()) {
549
+ if (!call.done || !call.emitted)
550
+ throw invalid('Responses provider ended with an incomplete function call');
551
+ }
552
+ const response = responsePayload(value);
553
+ const usage = usageFrom(isRecord(response.usage)
554
+ ? response.usage
555
+ : isRecord(value.usage)
556
+ ? value.usage
557
+ : undefined);
558
+ if (usage && !state.usageEmitted) {
559
+ state.usageEmitted = true;
560
+ events.push(usage);
561
+ }
562
+ state.terminal = true;
563
+ events.push({
564
+ type: 'terminal',
565
+ reason: type === 'response.incomplete'
566
+ ? finishReasonFor(value)
567
+ : state.toolCallEmitted
568
+ ? 'tool_use'
569
+ : 'end_turn',
570
+ });
571
+ return events;
572
+ }
573
+ if (type === 'response.failed' || type === 'error' || isRecord(value.error))
574
+ throw providerError('api_error', true);
575
+ return events;
576
+ }
577
+ function parseFrame(frame, state, limits) {
578
+ const data = frame
579
+ .split('\n')
580
+ .filter((line) => line.startsWith('data:'))
581
+ .map((line) => line.slice(5).trimStart())
582
+ .join('\n');
583
+ if (!data)
584
+ return [];
585
+ let parsed;
586
+ try {
587
+ parsed = JSON.parse(data);
588
+ }
589
+ catch {
590
+ throw invalid('Responses provider returned malformed SSE JSON');
591
+ }
592
+ if (!isRecord(parsed))
593
+ throw invalid('Responses provider returned malformed SSE event');
594
+ return parseEvent(parsed, state, limits);
595
+ }
596
+ export class ResponsesCodec {
597
+ maxStreamBufferBytes;
598
+ maxToolArgumentsBytes;
599
+ maxToolCallsPerResponse;
600
+ maxToolMetadataBytes;
601
+ maxReasoningBytes;
602
+ label;
603
+ configuredThinking;
604
+ constructor(options = {}) {
605
+ this.label = options.providerLabel ?? 'Responses provider';
606
+ this.configuredThinking = options.thinking;
607
+ if (options.thinking !== undefined &&
608
+ options.thinking.mode !== 'disabled' &&
609
+ options.thinking.mode !== 'enabled' &&
610
+ options.thinking.mode !== 'adaptive')
611
+ throw new Error(`${this.label} received an invalid thinking mode`);
612
+ if (options.thinking?.maxTokens !== undefined)
613
+ throw new Error(`${this.label} does not support thinking token budgets`);
614
+ this.maxStreamBufferBytes = positiveInteger(options.maxStreamBufferBytes, DEFAULT_MAX_STREAM);
615
+ this.maxToolArgumentsBytes = positiveInteger(options.maxToolArgumentsBytes, DEFAULT_MAX_ARGUMENTS);
616
+ this.maxToolCallsPerResponse = positiveInteger(options.maxToolCallsPerResponse, DEFAULT_MAX_CALLS);
617
+ this.maxToolMetadataBytes = positiveInteger(options.maxToolMetadataBytes, DEFAULT_MAX_METADATA);
618
+ this.maxReasoningBytes = positiveInteger(options.maxReasoningBytes, DEFAULT_MAX_REASONING);
619
+ }
620
+ serialize(request, model) {
621
+ try {
622
+ return serializeResponsesRequest(request, model, this.configuredThinking);
623
+ }
624
+ catch (error) {
625
+ throw this.contextualize(error);
626
+ }
627
+ }
628
+ async *stream(body, options = {}) {
629
+ const reader = body.getReader();
630
+ const decoder = new TextDecoder();
631
+ const state = this.createParser();
632
+ let buffer = '';
633
+ let readerDone = false;
634
+ let abortCancellation;
635
+ const cancelOnAbort = () => {
636
+ abortCancellation = reader.cancel().catch(() => undefined);
637
+ };
638
+ if (options.signal?.aborted)
639
+ cancelOnAbort();
640
+ else
641
+ options.signal?.addEventListener('abort', cancelOnAbort, { once: true });
642
+ try {
643
+ while (true) {
644
+ const next = await reader.read();
645
+ if (next.done)
646
+ readerDone = true;
647
+ if (!next.done && next.value.byteLength > 0)
648
+ options.onChunk?.();
649
+ buffer += decoder.decode(next.done ? undefined : next.value, {
650
+ stream: !next.done,
651
+ });
652
+ buffer = buffer.replaceAll('\r\n', '\n');
653
+ if (bytes(buffer) > this.maxStreamBufferBytes)
654
+ throw this.failure('invalid_request', false, `${this.label} exceeded the SSE buffer limit`);
655
+ let boundary = buffer.indexOf('\n\n');
656
+ while (boundary >= 0) {
657
+ const frame = buffer.slice(0, boundary);
658
+ buffer = buffer.slice(boundary + 2);
659
+ for (const event of this.parseFrame(frame, state))
660
+ yield event;
661
+ if (state.terminal)
662
+ return;
663
+ boundary = buffer.indexOf('\n\n');
664
+ }
665
+ if (next.done)
666
+ throw this.failure('transport_error', true, `${this.label} stream ended before a terminal event`);
667
+ }
668
+ }
669
+ catch (error) {
670
+ if (options.signal?.aborted)
671
+ throw this.failure('cancelled', false, `${this.label} request was cancelled`);
672
+ if (error instanceof ModelProviderError)
673
+ throw this.contextualize(error);
674
+ throw this.failure('transport_error', true, `${this.label} stream failed`);
675
+ }
676
+ finally {
677
+ options.signal?.removeEventListener('abort', cancelOnAbort);
678
+ if (abortCancellation !== undefined)
679
+ await abortCancellation;
680
+ else if (!readerDone) {
681
+ try {
682
+ await reader.cancel();
683
+ }
684
+ catch {
685
+ /* preserve the primary error or consumer return */
686
+ }
687
+ }
688
+ reader.releaseLock();
689
+ }
690
+ }
691
+ createParser() {
692
+ return {
693
+ reasoning: undefined,
694
+ calls: new Map(),
695
+ callOrder: 0,
696
+ toolCallCount: 0,
697
+ argumentBytes: 0,
698
+ metadataBytes: 0,
699
+ terminal: false,
700
+ usageEmitted: false,
701
+ toolCallEmitted: false,
702
+ };
703
+ }
704
+ parseFrame(frame, state) {
705
+ try {
706
+ return parseFrame(frame, state, {
707
+ arguments: this.maxToolArgumentsBytes,
708
+ calls: this.maxToolCallsPerResponse,
709
+ metadata: this.maxToolMetadataBytes,
710
+ reasoning: this.maxReasoningBytes,
711
+ });
712
+ }
713
+ catch (error) {
714
+ throw this.contextualize(error);
715
+ }
716
+ }
717
+ contextualize(error) {
718
+ if (!(error instanceof ModelProviderError))
719
+ return error;
720
+ if (error.message.includes(this.label))
721
+ return error;
722
+ const message = error.message.replace(/Codex subscription provider|Codex provider|Responses provider/gu, this.label);
723
+ if (message === error.message)
724
+ return error;
725
+ return new ModelProviderError(message, {
726
+ retryable: error.retryable,
727
+ ...(error.kind === undefined ? {} : { kind: error.kind }),
728
+ ...(error.status === undefined ? {} : { status: error.status }),
729
+ ...(error.retryDelayMs === undefined
730
+ ? {}
731
+ : { retryDelayMs: error.retryDelayMs }),
732
+ ...(error.timeoutPhase === undefined
733
+ ? {}
734
+ : { timeoutPhase: error.timeoutPhase }),
735
+ ...(error.cause === undefined ? {} : { cause: error.cause }),
736
+ });
737
+ }
738
+ failure(kind, retryable, message) {
739
+ return new ModelProviderError(message, { kind, retryable });
740
+ }
741
+ }
742
+ //# sourceMappingURL=responses-codec.js.map