@pentoshi/clai 3.14.0 → 3.15.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.
Files changed (44) hide show
  1. package/README.md +3 -3
  2. package/dist/app/commands/registry.js +1 -1
  3. package/dist/app/commands/registry.js.map +1 -1
  4. package/dist/attachments/service.js +1 -4
  5. package/dist/attachments/service.js.map +1 -1
  6. package/dist/llm/http.js +56 -11
  7. package/dist/llm/http.js.map +1 -1
  8. package/dist/llm/meta.js +860 -23
  9. package/dist/llm/meta.js.map +1 -1
  10. package/dist/llm/provider.js +3 -3
  11. package/dist/prompts/embedded.js +2 -2
  12. package/dist/prompts/embedded.js.map +1 -1
  13. package/dist/prompts/system.agent.md +1 -4
  14. package/dist/prompts/system.ask.md +2 -2
  15. package/dist/repl/slash-commands.js +4 -3
  16. package/dist/repl/slash-commands.js.map +1 -1
  17. package/dist/repl.js +18 -8
  18. package/dist/repl.js.map +1 -1
  19. package/dist/store/history.d.ts +4 -0
  20. package/dist/store/history.js +77 -1
  21. package/dist/store/history.js.map +1 -1
  22. package/dist/tools/file-diff.js +6 -1
  23. package/dist/tools/file-diff.js.map +1 -1
  24. package/dist/tools/shell.js +26 -3
  25. package/dist/tools/shell.js.map +1 -1
  26. package/dist/tui-v2/app/command-handlers.js +2 -2
  27. package/dist/tui-v2/app/command-handlers.js.map +1 -1
  28. package/dist/tui-v2/app/commands/picker-commands.d.ts +1 -1
  29. package/dist/tui-v2/app/commands/picker-commands.js +18 -5
  30. package/dist/tui-v2/app/commands/picker-commands.js.map +1 -1
  31. package/dist/tui-v2/components/transcript/tool-card.js +1 -1
  32. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  33. package/dist/tui-v2/components/transcript/user-message.js +8 -3
  34. package/dist/tui-v2/components/transcript/user-message.js.map +1 -1
  35. package/dist/tui-v2/rendering/format-help.js +1 -1
  36. package/dist/tui-v2/rendering/format-help.js.map +1 -1
  37. package/dist/tui-v2/rendering/tool-presenter.js +1 -4
  38. package/dist/tui-v2/rendering/tool-presenter.js.map +1 -1
  39. package/dist/ui/mentions.d.ts +2 -5
  40. package/dist/ui/mentions.js +11 -116
  41. package/dist/ui/mentions.js.map +1 -1
  42. package/dist/version.generated.d.ts +2 -2
  43. package/dist/version.generated.js +2 -2
  44. package/package.json +1 -1
package/dist/llm/meta.js CHANGED
@@ -1,8 +1,294 @@
1
- import { defaultModels, } from "./provider.js";
2
- import { openAiCompatibleComplete, openAiCompatiblePing, openAiCompatibleStream, toCompletionResult, readJson, ingestOpenAiModelCatalog, } from "./http.js";
1
+ import { defaultModels } from "./provider.js";
2
+ import { readJson, ingestOpenAiModelCatalog, ProviderError, createSseFrameAssembler, DEFAULT_STREAM_IDLE_TIMEOUT_MS, THINKING_STREAM_IDLE_TIMEOUT_MS, THINKING_STREAM_INITIAL_IDLE_TIMEOUT_MS, STREAM_STALL_MARKER, } from "./http.js";
3
+ import { modelAcceptsImages } from "./capabilities.js";
4
+ import { resolveSampling } from "./sampling.js";
5
+ import { toWireName, fromWireName, parseToolArguments } from "./tool-protocol.js";
6
+ import { normalizeTokenUsage } from "./token-usage.js";
3
7
  const baseUrl = "https://api.meta.ai/v1";
4
8
  const modelCache = new Map();
5
9
  const CACHE_TTL_MS = 60 * 60 * 1000;
