codex-overleaf-link 2.0.0 → 2.1.1

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 (119) hide show
  1. package/README.md +24 -23
  2. package/extension/bootstrap/manifest.template.json +2 -1
  3. package/extension/runtime-manifest.json +12 -6
  4. package/extension/src/background.js +7 -0
  5. package/extension/src/content/composerPanel.js +13 -3
  6. package/extension/src/content/contentRuntime.js +129 -64
  7. package/extension/src/content/contextTray.js +173 -9
  8. package/extension/src/content/markdownText.js +16 -1
  9. package/extension/src/content/mathText.js +233 -0
  10. package/extension/src/content/modelPicker.js +117 -106
  11. package/extension/src/content/modelPickerSupport.js +115 -0
  12. package/extension/src/content/nativeChannel.js +20 -1
  13. package/extension/src/content/panelMaintenance.js +2 -0
  14. package/extension/src/content/projectSettingsCoordinator.js +2 -0
  15. package/extension/src/content/providerSettingsCoordinator.js +413 -0
  16. package/extension/src/content/providerSettingsDialog.js +1006 -0
  17. package/extension/src/content/runTimelineView.js +24 -5
  18. package/extension/src/content/settingsPanel.js +33 -2
  19. package/extension/src/content/writebackOrchestrator.js +3 -1
  20. package/extension/src/page/overleafProjectSnapshot.js +29 -33
  21. package/extension/src/page/snapshotRouter.js +75 -8
  22. package/extension/src/page/writebackRouter.js +20 -33
  23. package/extension/src/pageBridge.js +1 -1
  24. package/extension/src/shared/agentTranscript.js +130 -0
  25. package/extension/src/shared/compatibility.js +3 -2
  26. package/extension/src/shared/failureReasons.js +75 -0
  27. package/extension/src/shared/i18n.js +22 -0
  28. package/extension/src/shared/models.js +1 -0
  29. package/extension/src/shared/providerProfiles.js +283 -0
  30. package/extension/src/shared/sessionState.js +12 -2
  31. package/extension/src/shared/storageDb.js +1 -1
  32. package/extension/styles/panel.css +689 -1
  33. package/extension/vendor/katex/LICENSE +21 -0
  34. package/extension/vendor/katex/VERSION +2 -0
  35. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
  36. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  37. package/extension/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  38. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  39. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  40. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  41. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  42. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  43. package/extension/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  44. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  45. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  46. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  47. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  48. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  49. package/extension/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  50. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
  51. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  52. package/extension/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  53. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  54. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  55. package/extension/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  56. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
  57. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  58. package/extension/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  59. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
  60. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  61. package/extension/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  62. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  63. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  64. package/extension/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  65. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
  66. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  67. package/extension/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  68. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  69. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  70. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  71. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  72. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  73. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  74. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  75. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  76. package/extension/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  77. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
  78. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
  79. package/extension/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  80. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
  81. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  82. package/extension/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  83. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
  84. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  85. package/extension/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  86. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
  87. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  88. package/extension/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  89. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
  90. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  91. package/extension/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  92. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  93. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  94. package/extension/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  95. package/extension/vendor/katex/katex.min.css +1 -0
  96. package/extension/vendor/katex/katex.min.js +1 -0
  97. package/native-host/src/anthropicBridgeRequest.js +435 -0
  98. package/native-host/src/anthropicBridgeResponse.js +431 -0
  99. package/native-host/src/anthropicMessagesBridge.js +281 -0
  100. package/native-host/src/chatBridgeRequest.js +274 -0
  101. package/native-host/src/chatBridgeResponse.js +594 -0
  102. package/native-host/src/chatCompletionsBridge.js +485 -0
  103. package/native-host/src/codexCommand.js +25 -0
  104. package/native-host/src/codexPromptAssembly.js +9 -0
  105. package/native-host/src/codexProviderLaunch.js +213 -0
  106. package/native-host/src/codexProviderTest.js +196 -0
  107. package/native-host/src/codexSessionRunner.js +29 -25
  108. package/native-host/src/managedInstall.js +1 -0
  109. package/native-host/src/providerBridgeRoutes.js +11 -0
  110. package/native-host/src/providerModelCatalog.js +107 -0
  111. package/native-host/src/providerProfile.js +454 -0
  112. package/native-host/src/providerReasoning.js +235 -0
  113. package/native-host/src/providerRedaction.js +18 -0
  114. package/native-host/src/providerRuntime.js +365 -0
  115. package/native-host/src/providerStore.js +691 -0
  116. package/native-host/src/readProgressGuard.js +360 -0
  117. package/native-host/src/taskRunnerRuntime.js +39 -3
  118. package/native-host/src/updateArchive.js +1 -0
  119. package/package.json +2 -1
