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