10
+ function mapMetaEffort(e) {
11
+ if (e === "none" || e === "minimal")
12
+ return "minimal";
13
+ if (e === "max" || e === "xhigh")
14
+ return "xhigh";
15
+ if (e === "low")
16
+ return "low";
17
+ if (e === "high")
18
+ return "high";
19
+ return "medium";
20
+ }
21
+ function metaReasoningPayload(reasoning) {
22
+ const enabled = Boolean(reasoning?.enabled);
23
+ const effort = reasoning?.effort ?? "medium";
24
+ const eff = mapMetaEffort(effort);
25
+ if (!enabled)
26
+ return { effort: "minimal" };
27
+ let summary;
28
+ if (eff === "xhigh" || eff === "high")
29
+ summary = "detailed";
30
+ else if (eff === "medium")
31
+ summary = "concise";
32
+ else
33
+ summary = "auto";
34
+ return { effort: eff, summary };
35
+ }
36
+ function toResponsesInput(messages, supportsVision) {
37
+ const input = [];
38
+ for (const m of messages) {
39
+ if (m.role === "system") {
40
+ input.push({
41
+ type: "message",
42
+ role: "system",
43
+ content: [{ type: "input_text", text: m.content }],
44
+ });
45
+ continue;
46
+ }
47
+ if (m.role === "user") {
48
+ const blocks = [];
49
+ if (m.content)
50
+ blocks.push({ type: "input_text", text: m.content });
51
+ if (supportsVision && m.images && m.images.length > 0) {
52
+ for (const img of m.images) {
53
+ const mt = (img.mediaType || "").toLowerCase();
54
+ const dataUrl = `data:${img.mediaType};base64,${img.dataBase64}`;
55
+ if (mt === "application/pdf") {
56
+ const filename = img.path ? img.path.split("/").pop() || "document.pdf" : "document.pdf";
57
+ blocks.push({ type: "input_file", filename, file_data: dataUrl });
58
+ }
59
+ else if (mt.startsWith("video/")) {
60
+ blocks.push({ type: "input_video", video_url: dataUrl });
61
+ }
62
+ else if (mt.startsWith("audio/")) {
63
+ blocks.push({ type: "input_audio", input_audio: { data: img.dataBase64, format: mt.includes("wav") ? "wav" : "mp3" } });
64
+ }
65
+ else {
66
+ blocks.push({ type: "input_image", image_url: dataUrl, detail: "high" });
67
+ }
68
+ }
69
+ }
70
+ if (blocks.length === 0)
71
+ blocks.push({ type: "input_text", text: "" });
72
+ input.push({ type: "message", role: "user", content: blocks });
73
+ continue;
74
+ }
75
+ if (m.role === "assistant") {
76
+ const hasTools = m.toolCalls && m.toolCalls.length > 0;
77
+ if (hasTools) {
78
+ if (m.content && m.content.trim()) {
79
+ input.push({
80
+ type: "message",
81
+ role: "assistant",
82
+ phase: "commentary",
83
+ content: [{ type: "output_text", text: m.content }],
84
+ });
85
+ }
86
+ for (const tc of m.toolCalls) {
87
+ const wire = toWireName(tc.name);
88
+ input.push({
89
+ type: "function_call",
90
+ call_id: tc.id,
91
+ name: wire,
92
+ arguments: tc.rawArguments ?? JSON.stringify(tc.args ?? {}),
93
+ });
94
+ }
95
+ continue;
96
+ }
97
+ if (m.content !== undefined && m.content !== null) {
98
+ input.push({
99
+ type: "message",
100
+ role: "assistant",
101
+ content: [{ type: "output_text", text: m.content }],
102
+ });
103
+ }
104
+ continue;
105
+ }
106
+ if (m.role === "tool") {
107
+ input.push({
108
+ type: "function_call_output",
109
+ call_id: m.toolCallId ?? `call_${Date.now()}`,
110
+ output: m.content,
111
+ });
112
+ continue;
113
+ }
114
+ }
115
+ return input;
116
+ }
117
+ function toResponsesTools(tools) {
118
+ if (!tools || tools.length === 0)
119
+ return undefined;
120
+ return tools.map((t) => ({
121
+ type: "function",
122
+ name: t.wireName,
123
+ description: t.description,
124
+ parameters: t.parameters,
125
+ }));
126
+ }
127
+ function parseMetaUsage(raw) {
128
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
129
+ return undefined;
130
+ const u = raw;
131
+ const inputTokens = u.input_tokens ??
132
+ u.prompt_tokens ??
133
+ u.inputTokens;
134
+ const outputTokens = u.output_tokens ??
135
+ u.completion_tokens ??
136
+ u.outputTokens;
137
+ const totalTokens = u.total_tokens ?? u.totalTokens;
138
+ const cached = u.input_tokens_details?.cached_tokens ??
139
+ u.prompt_tokens_details?.cached_tokens;
140
+ const reasoning = u.output_tokens_details?.reasoning_tokens ??
141
+ u.completion_tokens_details?.reasoning_tokens;
142
+ return normalizeTokenUsage({
143
+ promptTokens: inputTokens,
144
+ completionTokens: outputTokens,
145
+ totalTokens,
146
+ cachedPromptTokens: typeof cached === "number" ? cached : undefined,
147
+ reasoningTokens: typeof reasoning === "number" ? reasoning : undefined,
148
+ exact: true,
149
+ });
150
+ }
151
+ function extractReasoningSummary(item) {
152
+ if (!item || typeof item !== "object")
153
+ return "";
154
+ const obj = item;
155
+ const summary = obj.summary;
156
+ if (!Array.isArray(summary))
157
+ return "";
158
+ let out = "";
159
+ for (const s of summary) {
160
+ if (s && typeof s === "object" && typeof s.text === "string") {
161
+ out += s.text;
162
+ }
163
+ }
164
+ return out;
165
+ }
166
+ function buildResponsesBody(options) {
167
+ const reasoning = metaReasoningPayload(options.reasoning);
168
+ const input = toResponsesInput(options.messages, options.supportsVision);
169
+ const tools = toResponsesTools(options.tools);
170
+ const reasoningOn = Boolean(options.reasoning?.enabled);
171
+ const defaultMax = reasoningOn ? 8192 : 4096;
172
+ const effectiveMax = Math.max(16, options.maxTokens ?? defaultMax);
173
+ const sampling = resolveSampling({
174
+ model: options.model,
175
+ reasoningEnabled: reasoningOn,
176
+ requestedTemperature: options.temperature,
177
+ });
178
+ const body = {
179
+ model: options.model,
180
+ input,
181
+ store: false,
182
+ prompt_cache_key: "clai",
183
+ prompt_cache_retention: "24h",
184
+ include: ["reasoning.encrypted_content"],
185
+ max_output_tokens: effectiveMax,
186
+ temperature: sampling.temperature,
187
+ };
188
+ if (sampling.topP !== undefined)
189
+ body.top_p = sampling.topP;
190
+ if (reasoning)
191
+ body.reasoning = reasoning;
192
+ if (options.stream)
193
+ body.stream = true;
194
+ if (tools) {
195
+ body.tools = tools;
196
+ body.tool_choice = "auto";
197
+ body.parallel_tool_calls = options.parallelToolCalls === false ? false : true;
198
+ }
199
+ return JSON.stringify(body);
200
+ }
201
+ function parseResponsesOutput(data) {
202
+ const output = Array.isArray(data.output) ? data.output : [];
203
+ let text = "";
204
+ let reasoningSummary = "";
205
+ const toolCalls = [];
206
+ for (const item of output) {
207
+ if (!item || typeof item !== "object")
208
+ continue;
209
+ const obj = item;
210
+ if (obj.type === "message" && obj.role === "assistant") {
211
+ const content = obj.content;
212
+ if (Array.isArray(content)) {
213
+ for (const block of content) {
214
+ if (block && typeof block === "object" && block.type === "output_text" && typeof block.text === "string") {
215
+ text += block.text;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ else if (obj.type === "reasoning") {
221
+ const s = extractReasoningSummary(obj);
222
+ if (s)
223
+ reasoningSummary += s;
224
+ }
225
+ else if (obj.type === "function_call") {
226
+ const callId = typeof obj.call_id === "string" ? obj.call_id : typeof obj.id === "string" ? obj.id : `call_${toolCalls.length}`;
227
+ const nameWire = typeof obj.name === "string" ? obj.name : "";
228
+ const canonical = fromWireName(nameWire) ?? nameWire;
229
+ const rawArgs = typeof obj.arguments === "string" ? obj.arguments : JSON.stringify(obj.arguments ?? {});
230
+ let args;
231
+ try {
232
+ const parsed = JSON.parse(rawArgs);
233
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
234
+ args = parsed;
235
+ else
236
+ args = {};
237
+ }
238
+ catch {
239
+ args = parseToolArguments(rawArgs);
240
+ }
241
+ toolCalls.push({ id: callId, name: canonical, args, rawArguments: rawArgs });
242
+ }
243
+ }
244
+ const usage = parseMetaUsage(data.usage);
245
+ return { text, toolCalls, usage, reasoningSummary };
246
+ }
247
+ function foldResponsesReasoning(text, reasoningSummary, usage, effort) {
248
+ if (reasoningSummary && reasoningSummary.trim()) {
249
+ return `<think>${reasoningSummary}</think>${text}`;
250
+ }
251
+ const tokens = usage?.reasoningTokens ?? 0;
252
+ if (tokens > 0) {
253
+ const effortText = effort ? ` at ${effort} effort` : "";
254
+ const note = `Reasoning is private on Meta Model API: the model reasoned${effortText} and used ${tokens.toLocaleString("en-US")} reasoning tokens, but the API returns no reasoning text to display.`;
255
+ return `<think>${note}</think>${text}`;
256
+ }
257
+ return text;
258
+ }
259
+ function readWithAbort(reader, signal) {
260
+ if (signal.aborted) {
261
+ return Promise.reject(signal.reason ?? new Error("Stream aborted"));
262
+ }
263
+ return new Promise((resolve, reject) => {
264
+ let settled = false;
265
+ const cleanup = () => signal.removeEventListener("abort", abort);
266
+ const succeed = (value) => {
267
+ if (settled)
268
+ return;
269
+ settled = true;
270
+ cleanup();
271
+ resolve(value);
272
+ };
273
+ const fail = (error) => {
274
+ if (settled)
275
+ return;
276
+ settled = true;
277
+ cleanup();
278
+ reject(error);
279
+ };
280
+ const abort = () => {
281
+ fail(signal.reason ?? new Error("Stream aborted"));
282
+ };
283
+ signal.addEventListener("abort", abort, { once: true });
284
+ try {
285
+ void reader.read().then(succeed, fail);
286
+ }
287
+ catch (error) {
288
+ fail(error);
289
+ }
290
+ });
291
+ }
6
292
  export const metaProvider = {
7
293
  id: "meta",
8
294
  displayName: "Meta Model API",
@@ -35,53 +321,604 @@ export const metaProvider = {
35
321
  async ping(auth) {
36
322
  if (!auth.apiKey)
37
323
  throw new Error("Meta Model API key is required");
38
- await openAiCompatiblePing(baseUrl, auth.apiKey);
324
+ const response = await fetch(`${baseUrl}/models`, {
325
+ headers: { authorization: `Bearer ${auth.apiKey}` },
326
+ });
327
+ await readJson(response);
39
328
  },
40
329
  async complete(request, auth) {
41
330
  if (!auth.apiKey)
42
331
  throw new Error("Meta Model API key is required");
43
332
  const model = request.model ?? defaultModels.meta;
44
- const payload = await openAiCompatibleComplete({
45
- provider: "Meta Model API",
46
- providerId: "meta",
47
- baseUrl,
48
- apiKey: auth.apiKey,
333
+ const supportsVision = modelAcceptsImages("meta", model);
334
+ const body = buildResponsesBody({
49
335
  model,
50
336
  messages: request.messages,
51
337
  maxTokens: request.maxTokens,
52
338
  temperature: request.temperature,
53
- signal: request.signal,
339
+ stream: false,
54
340
  reasoning: request.thinking,
55
- reasoningStyle: "meta",
341
+ supportsVision,
56
342
  tools: request.tools,
57
- toolChoice: request.toolChoice,
58
343
  parallelToolCalls: request.parallelToolCalls,
59
344
  });
60
- return toCompletionResult("meta", model, payload);
345
+ let response;
346
+ try {
347
+ response = await fetch(`${baseUrl}/responses`, {
348
+ method: "POST",
349
+ signal: request.signal ?? null,
350
+ headers: {
351
+ "content-type": "application/json",
352
+ accept: "application/json",
353
+ authorization: `Bearer ${auth.apiKey}`,
354
+ },
355
+ body,
356
+ verbose: process.env.CLAI_VERBOSE === "true",
357
+ });
358
+ }
359
+ catch (error) {
360
+ if (error instanceof Error && error.name === "AbortError")
361
+ throw error;
362
+ const msg = error instanceof Error ? error.message : String(error);
363
+ throw new ProviderError(`Meta Model API request could not be sent (${msg}). Check connectivity to ${baseUrl}.`);
364
+ }
365
+ let data;
366
+ try {
367
+ data = await readJson(response);
368
+ }
369
+ catch (error) {
370
+ if (error instanceof ProviderError) {
371
+ throw new ProviderError(`Meta Model API (model=${model}): ${error.message}`, error.status, error.body, error.retryAfterSeconds);
372
+ }
373
+ throw error;
374
+ }
375
+ const parsed = parseResponsesOutput(data);
376
+ const usage = parsed.usage ?? parseMetaUsage(data.usage);
377
+ const effort = metaReasoningPayload(request.thinking)?.effort;
378
+ const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usage, effort);
379
+ if (!full.trim() && parsed.toolCalls.length === 0) {
380
+ throw new ProviderError(`Meta Model API returned no completion text (model=${model}). The response was empty — try /effort off, raise max_tokens, or pick another model with /model.`);
381
+ }
382
+ return {
383
+ text: full,
384
+ provider: "meta",
385
+ model,
386
+ ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
387
+ ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
388
+ ...(usage ? { usage } : {}),
389
+ };
61
390
  },
62
391
  async stream(request, auth, onToken) {
63
392
  if (!auth.apiKey)
64
393
  throw new Error("Meta Model API key is required");
65
394
  const model = request.model ?? defaultModels.meta;
66
- const payload = await openAiCompatibleStream({
67
- provider: "Meta Model API",
68
- providerId: "meta",
69
- baseUrl,
70
- apiKey: auth.apiKey,
395
+ const supportsVision = modelAcceptsImages("meta", model);
396
+ const reasoningOn = Boolean(request.thinking?.enabled);
397
+ const idleTimeoutMs = reasoningOn ? THINKING_STREAM_IDLE_TIMEOUT_MS : DEFAULT_STREAM_IDLE_TIMEOUT_MS;
398
+ const initialIdleTimeoutMs = reasoningOn ? THINKING_STREAM_INITIAL_IDLE_TIMEOUT_MS : idleTimeoutMs;
399
+ const outputIdleTimeoutMs = Math.round(Math.max(idleTimeoutMs, initialIdleTimeoutMs) * 1.5);
400
+ const idleController = new AbortController();
401
+ let transportTimer;
402
+ let outputTimer;
403
+ let idleFired = false;
404
+ let firedWatchdog;
405
+ let firedBudgetMs = initialIdleTimeoutMs;
406
+ let sawTransportActivity = false;
407
+ let sawStreamProgress = false;
408
+ const fireStall = (watchdog, budgetMs) => {
409
+ if (idleFired)
410
+ return;
411
+ idleFired = true;
412
+ firedWatchdog = watchdog;
413
+ firedBudgetMs = budgetMs;
414
+ idleController.abort();
415
+ };
416
+ const armTransportTimer = (budgetMs) => {
417
+ if (transportTimer)
418
+ clearTimeout(transportTimer);
419
+ transportTimer = setTimeout(() => fireStall("transport", budgetMs), budgetMs);
420
+ };
421
+ const noteTransportActivity = () => {
422
+ sawTransportActivity = true;
423
+ armTransportTimer(idleTimeoutMs);
424
+ };
425
+ const resetIdleTimer = () => {
426
+ sawStreamProgress = true;
427
+ noteTransportActivity();
428
+ if (outputTimer)
429
+ clearTimeout(outputTimer);
430
+ outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
431
+ };
432
+ armTransportTimer(initialIdleTimeoutMs);
433
+ outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
434
+ const clearIdleTimers = () => {
435
+ if (transportTimer)
436
+ clearTimeout(transportTimer);
437
+ if (outputTimer)
438
+ clearTimeout(outputTimer);
439
+ transportTimer = undefined;
440
+ outputTimer = undefined;
441
+ };
442
+ const onCallerAbort = () => idleController.abort(request.signal?.reason);
443
+ request.signal?.addEventListener("abort", onCallerAbort, { once: true });
444
+ const body = buildResponsesBody({
71
445
  model,
72
446
  messages: request.messages,
73
447
  maxTokens: request.maxTokens,
74
448
  temperature: request.temperature,
75
- signal: request.signal,
76
- onToken,
77
- onToolCallDelta: request.onToolCallDelta,
449
+ stream: true,
78
450
  reasoning: request.thinking,
79
- reasoningStyle: "meta",
451
+ supportsVision,
80
452
  tools: request.tools,
81
- toolChoice: request.toolChoice,
82
453
  parallelToolCalls: request.parallelToolCalls,
83
454
  });
84
- return toCompletionResult("meta", model, payload);
455
+ let response;
456
+ try {
457
+ response = await fetch(`${baseUrl}/responses`, {
458
+ method: "POST",
459
+ signal: idleController.signal,
460
+ headers: {
461
+ "content-type": "application/json",
462
+ accept: "text/event-stream",
463
+ authorization: `Bearer ${auth.apiKey}`,
464
+ },
465
+ body,
466
+ verbose: process.env.CLAI_VERBOSE === "true",
467
+ });
468
+ }
469
+ catch (error) {
470
+ clearIdleTimers();
471
+ request.signal?.removeEventListener("abort", onCallerAbort);
472
+ if (idleFired) {
473
+ throw new ProviderError(`Meta Model API request timed out before any response (${Math.round(firedBudgetMs / 1000)}s)`);
474
+ }
475
+ throw error;
476
+ }
477
+ if (!response.ok) {
478
+ clearIdleTimers();
479
+ request.signal?.removeEventListener("abort", onCallerAbort);
480
+ try {
481
+ await readJson(response);
482
+ }
483
+ catch (error) {
484
+ if (error instanceof ProviderError) {
485
+ throw new ProviderError(`Meta Model API (model=${model}): ${error.message}`, error.status, error.body, error.retryAfterSeconds);
486
+ }
487
+ throw error;
488
+ }
489
+ }
490
+ if (!response.body) {
491
+ clearIdleTimers();
492
+ request.signal?.removeEventListener("abort", onCallerAbort);
493
+ throw new ProviderError(`Meta Model API returned no stream body`);
494
+ }
495
+ const contentType = response.headers.get("content-type") ?? "";
496
+ if (response.status === 202 || /\bapplication\/json\b/i.test(contentType)) {
497
+ clearIdleTimers();
498
+ request.signal?.removeEventListener("abort", onCallerAbort);
499
+ const data = await readJson(response);
500
+ if (response.status === 202) {
501
+ const requestId = data.requestId ?? data.id;
502
+ throw new ProviderError(`Meta Model API returned a pending async response${requestId ? ` (${requestId})` : ""}; streaming did not start.`, response.status, JSON.stringify(data).slice(0, 1_000));
503
+ }
504
+ const parsed = parseResponsesOutput(data);
505
+ const usageTmp = parsed.usage ?? parseMetaUsage(data.usage);
506
+ const effortTmp = metaReasoningPayload(request.thinking)?.effort;
507
+ const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usageTmp, effortTmp);
508
+ if (full.trim() || parsed.toolCalls.length > 0) {
509
+ if (full.trim())
510
+ onToken(full);
511
+ return {
512
+ text: full,
513
+ provider: "meta",
514
+ model,
515
+ ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
516
+ ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
517
+ ...(usageTmp ? { usage: usageTmp } : {}),
518
+ };
519
+ }
520
+ throw new ProviderError(`Meta Model API returned JSON instead of an SSE stream, but no completion text was present.`, response.status, JSON.stringify(data).slice(0, 1_000));
521
+ }
522
+ const decoder = new TextDecoder();
523
+ const reader = response.body.getReader();
524
+ let buffer = "";
525
+ let full = "";
526
+ let visible = "";
527
+ let reasoningSeen = "";
528
+ let inReasoning = false;
529
+ let finishReason;
530
+ let streamUsage;
531
+ const toolCallState = new Map();
532
+ const outputIndexToItemId = new Map();
533
+ let responseId;
534
+ const enterReasoning = () => {
535
+ if (inReasoning)
536
+ return;
537
+ inReasoning = true;
538
+ full += "<think>";
539
+ onToken("<think>");
540
+ };
541
+ const exitReasoning = () => {
542
+ if (!inReasoning)
543
+ return;
544
+ inReasoning = false;
545
+ full += "</think>";
546
+ onToken("</think>");
547
+ };
548
+ const emitVisible = (text) => {
549
+ if (!text)
550
+ return;
551
+ if (inReasoning)
552
+ exitReasoning();
553
+ visible += text;
554
+ full += text;
555
+ onToken(text);
556
+ };
557
+ const emitReasoningDelta = (text) => {
558
+ if (!text)
559
+ return;
560
+ enterReasoning();
561
+ reasoningSeen += text;
562
+ full += text;
563
+ onToken(text);
564
+ };
565
+ const cleanup = () => {
566
+ clearIdleTimers();
567
+ request.signal?.removeEventListener("abort", onCallerAbort);
568
+ idleController.signal.removeEventListener("abort", cancelReaderOnAbort);
569
+ };
570
+ const cancelReaderOnAbort = () => {
571
+ reader.cancel().catch(() => undefined);
572
+ };
573
+ idleController.signal.addEventListener("abort", cancelReaderOnAbort, { once: true });
574
+ const sseFrames = createSseFrameAssembler();
575
+ try {
576
+ while (true) {
577
+ request.signal?.throwIfAborted();
578
+ if (idleController.signal.aborted)
579
+ throw new Error("Stream aborted");
580
+ const { done, value } = await readWithAbort(reader, idleController.signal);
581
+ request.signal?.throwIfAborted();
582
+ if (idleController.signal.aborted)
583
+ throw new Error("Stream aborted");
584
+ if (done)
585
+ break;
586
+ if (value && value.byteLength > 0)
587
+ noteTransportActivity();
588
+ buffer += decoder.decode(value, { stream: true });
589
+ const lines = buffer.split("\n");
590
+ buffer = lines.pop() ?? "";
591
+ for (const line of lines) {
592
+ const payload = sseFrames.pushLine(line);
593
+ if (payload === undefined)
594
+ continue;
595
+ if (payload === "[DONE]") {
596
+ if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
597
+ const effort = metaReasoningPayload(request.thinking)?.effort;
598
+ const effortText = effort ? ` at ${effort} effort` : "";
599
+ const note = `Reasoning is private on Meta Model API: the model reasoned${effortText} and used ${streamUsage.reasoningTokens.toLocaleString("en-US")} reasoning tokens, but the API returns no reasoning text to display.`;
600
+ emitReasoningDelta(note);
601
+ exitReasoning();
602
+ }
603
+ else {
604
+ exitReasoning();
605
+ }
606
+ cleanup();
607
+ const toolCalls = [];
608
+ for (const [, state] of toolCallState) {
609
+ if (!state.name)
610
+ continue;
611
+ const canonical = state.name ? fromWireName(state.name) ?? state.name : state.name ?? "";
612
+ const raw = state.arguments;
613
+ let args;
614
+ try {
615
+ const parsed = JSON.parse(raw);
616
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
617
+ args = parsed;
618
+ else
619
+ args = {};
620
+ }
621
+ catch {
622
+ args = parseToolArguments(raw);
623
+ }
624
+ toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
625
+ }
626
+ if (!visible.trim() && toolCalls.length === 0) {
627
+ if (reasoningSeen.trim()) {
628
+ return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}) };
629
+ }
630
+ throw new ProviderError(`Meta Model API completed without a visible answer.`);
631
+ }
632
+ return {
633
+ text: full,
634
+ provider: "meta",
635
+ model,
636
+ ...(toolCalls.length ? { toolCalls } : {}),
637
+ ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
638
+ ...(streamUsage ? { usage: streamUsage } : {}),
639
+ };
640
+ }
641
+ let parsed;
642
+ try {
643
+ parsed = JSON.parse(payload);
644
+ }
645
+ catch {
646
+ continue;
647
+ }
648
+ if (parsed.error) {
649
+ const detail = typeof parsed.error === "string"
650
+ ? parsed.error
651
+ : parsed.error.message ?? parsed.error.type ?? "unknown error";
652
+ throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 500));
653
+ }
654
+ const type = parsed.type;
655
+ if (type === "response.created" || type === "response.in_progress") {
656
+ const resp = (parsed.response ?? parsed);
657
+ if (typeof resp.id === "string")
658
+ responseId = resp.id;
659
+ continue;
660
+ }
661
+ if (type === "response.output_item.added") {
662
+ const item = parsed.item;
663
+ if (!item)
664
+ continue;
665
+ const outputIndex = typeof parsed.output_index === "number" ? parsed.output_index : undefined;
666
+ const itemId = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
667
+ if (outputIndex !== undefined && itemId)
668
+ outputIndexToItemId.set(outputIndex, itemId);
669
+ if (item.type === "function_call") {
670
+ const id = typeof item.id === "string" ? item.id : typeof item.call_id === "string" ? item.call_id : itemId ?? `call_${toolCallState.size}`;
671
+ const callId = typeof item.call_id === "string" ? item.call_id : id;
672
+ const name = typeof item.name === "string" ? item.name : "";
673
+ const args = typeof item.arguments === "string" ? item.arguments : "";
674
+ toolCallState.set(id, { id, callId, name, arguments: args });
675
+ resetIdleTimer();
676
+ if (request.onToolCallDelta) {
677
+ const canonical = name ? fromWireName(name) ?? name : undefined;
678
+ request.onToolCallDelta({ index: toolCallState.size - 1, ...(callId ? { id: callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: args.length });
679
+ }
680
+ }
681
+ else if (item.type === "reasoning") {
682
+ const s = extractReasoningSummary(item);
683
+ if (s) {
684
+ resetIdleTimer();
685
+ emitReasoningDelta(s);
686
+ }
687
+ }
688
+ else if (item.type === "message") {
689
+ resetIdleTimer();
690
+ }
691
+ continue;
692
+ }
693
+ if (type === "response.output_item.done") {
694
+ const item = parsed.item;
695
+ if (item?.type === "function_call") {
696
+ const id = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
697
+ if (id && toolCallState.has(id)) {
698
+ const state = toolCallState.get(id);
699
+ if (typeof item.arguments === "string" && item.arguments.length > state.arguments.length)
700
+ state.arguments = item.arguments;
701
+ if (typeof item.name === "string" && !state.name)
702
+ state.name = item.name;
703
+ if (typeof item.call_id === "string" && !state.callId)
704
+ state.callId = item.call_id;
705
+ }
706
+ resetIdleTimer();
707
+ }
708
+ if (item && typeof item.status === "string")
709
+ finishReason = item.status;
710
+ continue;
711
+ }
712
+ if (type === "response.content_part.added" || type === "response.content_part.done") {
713
+ continue;
714
+ }
715
+ if (type === "response.output_text.delta") {
716
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
717
+ if (delta) {
718
+ resetIdleTimer();
719
+ emitVisible(delta);
720
+ }
721
+ continue;
722
+ }
723
+ if (type === "response.reasoning_summary_text.delta") {
724
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
725
+ if (delta) {
726
+ resetIdleTimer();
727
+ emitReasoningDelta(delta);
728
+ }
729
+ continue;
730
+ }
731
+ if (type === "response.reasoning_summary_text.done") {
732
+ const textVal = typeof parsed.text === "string" ? parsed.text : "";
733
+ if (textVal && !reasoningSeen.includes(textVal)) {
734
+ const remaining = textVal.slice(reasoningSeen.length);
735
+ if (remaining) {
736
+ resetIdleTimer();
737
+ emitReasoningDelta(remaining);
738
+ }
739
+ }
740
+ exitReasoning();
741
+ continue;
742
+ }
743
+ if (type === "response.function_call_arguments.delta") {
744
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
745
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : typeof parsed.itemId === "string" ? parsed.itemId : undefined;
746
+ let targetId = itemId;
747
+ if (!targetId && typeof parsed.output_index === "number")
748
+ targetId = outputIndexToItemId.get(parsed.output_index);
749
+ if (targetId) {
750
+ const state = toolCallState.get(targetId);
751
+ if (state) {
752
+ state.arguments += delta;
753
+ resetIdleTimer();
754
+ if (request.onToolCallDelta) {
755
+ const canonical = state.name ? fromWireName(state.name) ?? state.name : undefined;
756
+ request.onToolCallDelta({ index: Array.from(toolCallState.keys()).indexOf(targetId), ...(state.callId ? { id: state.callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: state.arguments.length });
757
+ }
758
+ }
759
+ else {
760
+ toolCallState.set(targetId, { id: targetId, callId: targetId, name: "", arguments: delta });
761
+ resetIdleTimer();
762
+ }
763
+ }
764
+ else if (delta) {
765
+ const anyKey = Array.from(toolCallState.keys()).pop();
766
+ if (anyKey) {
767
+ const state = toolCallState.get(anyKey);
768
+ state.arguments += delta;
769
+ resetIdleTimer();
770
+ }
771
+ }
772
+ continue;
773
+ }
774
+ if (type === "response.function_call_arguments.done") {
775
+ const args = typeof parsed.arguments === "string" ? parsed.arguments : typeof parsed.argument === "string" ? parsed.argument : "";
776
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : undefined;
777
+ let targetId = itemId;
778
+ if (!targetId && typeof parsed.output_index === "number")
779
+ targetId = outputIndexToItemId.get(parsed.output_index);
780
+ if (targetId && toolCallState.has(targetId) && args) {
781
+ toolCallState.get(targetId).arguments = args;
782
+ }
783
+ else if (args && toolCallState.size > 0) {
784
+ const lastKey = Array.from(toolCallState.keys()).pop();
785
+ if (!toolCallState.get(lastKey).arguments)
786
+ toolCallState.get(lastKey).arguments = args;
787
+ }
788
+ resetIdleTimer();
789
+ continue;
790
+ }
791
+ if (type === "response.completed") {
792
+ const resp = (parsed.response ?? parsed);
793
+ if (resp.usage) {
794
+ const u = parseMetaUsage(resp.usage);
795
+ if (u)
796
+ streamUsage = u;
797
+ }
798
+ if (typeof resp.status === "string")
799
+ finishReason = resp.status;
800
+ if (Array.isArray(resp.output)) {
801
+ const out = parseResponsesOutput(resp);
802
+ if (out.reasoningSummary && !reasoningSeen.trim()) {
803
+ emitReasoningDelta(out.reasoningSummary);
804
+ exitReasoning();
805
+ }
806
+ if (out.text && !visible.trim()) {
807
+ emitVisible(out.text);
808
+ }
809
+ for (const tc of out.toolCalls) {
810
+ const exists = Array.from(toolCallState.values()).some((s) => s.callId === tc.id);
811
+ if (!exists) {
812
+ const id = tc.id;
813
+ toolCallState.set(id, { id, callId: tc.id, name: toWireName(tc.name), arguments: tc.rawArguments ?? JSON.stringify(tc.args) });
814
+ }
815
+ }
816
+ }
817
+ continue;
818
+ }
819
+ if (type === "response.failed" || type === "response.incomplete") {
820
+ const resp = (parsed.response ?? parsed);
821
+ const err = resp.error;
822
+ const detail = err?.message ?? err?.code ?? type;
823
+ throw new ProviderError(`Meta Model API stream error: ${String(detail)}`, undefined, payload.slice(0, 500));
824
+ }
825
+ const usageField = parsed.usage;
826
+ if (usageField) {
827
+ const u = parseMetaUsage(usageField);
828
+ if (u) {
829
+ streamUsage = u;
830
+ resetIdleTimer();
831
+ }
832
+ }
833
+ const choice = parsed.choices;
834
+ if (choice) {
835
+ const chunkUsage = parseMetaUsage(parsed.usage);
836
+ if (chunkUsage)
837
+ streamUsage = chunkUsage;
838
+ }
839
+ }
840
+ }
841
+ if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
842
+ const effort = metaReasoningPayload(request.thinking)?.effort;
843
+ const effortText = effort ? ` at ${effort} effort` : "";
844
+ const note = `Reasoning is private on Meta Model API: the model reasoned${effortText} and used ${streamUsage.reasoningTokens.toLocaleString("en-US")} reasoning tokens, but the API returns no reasoning text to display.`;
845
+ if (!inReasoning) {
846
+ full += "<think>";
847
+ visible = full;
848
+ onToken("<think>");
849
+ }
850
+ full += note;
851
+ reasoningSeen += note;
852
+ onToken(note);
853
+ full += "</think>";
854
+ onToken("</think>");
855
+ inReasoning = false;
856
+ }
857
+ else {
858
+ exitReasoning();
859
+ }
860
+ cleanup();
861
+ const toolCalls = [];
862
+ for (const [, state] of toolCallState) {
863
+ if (!state.name && !state.arguments)
864
+ continue;
865
+ const name = state.name || "";
866
+ const canonical = name ? fromWireName(name) ?? name : "";
867
+ if (!canonical)
868
+ continue;
869
+ const raw = state.arguments;
870
+ let args;
871
+ try {
872
+ const parsed = JSON.parse(raw || "{}");
873
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
874
+ args = parsed;
875
+ else
876
+ args = {};
877
+ }
878
+ catch {
879
+ args = parseToolArguments(raw);
880
+ }
881
+ toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
882
+ }
883
+ if (!visible.trim() && toolCalls.length === 0) {
884
+ if (reasoningSeen.trim()) {
885
+ return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}) };
886
+ }
887
+ throw new ProviderError(`Meta Model API completed without a visible answer.`);
888
+ }
889
+ return {
890
+ text: full,
891
+ provider: "meta",
892
+ model,
893
+ ...(toolCalls.length ? { toolCalls } : {}),
894
+ ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
895
+ ...(streamUsage ? { usage: streamUsage } : {}),
896
+ };
897
+ }
898
+ catch (error) {
899
+ if (idleFired) {
900
+ const seconds = Math.round(firedBudgetMs / 1000);
901
+ if (firedWatchdog === "transport" || !sawTransportActivity) {
902
+ if (!sawTransportActivity) {
903
+ throw new ProviderError(`Meta Model API request timed out before any response (${seconds}s) — no data arrived on the connection.`);
904
+ }
905
+ throw new ProviderError(`Meta Model API stream transport timeout (${seconds}s) — no data arrived on the connection after it had started.`);
906
+ }
907
+ throw new ProviderError(`Meta Model API stream stalled — ${STREAM_STALL_MARKER} for ${seconds}s` +
908
+ (sawStreamProgress
909
+ ? " after it had already started producing output. The connection stayed open, so the model was most likely buffering one very large tool call. Split large writes into smaller sequential calls, or try a smaller model / disable thinking with /effort off."
910
+ : " — the connection stayed open but the model never produced anything. Try another model, or disable thinking with /effort off."));
911
+ }
912
+ throw error;
913
+ }
914
+ finally {
915
+ cleanup();
916
+ void reader.cancel().catch(() => undefined);
917
+ try {
918
+ reader.releaseLock();
919
+ }
920
+ catch { }
921
+ }
85
922
  },
86
923
  };
87
924
  //# sourceMappingURL=meta.js.map