@@ -0,0 +1,431 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const { encodeAnthropicThinkingBlock } = require('./anthropicBridgeRequest');
5
+
6
+ function convertAnthropicResponse(message = {}, context = {}) {
7
+ if (message.type === 'error' || message.error) {
8
+ const source = message.error || message;
9
+ throw responseError(source.message || 'Anthropic returned an error envelope.', 'provider_upstream_error');
10
+ }
11
+ const responseIdValue = responseId(message.id);
12
+ const output = [];
13
+ const blocks = Array.isArray(message.content) ? message.content : [];
14
+ let pendingText = [];
15
+ const flushText = () => {
16
+ if (!pendingText.length) return;
17
+ output.push(messageItem(pendingText.join(''), responseIdValue));
18
+ pendingText = [];
19
+ };
20
+ for (const block of blocks) {
21
+ if (block?.type === 'text' && typeof block.text === 'string') {
22
+ pendingText.push(block.text);
23
+ } else if (block?.type === 'tool_use') {
24
+ flushText();
25
+ output.push(toolItem(block, context.toolContext));
26
+ } else if (block?.type === 'thinking' || block?.type === 'redacted_thinking') {
27
+ flushText();
28
+ output.push(reasoningItem(block));
29
+ }
30
+ }
31
+ flushText();
32
+ if (!output.length) throw responseError('Anthropic completed without usable output.');
33
+ const terminal = mapStopReason(message.stop_reason);
34
+ return {
35
+ response: responseEnvelope({
36
+ id: responseIdValue,
37
+ model: message.model || context.model,
38
+ output,
39
+ usage: normalizeUsage(message.usage),
40
+ requestBody: context.requestBody,
41
+ ...terminal
42
+ }),
43
+ assistantBlocks: clone(blocks)
44
+ };
45
+ }
46
+
47
+ async function streamAnthropicResponse({ upstream, res, context = {}, onComplete = () => {} } = {}) {
48
+ const state = createState(context);
49
+ beginSse(res);
50
+ emit(state, res, 'response.created', { response: streamEnvelope(state, 'in_progress') });
51
+ emit(state, res, 'response.in_progress', { response: streamEnvelope(state, 'in_progress') });
52
+ const decoder = new TextDecoder();
53
+ let buffer = '';
54
+ for await (const chunk of upstream.body || []) {
55
+ buffer += typeof chunk === 'string' ? chunk : decoder.decode(chunk, { stream: true });
56
+ let boundary = findBoundary(buffer);
57
+ while (boundary) {
58
+ const block = buffer.slice(0, boundary.index);
59
+ buffer = buffer.slice(boundary.index + boundary.length);
60
+ const event = parseEvent(block);
61
+ if (event.data) processEvent(state, res, event.type, parseJson(event.data));
62
+ boundary = findBoundary(buffer);
63
+ }
64
+ }
65
+ buffer += decoder.decode();
66
+ if (buffer.trim()) {
67
+ const event = parseEvent(buffer);
68
+ if (event.data) processEvent(state, res, event.type, parseJson(event.data));
69
+ }
70
+ const converted = completeStream(state, res);
71
+ onComplete(converted);
72
+ res.end();
73
+ return converted;
74
+ }
75
+
76
+ function processEvent(state, res, eventType, value) {
77
+ if (!value || typeof value !== 'object') return;
78
+ if (value.type === 'error' || value.error) {
79
+ throw responseError(value.error?.message || 'Anthropic stream returned an error.', 'provider_upstream_error');
80
+ }
81
+ const type = value.type || eventType;
82
+ if (type === 'message_start') {
83
+ const message = value.message || {};
84
+ state.model = message.model || state.model;
85
+ state.usage = normalizeUsage(message.usage);
86
+ return;
87
+ }
88
+ if (type === 'content_block_start') {
89
+ openBlock(state, res, Number(value.index) || 0, value.content_block || {});
90
+ return;
91
+ }
92
+ if (type === 'content_block_delta') {
93
+ appendBlockDelta(state, res, Number(value.index) || 0, value.delta || {});
94
+ return;
95
+ }
96
+ if (type === 'content_block_stop') {
97
+ closeBlock(state, res, Number(value.index) || 0);
98
+ return;
99
+ }
100
+ if (type === 'message_delta') {
101
+ state.stopReason = value.delta?.stop_reason || state.stopReason;
102
+ state.usage = mergeUsage(state.usage, value.usage);
103
+ return;
104
+ }
105
+ if (type === 'message_stop') state.sawMessageStop = true;
106
+ }
107
+
108
+ function openBlock(state, res, index, block) {
109
+ if (state.blocks.has(index)) return;
110
+ if (block.type === 'text') {
111
+ const item = { id: randomId('msg'), type: 'message', status: 'in_progress', role: 'assistant', content: [] };
112
+ const initialText = block.text || '';
113
+ const entry = { type: 'text', item, outputIndex: reserve(state, item), text: '', original: { type: 'text', text: '' } };
114
+ state.blocks.set(index, entry);
115
+ emit(state, res, 'response.output_item.added', { output_index: entry.outputIndex, item });
116
+ emit(state, res, 'response.content_part.added', {
117
+ item_id: item.id,
118
+ output_index: entry.outputIndex,
119
+ content_index: 0,
120
+ part: { type: 'output_text', text: '', annotations: [] }
121
+ });
122
+ if (initialText) appendBlockDelta(state, res, index, { type: 'text_delta', text: initialText });
123
+ } else if (block.type === 'thinking' || block.type === 'redacted_thinking') {
124
+ const item = { id: randomId('rs'), type: 'reasoning', status: 'in_progress', summary: [] };
125
+ const entry = {
126
+ type: block.type,
127
+ item,
128
+ outputIndex: reserve(state, item),
129
+ thinking: block.thinking || '',
130
+ signature: block.signature || '',
131
+ data: block.data || ''
132
+ };
133
+ state.blocks.set(index, entry);
134
+ emit(state, res, 'response.output_item.added', { output_index: entry.outputIndex, item });
135
+ emit(state, res, 'response.reasoning_summary_part.added', {
136
+ item_id: item.id,
137
+ output_index: entry.outputIndex,
138
+ summary_index: 0,
139
+ part: { type: 'summary_text', text: '' }
140
+ });
141
+ } else if (block.type === 'tool_use') {
142
+ const spec = resolveToolSpec(state.context.toolContext, block.name);
143
+ const kind = spec?.kind === 'custom' ? 'custom' : 'function';
144
+ const callId = block.id || randomId('call');
145
+ const name = spec?.name || block.name || 'tool';
146
+ const item = kind === 'custom'
147
+ ? { id: randomId('ct'), type: 'custom_tool_call', status: 'in_progress', call_id: callId, name, input: '' }
148
+ : { id: randomId('fc'), type: 'function_call', status: 'in_progress', call_id: callId, name, arguments: '' };
149
+ if (spec?.namespace) item.namespace = spec.namespace;
150
+ const initial = block.input && Object.keys(block.input).length ? JSON.stringify(block.input) : '';
151
+ const entry = { type: 'tool_use', item, outputIndex: reserve(state, item), kind, spec, callId, upstreamName: block.name, arguments: initial };
152
+ state.blocks.set(index, entry);
153
+ emit(state, res, 'response.output_item.added', { output_index: entry.outputIndex, item });
154
+ }
155
+ }
156
+
157
+ function appendBlockDelta(state, res, index, delta) {
158
+ let entry = state.blocks.get(index);
159
+ if (!entry) {
160
+ const inferred = delta.type === 'text_delta'
161
+ ? { type: 'text', text: '' }
162
+ : delta.type === 'thinking_delta' || delta.type === 'signature_delta'
163
+ ? { type: 'thinking', thinking: '' }
164
+ : { type: 'tool_use', id: randomId('call'), name: 'tool', input: {} };
165
+ openBlock(state, res, index, inferred);
166
+ entry = state.blocks.get(index);
167
+ }
168
+ if (delta.type === 'text_delta') {
169
+ const value = String(delta.text || '');
170
+ entry.text += value;
171
+ emit(state, res, 'response.output_text.delta', { item_id: entry.item.id, output_index: entry.outputIndex, content_index: 0, delta: value, logprobs: [] });
172
+ } else if (delta.type === 'thinking_delta') {
173
+ const value = String(delta.thinking || '');
174
+ entry.thinking += value;
175
+ emit(state, res, 'response.reasoning_summary_text.delta', { item_id: entry.item.id, output_index: entry.outputIndex, summary_index: 0, delta: value });
176
+ } else if (delta.type === 'signature_delta') {
177
+ entry.signature += String(delta.signature || '');
178
+ } else if (delta.type === 'input_json_delta') {
179
+ const value = String(delta.partial_json || '');
180
+ entry.arguments += value;
181
+ if (entry.kind === 'function') {
182
+ emit(state, res, 'response.function_call_arguments.delta', { item_id: entry.item.id, output_index: entry.outputIndex, delta: value });
183
+ }
184
+ }
185
+ }
186
+
187
+ function closeBlock(state, res, index) {
188
+ const entry = state.blocks.get(index);
189
+ if (!entry || entry.closed) return;
190
+ entry.closed = true;
191
+ if (entry.type === 'text') {
192
+ const part = { type: 'output_text', text: entry.text, annotations: [] };
193
+ entry.item.status = 'completed';
194
+ entry.item.content = [part];
195
+ emit(state, res, 'response.output_text.done', { item_id: entry.item.id, output_index: entry.outputIndex, content_index: 0, text: entry.text, logprobs: [] });
196
+ emit(state, res, 'response.content_part.done', { item_id: entry.item.id, output_index: entry.outputIndex, content_index: 0, part });
197
+ state.assistantBlocks[index] = { type: 'text', text: entry.text };
198
+ } else if (entry.type === 'thinking' || entry.type === 'redacted_thinking') {
199
+ const block = entry.type === 'redacted_thinking'
200
+ ? { type: 'redacted_thinking', data: entry.data }
201
+ : { type: 'thinking', thinking: entry.thinking, signature: entry.signature };
202
+ entry.item.status = 'completed';
203
+ entry.item.summary = entry.thinking ? [{ type: 'summary_text', text: entry.thinking }] : [];
204
+ entry.item.encrypted_content = encodeAnthropicThinkingBlock(block);
205
+ emit(state, res, 'response.reasoning_summary_text.done', { item_id: entry.item.id, output_index: entry.outputIndex, summary_index: 0, text: entry.thinking || '' });
206
+ emit(state, res, 'response.reasoning_summary_part.done', { item_id: entry.item.id, output_index: entry.outputIndex, summary_index: 0, part: entry.item.summary[0] || { type: 'summary_text', text: '' } });
207
+ state.assistantBlocks[index] = block;
208
+ } else {
209
+ entry.item.status = 'completed';
210
+ const args = canonicalArguments(entry.arguments);
211
+ if (entry.kind === 'custom') {
212
+ entry.item.input = unwrapCustomInput(args);
213
+ emit(state, res, 'response.custom_tool_call_input.done', { item_id: entry.item.id, output_index: entry.outputIndex, input: entry.item.input });
214
+ } else {
215
+ entry.item.arguments = args;
216
+ emit(state, res, 'response.function_call_arguments.done', { item_id: entry.item.id, output_index: entry.outputIndex, arguments: args });
217
+ }
218
+ state.assistantBlocks[index] = { type: 'tool_use', id: entry.callId, name: entry.upstreamName, input: parseObject(args) };
219
+ }
220
+ emit(state, res, 'response.output_item.done', { output_index: entry.outputIndex, item: entry.item });
221
+ state.items[entry.outputIndex] = entry.item;
222
+ }
223
+
224
+ function completeStream(state, res) {
225
+ for (const index of Array.from(state.blocks.keys()).sort((a, b) => a - b)) closeBlock(state, res, index);
226
+ if (!state.items.filter(Boolean).length) throw responseError('Anthropic stream ended without usable output.');
227
+ const terminal = mapStopReason(state.stopReason);
228
+ const response = responseEnvelope({
229
+ id: state.id,
230
+ model: state.model,
231
+ output: state.items.filter(Boolean),
232
+ usage: state.usage,
233
+ requestBody: state.context.requestBody,
234
+ ...terminal
235
+ });
236
+ emit(state, res, terminal.status === 'incomplete' ? 'response.incomplete' : 'response.completed', { response });
237
+ return { response, assistantBlocks: state.assistantBlocks.filter(Boolean) };
238
+ }
239
+
240
+ function toolItem(block, toolContext) {
241
+ const spec = resolveToolSpec(toolContext, block.name);
242
+ const kind = spec?.kind === 'custom' ? 'custom' : 'function';
243
+ const name = spec?.name || block.name || 'tool';
244
+ const callId = block.id || randomId('call');
245
+ const args = canonicalArguments(JSON.stringify(block.input || {}));
246
+ const item = kind === 'custom'
247
+ ? { id: randomId('ct'), type: 'custom_tool_call', status: 'completed', call_id: callId, name, input: unwrapCustomInput(args) }
248
+ : { id: randomId('fc'), type: 'function_call', status: 'completed', call_id: callId, name, arguments: args };
249
+ if (spec?.namespace) item.namespace = spec.namespace;
250
+ return item;
251
+ }
252
+
253
+ function reasoningItem(block) {
254
+ const thinking = block.type === 'thinking' ? String(block.thinking || '') : '';
255
+ return {
256
+ id: randomId('rs'),
257
+ type: 'reasoning',
258
+ status: 'completed',
259
+ summary: thinking ? [{ type: 'summary_text', text: thinking }] : [],
260
+ encrypted_content: encodeAnthropicThinkingBlock(block)
261
+ };
262
+ }
263
+
264
+ function messageItem(value, id) {
265
+ return { id: `${id}_msg_${crypto.randomUUID().slice(0, 8)}`, type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', text: value, annotations: [] }] };
266
+ }
267
+
268
+ function responseEnvelope({ id, model, output, usage, requestBody = {}, status, incompleteReason }) {
269
+ return {
270
+ id,
271
+ object: 'response',
272
+ created_at: Math.floor(Date.now() / 1000),
273
+ status,
274
+ error: null,
275
+ incomplete_details: incompleteReason ? { reason: incompleteReason } : null,
276
+ instructions: null,
277
+ model,
278
+ output,
279
+ parallel_tool_calls: requestBody.parallel_tool_calls !== false,
280
+ previous_response_id: requestBody.previous_response_id || null,
281
+ reasoning: requestBody.reasoning || null,
282
+ store: false,
283
+ temperature: requestBody.temperature ?? null,
284
+ tool_choice: requestBody.tool_choice || 'auto',
285
+ tools: Array.isArray(requestBody.tools) ? requestBody.tools : [],
286
+ top_p: requestBody.top_p ?? null,
287
+ usage
288
+ };
289
+ }
290
+
291
+ function mapStopReason(value) {
292
+ if (value === 'max_tokens' || value === 'model_context_window_exceeded') return { status: 'incomplete', incompleteReason: 'max_output_tokens' };
293
+ if (value === 'refusal') return { status: 'incomplete', incompleteReason: 'content_filter' };
294
+ return { status: 'completed', incompleteReason: null };
295
+ }
296
+
297
+ function normalizeUsage(value = {}) {
298
+ const fresh = positive(value.input_tokens);
299
+ const cached = positive(value.cache_read_input_tokens);
300
+ const created = positive(value.cache_creation_input_tokens);
301
+ const output = positive(value.output_tokens);
302
+ const result = {
303
+ input_tokens: fresh + cached + created,
304
+ input_tokens_details: { cached_tokens: cached, cache_write_tokens: created },
305
+ output_tokens: output,
306
+ output_tokens_details: { reasoning_tokens: positive(value.output_tokens_details?.thinking_tokens) },
307
+ total_tokens: fresh + cached + created + output
308
+ };
309
+ return result;
310
+ }
311
+
312
+ function mergeUsage(current, next = {}) {
313
+ const cached = positive(current.input_tokens_details?.cached_tokens);
314
+ const created = positive(current.input_tokens_details?.cache_write_tokens);
315
+ const merged = {
316
+ input_tokens: Math.max(0, positive(current.input_tokens) - cached - created),
317
+ cache_read_input_tokens: cached,
318
+ cache_creation_input_tokens: created,
319
+ output_tokens: next.output_tokens ?? current.output_tokens,
320
+ output_tokens_details: next.output_tokens_details
321
+ };
322
+ return normalizeUsage(merged);
323
+ }
324
+
325
+ function createState(context) {
326
+ return {
327
+ id: responseId(''),
328
+ model: context.model || context.launch?.modelId || '',
329
+ context,
330
+ items: [],
331
+ blocks: new Map(),
332
+ assistantBlocks: [],
333
+ usage: normalizeUsage(),
334
+ stopReason: '',
335
+ sawMessageStop: false,
336
+ sequence: 0
337
+ };
338
+ }
339
+
340
+ function streamEnvelope(state, status) {
341
+ return responseEnvelope({
342
+ id: state.id,
343
+ model: state.model,
344
+ output: status === 'completed' ? state.items.filter(Boolean) : [],
345
+ usage: status === 'completed' ? state.usage : null,
346
+ requestBody: state.context.requestBody,
347
+ status,
348
+ incompleteReason: null
349
+ });
350
+ }
351
+
352
+ function resolveToolSpec(context, upstreamName) {
353
+ return context?.byUpstreamName?.get?.(upstreamName) || null;
354
+ }
355
+
356
+ function reserve(state, item) {
357
+ const index = state.items.length;
358
+ state.items.push(item);
359
+ return index;
360
+ }
361
+
362
+ function canonicalArguments(value) {
363
+ try { return JSON.stringify(JSON.parse(value || '{}')); } catch (_error) { return '{}'; }
364
+ }
365
+
366
+ function unwrapCustomInput(value) {
367
+ const parsed = parseObject(value);
368
+ return typeof parsed.input === 'string' ? parsed.input : value;
369
+ }
370
+
371
+ function parseObject(value) {
372
+ try {
373
+ const parsed = JSON.parse(value || '{}');
374
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
375
+ } catch (_error) {
376
+ return {};
377
+ }
378
+ }
379
+
380
+ function emit(state, res, type, payload) {
381
+ res.write(`event: ${type}\ndata: ${JSON.stringify({ type, sequence_number: state.sequence++, ...payload })}\n\n`);
382
+ }
383
+
384
+ function beginSse(res) {
385
+ res.writeHead(200, { 'content-type': 'text/event-stream; charset=utf-8', 'cache-control': 'no-cache, no-transform', connection: 'keep-alive' });
386
+ }
387
+
388
+ function findBoundary(value) {
389
+ const match = /\r?\n\r?\n/.exec(value);
390
+ return match ? { index: match.index, length: match[0].length } : null;
391
+ }
392
+
393
+ function parseEvent(block) {
394
+ const lines = String(block || '').split(/\r?\n/);
395
+ const type = lines.find(line => line.startsWith('event:'))?.slice(6).trim() || '';
396
+ const data = lines.filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\n').trim();
397
+ return { type, data };
398
+ }
399
+
400
+ function parseJson(value) {
401
+ try { return JSON.parse(value); } catch (_error) { return null; }
402
+ }
403
+
404
+ function responseId(value) {
405
+ const suffix = String(value || '').replace(/[^A-Za-z0-9_-]/g, '').slice(-48) || crypto.randomUUID().replaceAll('-', '');
406
+ return suffix.startsWith('resp_') ? suffix : `resp_${suffix}`;
407
+ }
408
+
409
+ function randomId(prefix) {
410
+ return `${prefix}_${crypto.randomUUID().replaceAll('-', '')}`;
411
+ }
412
+
413
+ function positive(value) {
414
+ const number = Number(value);
415
+ return Number.isFinite(number) && number > 0 ? Math.floor(number) : 0;
416
+ }
417
+
418
+ function clone(value) {
419
+ try { return JSON.parse(JSON.stringify(value)); } catch (_error) { return []; }
420
+ }
421
+
422
+ function responseError(message, code = 'provider_response_invalid') {
423
+ const error = new Error(message);
424
+ error.code = code;
425
+ return error;
426
+ }
427
+
428
+ module.exports = {
429
+ convertAnthropicResponse,
430
+ streamAnthropicResponse
431
+ };