@pentoshi/clai 4.1.0 → 4.2.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 (264) hide show
  1. package/README.md +14 -0
  2. package/dist/agent/compaction-executor.d.ts +68 -0
  3. package/dist/agent/compaction-executor.js +343 -0
  4. package/dist/agent/compaction-executor.js.map +1 -0
  5. package/dist/agent/context-breakdown.js +2 -0
  6. package/dist/agent/context-breakdown.js.map +1 -1
  7. package/dist/agent/context-manager.d.ts +23 -9
  8. package/dist/agent/context-manager.js +72 -43
  9. package/dist/agent/context-manager.js.map +1 -1
  10. package/dist/agent/events.d.ts +3 -0
  11. package/dist/agent/loop-guard.d.ts +2 -0
  12. package/dist/agent/loop-guard.js +48 -3
  13. package/dist/agent/loop-guard.js.map +1 -1
  14. package/dist/agent/prompt-composer.js +6 -6
  15. package/dist/agent/prompt-composer.js.map +1 -1
  16. package/dist/agent/reliability-policy.d.ts +1 -0
  17. package/dist/agent/reliability-policy.js +6 -3
  18. package/dist/agent/reliability-policy.js.map +1 -1
  19. package/dist/agent/request-accounting.d.ts +97 -0
  20. package/dist/agent/request-accounting.js +207 -0
  21. package/dist/agent/request-accounting.js.map +1 -0
  22. package/dist/agent/request-budget.d.ts +2 -2
  23. package/dist/agent/request-budget.js +2 -4
  24. package/dist/agent/request-budget.js.map +1 -1
  25. package/dist/agent/runner.d.ts +8 -2
  26. package/dist/agent/runner.js +409 -178
  27. package/dist/agent/runner.js.map +1 -1
  28. package/dist/agent/session-title.d.ts +0 -26
  29. package/dist/agent/session-title.js +0 -80
  30. package/dist/agent/session-title.js.map +1 -1
  31. package/dist/agent/tool-history.d.ts +2 -2
  32. package/dist/agent/tool-history.js +19 -6
  33. package/dist/agent/tool-history.js.map +1 -1
  34. package/dist/app/adapters/agent-event-adapter.js +19 -0
  35. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  36. package/dist/app/adapters/current-agent-adapter.js +4 -0
  37. package/dist/app/adapters/current-agent-adapter.js.map +1 -1
  38. package/dist/app/commands/catalog.js +24 -0
  39. package/dist/app/commands/catalog.js.map +1 -1
  40. package/dist/app/controllers/cancel-coordinator.d.ts +51 -0
  41. package/dist/app/controllers/cancel-coordinator.js +48 -0
  42. package/dist/app/controllers/cancel-coordinator.js.map +1 -0
  43. package/dist/app/controllers/session-compact-helper.d.ts +19 -2
  44. package/dist/app/controllers/session-compact-helper.js +100 -127
  45. package/dist/app/controllers/session-compact-helper.js.map +1 -1
  46. package/dist/app/controllers/session-context-usage.d.ts +32 -15
  47. package/dist/app/controllers/session-context-usage.js +186 -51
  48. package/dist/app/controllers/session-context-usage.js.map +1 -1
  49. package/dist/app/controllers/session-controller.d.ts +20 -11
  50. package/dist/app/controllers/session-controller.js +111 -90
  51. package/dist/app/controllers/session-controller.js.map +1 -1
  52. package/dist/app/controllers/session-naming.d.ts +31 -0
  53. package/dist/app/controllers/session-naming.js +173 -0
  54. package/dist/app/controllers/session-naming.js.map +1 -0
  55. package/dist/app/controllers/session-persistence.d.ts +2 -2
  56. package/dist/app/controllers/session-persistence.js +3 -6
  57. package/dist/app/controllers/session-persistence.js.map +1 -1
  58. package/dist/app/controllers/session-turn-request.d.ts +2 -1
  59. package/dist/app/controllers/session-turn-request.js +3 -0
  60. package/dist/app/controllers/session-turn-request.js.map +1 -1
  61. package/dist/app/controllers/turn-controller.d.ts +2 -1
  62. package/dist/app/controllers/turn-controller.js +1 -0
  63. package/dist/app/controllers/turn-controller.js.map +1 -1
  64. package/dist/app/events/app-event.d.ts +9 -0
  65. package/dist/app/events/app-event.js.map +1 -1
  66. package/dist/app/ports/agent-port.d.ts +8 -1
  67. package/dist/classic/app/ClassicApp.js +2 -1
  68. package/dist/classic/app/ClassicApp.js.map +1 -1
  69. package/dist/classic/app/app-wiring.js +1 -2
  70. package/dist/classic/app/app-wiring.js.map +1 -1
  71. package/dist/classic/app/wiring-interactions.js +2 -7
  72. package/dist/classic/app/wiring-interactions.js.map +1 -1
  73. package/dist/classic/blocks/tool-lines.js +2 -1
  74. package/dist/classic/blocks/tool-lines.js.map +1 -1
  75. package/dist/classic/input/cancel-ladder.d.ts +2 -19
  76. package/dist/classic/input/cancel-ladder.js +13 -20
  77. package/dist/classic/input/cancel-ladder.js.map +1 -1
  78. package/dist/commands/providers.js +2 -0
  79. package/dist/commands/providers.js.map +1 -1
  80. package/dist/llm/adapters/anthropic-tools.d.ts +31 -14
  81. package/dist/llm/adapters/anthropic-tools.js +124 -29
  82. package/dist/llm/adapters/anthropic-tools.js.map +1 -1
  83. package/dist/llm/adapters/gemini-tools.d.ts +16 -4
  84. package/dist/llm/adapters/gemini-tools.js +117 -16
  85. package/dist/llm/adapters/gemini-tools.js.map +1 -1
  86. package/dist/llm/adapters/openai-tools.d.ts +13 -2
  87. package/dist/llm/adapters/openai-tools.js +58 -11
  88. package/dist/llm/adapters/openai-tools.js.map +1 -1
  89. package/dist/llm/agentrouter.js +68 -45
  90. package/dist/llm/agentrouter.js.map +1 -1
  91. package/dist/llm/anthropic.js +69 -36
  92. package/dist/llm/anthropic.js.map +1 -1
  93. package/dist/llm/aws-mantle.js +70 -32
  94. package/dist/llm/aws-mantle.js.map +1 -1
  95. package/dist/llm/bynara.js +26 -69
  96. package/dist/llm/bynara.js.map +1 -1
  97. package/dist/llm/capabilities.d.ts +2 -1
  98. package/dist/llm/capabilities.js +47 -0
  99. package/dist/llm/capabilities.js.map +1 -1
  100. package/dist/llm/context-snapshot.d.ts +100 -0
  101. package/dist/llm/context-snapshot.js +277 -0
  102. package/dist/llm/context-snapshot.js.map +1 -0
  103. package/dist/llm/context-windows.d.ts +10 -0
  104. package/dist/llm/context-windows.js +129 -0
  105. package/dist/llm/context-windows.js.map +1 -0
  106. package/dist/llm/custom-provider-profile.d.ts +75 -0
  107. package/dist/llm/custom-provider-profile.js +558 -0
  108. package/dist/llm/custom-provider-profile.js.map +1 -0
  109. package/dist/llm/custom-providers.d.ts +13 -1
  110. package/dist/llm/custom-providers.js +77 -25
  111. package/dist/llm/custom-providers.js.map +1 -1
  112. package/dist/llm/effort-fallback.d.ts +44 -0
  113. package/dist/llm/effort-fallback.js +101 -0
  114. package/dist/llm/effort-fallback.js.map +1 -0
  115. package/dist/llm/fireworks.js +4 -0
  116. package/dist/llm/fireworks.js.map +1 -1
  117. package/dist/llm/free.js +4 -0
  118. package/dist/llm/free.js.map +1 -1
  119. package/dist/llm/gemini.d.ts +1 -1
  120. package/dist/llm/gemini.js +89 -51
  121. package/dist/llm/gemini.js.map +1 -1
  122. package/dist/llm/groq.js +4 -0
  123. package/dist/llm/groq.js.map +1 -1
  124. package/dist/llm/hetzner.js +4 -0
  125. package/dist/llm/hetzner.js.map +1 -1
  126. package/dist/llm/http.d.ts +49 -10
  127. package/dist/llm/http.js +417 -153
  128. package/dist/llm/http.js.map +1 -1
  129. package/dist/llm/kimchi.js +4 -0
  130. package/dist/llm/kimchi.js.map +1 -1
  131. package/dist/llm/lightning.js +4 -0
  132. package/dist/llm/lightning.js.map +1 -1
  133. package/dist/llm/meta.d.ts +2 -0
  134. package/dist/llm/meta.js +811 -655
  135. package/dist/llm/meta.js.map +1 -1
  136. package/dist/llm/modal.js +4 -0
  137. package/dist/llm/modal.js.map +1 -1
  138. package/dist/llm/model-families.d.ts +9 -0
  139. package/dist/llm/model-families.js +39 -0
  140. package/dist/llm/model-families.js.map +1 -0
  141. package/dist/llm/nvidia.js +4 -0
  142. package/dist/llm/nvidia.js.map +1 -1
  143. package/dist/llm/ollama.js +44 -40
  144. package/dist/llm/ollama.js.map +1 -1
  145. package/dist/llm/openai.js +4 -0
  146. package/dist/llm/openai.js.map +1 -1
  147. package/dist/llm/openrouter.js +4 -0
  148. package/dist/llm/openrouter.js.map +1 -1
  149. package/dist/llm/operation-ledger.d.ts +60 -0
  150. package/dist/llm/operation-ledger.js +136 -0
  151. package/dist/llm/operation-ledger.js.map +1 -0
  152. package/dist/llm/operation-usage.d.ts +32 -0
  153. package/dist/llm/operation-usage.js +172 -0
  154. package/dist/llm/operation-usage.js.map +1 -0
  155. package/dist/llm/orcarouter.d.ts +7 -0
  156. package/dist/llm/orcarouter.js +166 -0
  157. package/dist/llm/orcarouter.js.map +1 -0
  158. package/dist/llm/provider-profile-layers.d.ts +7 -0
  159. package/dist/llm/provider-profile-layers.js +961 -0
  160. package/dist/llm/provider-profile-layers.js.map +1 -0
  161. package/dist/llm/provider-profile.d.ts +181 -0
  162. package/dist/llm/provider-profile.js +329 -0
  163. package/dist/llm/provider-profile.js.map +1 -0
  164. package/dist/llm/provider-profiles.d.ts +13 -0
  165. package/dist/llm/provider-profiles.js +86 -0
  166. package/dist/llm/provider-profiles.js.map +1 -0
  167. package/dist/llm/provider.d.ts +2 -0
  168. package/dist/llm/provider.js +67 -0
  169. package/dist/llm/provider.js.map +1 -1
  170. package/dist/llm/qwen-cloud.js +4 -0
  171. package/dist/llm/qwen-cloud.js.map +1 -1
  172. package/dist/llm/reasoning-artifacts.d.ts +101 -0
  173. package/dist/llm/reasoning-artifacts.js +440 -0
  174. package/dist/llm/reasoning-artifacts.js.map +1 -0
  175. package/dist/llm/request-fingerprint.d.ts +10 -0
  176. package/dist/llm/request-fingerprint.js +313 -0
  177. package/dist/llm/request-fingerprint.js.map +1 -0
  178. package/dist/llm/request-plan.d.ts +119 -0
  179. package/dist/llm/request-plan.js +279 -0
  180. package/dist/llm/request-plan.js.map +1 -0
  181. package/dist/llm/router.d.ts +19 -1
  182. package/dist/llm/router.js +447 -75
  183. package/dist/llm/router.js.map +1 -1
  184. package/dist/llm/stream-events.d.ts +47 -0
  185. package/dist/llm/stream-events.js +55 -0
  186. package/dist/llm/stream-events.js.map +1 -0
  187. package/dist/llm/stream-terminal.d.ts +30 -0
  188. package/dist/llm/stream-terminal.js +51 -0
  189. package/dist/llm/stream-terminal.js.map +1 -0
  190. package/dist/llm/token-estimate-calibration.d.ts +23 -0
  191. package/dist/llm/token-estimate-calibration.js +140 -0
  192. package/dist/llm/token-estimate-calibration.js.map +1 -0
  193. package/dist/llm/token-usage.d.ts +35 -5
  194. package/dist/llm/token-usage.js +212 -162
  195. package/dist/llm/token-usage.js.map +1 -1
  196. package/dist/llm/tokenrouter.js +8 -2
  197. package/dist/llm/tokenrouter.js.map +1 -1
  198. package/dist/modes/ask.js +11 -5
  199. package/dist/modes/ask.js.map +1 -1
  200. package/dist/noninteractive/start-noninteractive.js +31 -2
  201. package/dist/noninteractive/start-noninteractive.js.map +1 -1
  202. package/dist/noninteractive/stream-blocks.js +15 -4
  203. package/dist/noninteractive/stream-blocks.js.map +1 -1
  204. package/dist/prompts/embedded.js +1 -1
  205. package/dist/prompts/embedded.js.map +1 -1
  206. package/dist/prompts/system.agent.md +6 -0
  207. package/dist/store/config.js +5 -1
  208. package/dist/store/config.js.map +1 -1
  209. package/dist/store/history.d.ts +2 -0
  210. package/dist/store/history.js +19 -10
  211. package/dist/store/history.js.map +1 -1
  212. package/dist/store/plan.js +20 -14
  213. package/dist/store/plan.js.map +1 -1
  214. package/dist/tools/elevated-shell.d.ts +1 -1
  215. package/dist/tools/elevated-shell.js +24 -72
  216. package/dist/tools/elevated-shell.js.map +1 -1
  217. package/dist/tools/nmap-runner.js +30 -36
  218. package/dist/tools/nmap-runner.js.map +1 -1
  219. package/dist/tools/sudo-session.d.ts +83 -0
  220. package/dist/tools/sudo-session.js +124 -0
  221. package/dist/tools/sudo-session.js.map +1 -0
  222. package/dist/tui-v2/app/App.js +9 -24
  223. package/dist/tui-v2/app/App.js.map +1 -1
  224. package/dist/tui-v2/components/status/context-limit-chip.js +2 -2
  225. package/dist/tui-v2/components/status/context-limit-chip.js.map +1 -1
  226. package/dist/tui-v2/components/transcript/intro-card.js +2 -1
  227. package/dist/tui-v2/components/transcript/intro-card.js.map +1 -1
  228. package/dist/tui-v2/composer/composer-editor.js +2 -1
  229. package/dist/tui-v2/composer/composer-editor.js.map +1 -1
  230. package/dist/types.d.ts +156 -1
  231. package/dist/types.js +1 -0
  232. package/dist/types.js.map +1 -1
  233. package/dist/ui-core/bootstrap/composition-root.d.ts +2 -0
  234. package/dist/ui-core/bootstrap/composition-root.js +26 -3
  235. package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
  236. package/dist/ui-core/commands/picker-commands.js +11 -5
  237. package/dist/ui-core/commands/picker-commands.js.map +1 -1
  238. package/dist/ui-core/commands/session-commands.js +41 -5
  239. package/dist/ui-core/commands/session-commands.js.map +1 -1
  240. package/dist/ui-core/plan/plan-lifecycle.js +3 -2
  241. package/dist/ui-core/plan/plan-lifecycle.js.map +1 -1
  242. package/dist/ui-core/rendering/context-limit.js +4 -1
  243. package/dist/ui-core/rendering/context-limit.js.map +1 -1
  244. package/dist/ui-core/rendering/duration.d.ts +2 -1
  245. package/dist/ui-core/rendering/duration.js +13 -5
  246. package/dist/ui-core/rendering/duration.js.map +1 -1
  247. package/dist/ui-core/rendering/intro-header.js +2 -2
  248. package/dist/ui-core/rendering/intro-header.js.map +1 -1
  249. package/dist/ui-core/rendering/theme.d.ts +1 -0
  250. package/dist/ui-core/rendering/theme.js +2 -0
  251. package/dist/ui-core/rendering/theme.js.map +1 -1
  252. package/dist/ui-core/state/transcript-hydrate.js +3 -3
  253. package/dist/ui-core/state/transcript-hydrate.js.map +1 -1
  254. package/dist/ui-core/state/transcript-reducer.d.ts +5 -0
  255. package/dist/ui-core/state/transcript-reducer.js +76 -3
  256. package/dist/ui-core/state/transcript-reducer.js.map +1 -1
  257. package/dist/ui-core/state/transcript-store.d.ts +10 -1
  258. package/dist/ui-core/state/transcript-store.js +22 -1
  259. package/dist/ui-core/state/transcript-store.js.map +1 -1
  260. package/dist/ui-core/state/transcript-types.d.ts +2 -0
  261. package/dist/ui-core/state/transcript-types.js.map +1 -1
  262. package/dist/version.generated.d.ts +2 -2
  263. package/dist/version.generated.js +2 -2
  264. package/package.json +6 -1
package/dist/llm/meta.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { defaultModels } from "./provider.js";
2
2
  import { readJson, ingestOpenAiModelCatalog, ProviderError, createSseFrameAssembler, 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";
3
+ import { completeGenerationAttempt, generationFetch, runGenerationAttempt, } from "./operation-usage.js";
4
+ import { isOperationPolicyError } from "./operation-ledger.js";
5
5
  import { toWireName, fromWireName, parseToolArguments } from "./tool-protocol.js";
6
6
  import { normalizeTokenUsage } from "./token-usage.js";
7
- import { REASONING_CLOSE, REASONING_OPEN, wrapReasoning, } from "./reasoning-marker.js";
7
+ import { createReasoningArtifact, createReasoningArtifactProvenance, reasoningArtifactItems, reasoningArtifactsForMessage, selectReasoningArtifactsForReplay, } from "./reasoning-artifacts.js";
8
+ import { compileRequestPlan } from "./request-plan.js";
9
+ import { emitStreamReasoningArtifacts, emitStreamReasoningDelta, } from "./stream-events.js";
10
+ import { META_STREAM_TERMINAL, requireTerminalProof, } from "./stream-terminal.js";
8
11
  const baseUrl = "https://api.meta.ai/v1";
9
12
  const modelCache = new Map();
10
13
  const CACHE_TTL_MS = 60 * 60 * 1000;
@@ -34,7 +37,32 @@ function metaReasoningPayload(reasoning) {
34
37
  summary = "auto";
35
38
  return { effort: eff, summary };
36
39
  }
37
- function toResponsesInput(messages, supportsVision) {
40
+ function metaReplayArtifacts(message, replay) {
41
+ return selectReasoningArtifactsForReplay({
42
+ artifacts: reasoningArtifactsForMessage(message),
43
+ target: replay.target,
44
+ context: { hasToolCalls: Boolean(message.toolCalls?.length) },
45
+ observe: replay.observe,
46
+ })
47
+ .filter((artifact) => artifact.kind === "encrypted")
48
+ .sort((left, right) => left.position.sequence - right.position.sequence)
49
+ .map((artifact) => {
50
+ const byId = artifact.position.toolCallId
51
+ ? message.toolCalls?.findIndex((toolCall) => toolCall.id === artifact.position.toolCallId)
52
+ : undefined;
53
+ const toolCallIndex = artifact.position.toolCallIndex ??
54
+ (byId !== undefined && byId >= 0 ? byId : undefined);
55
+ return {
56
+ items: reasoningArtifactItems(artifact),
57
+ ...(toolCallIndex === undefined ? {} : { toolCallIndex }),
58
+ };
59
+ });
60
+ }
61
+ function appendMetaReplayItems(input, entries) {
62
+ for (const entry of entries)
63
+ input.push(...entry.items);
64
+ }
65
+ function toResponsesInput(messages, supportsVision, replay) {
38
66
  const input = [];
39
67
  for (const m of messages) {
40
68
  if (m.role === "system") {
@@ -74,13 +102,19 @@ function toResponsesInput(messages, supportsVision) {
74
102
  continue;
75
103
  }
76
104
  if (m.role === "assistant") {
77
- const replayItems = m.reasoningBlock?.items;
78
- if (Array.isArray(replayItems) && replayItems.length > 0) {
79
- for (const item of replayItems)
80
- input.push(item);
105
+ const replayArtifacts = metaReplayArtifacts(m, replay);
106
+ const leadingArtifacts = replayArtifacts.filter((artifact) => artifact.toolCallIndex === undefined);
107
+ const artifactsByTool = new Map();
108
+ for (const artifact of replayArtifacts) {
109
+ if (artifact.toolCallIndex === undefined)
110
+ continue;
111
+ const current = artifactsByTool.get(artifact.toolCallIndex) ?? [];
112
+ current.push(artifact);
113
+ artifactsByTool.set(artifact.toolCallIndex, current);
81
114
  }
82
115
  const hasTools = m.toolCalls && m.toolCalls.length > 0;
83
116
  if (hasTools) {
117
+ appendMetaReplayItems(input, leadingArtifacts);
84
118
  if (m.content && m.content.trim()) {
85
119
  input.push({
86
120
  type: "message",
@@ -89,7 +123,8 @@ function toResponsesInput(messages, supportsVision) {
89
123
  content: [{ type: "output_text", text: m.content }],
90
124
  });
91
125
  }
92
- for (const tc of m.toolCalls) {
126
+ for (const [toolCallIndex, tc] of m.toolCalls.entries()) {
127
+ appendMetaReplayItems(input, artifactsByTool.get(toolCallIndex) ?? []);
93
128
  const wire = toWireName(tc.name);
94
129
  input.push({
95
130
  type: "function_call",
@@ -100,6 +135,7 @@ function toResponsesInput(messages, supportsVision) {
100
135
  }
101
136
  continue;
102
137
  }
138
+ appendMetaReplayItems(input, replayArtifacts);
103
139
  if (m.content !== undefined && m.content !== null) {
104
140
  input.push({
105
141
  type: "message",
@@ -130,7 +166,7 @@ function toResponsesTools(tools) {
130
166
  parameters: t.parameters,
131
167
  }));
132
168
  }
133
- function parseMetaUsage(raw) {
169
+ export function parseMetaUsage(raw) {
134
170
  if (!raw || typeof raw !== "object" || Array.isArray(raw))
135
171
  return undefined;
136
172
  const u = raw;
@@ -140,16 +176,27 @@ function parseMetaUsage(raw) {
140
176
  const outputTokens = u.output_tokens ??
141
177
  u.completion_tokens ??
142
178
  u.outputTokens;
143
- const totalTokens = u.total_tokens ?? u.totalTokens;
144
- const cached = u.input_tokens_details?.cached_tokens ??
145
- u.prompt_tokens_details?.cached_tokens;
146
- const reasoning = u.output_tokens_details?.reasoning_tokens ??
147
- u.completion_tokens_details?.reasoning_tokens;
179
+ const totalTokens = u.total_tokens ??
180
+ u.totalTokens;
181
+ const inputDetails = u.input_tokens_details;
182
+ const promptDetails = u.prompt_tokens_details;
183
+ const outputDetails = u.output_tokens_details;
184
+ const completionDetails = u.completion_tokens_details;
185
+ const cached = inputDetails?.cached_tokens ?? promptDetails?.cached_tokens;
186
+ const cacheCreation = inputDetails?.cache_creation_tokens ??
187
+ promptDetails?.cache_creation_tokens ??
188
+ u.cache_creation_input_tokens;
189
+ const uncached = inputDetails?.uncached_tokens ??
190
+ promptDetails?.uncached_tokens ??
191
+ u.prompt_cache_miss_tokens;
192
+ const reasoning = outputDetails?.reasoning_tokens ?? completionDetails?.reasoning_tokens;
148
193
  return normalizeTokenUsage({
149
194
  promptTokens: inputTokens,
150
195
  completionTokens: outputTokens,
151
196
  totalTokens,
152
197
  cachedPromptTokens: typeof cached === "number" ? cached : undefined,
198
+ cacheCreationTokens: typeof cacheCreation === "number" ? cacheCreation : undefined,
199
+ uncachedPromptTokens: typeof uncached === "number" ? uncached : undefined,
153
200
  reasoningTokens: typeof reasoning === "number" ? reasoning : undefined,
154
201
  exact: true,
155
202
  });
@@ -170,29 +217,42 @@ function extractReasoningSummary(item) {
170
217
  return out;
171
218
  }
172
219
  function buildResponsesBody(options) {
173
- const reasoning = metaReasoningPayload(options.reasoning);
174
- const input = toResponsesInput(options.messages, options.supportsVision);
175
- const tools = toResponsesTools(options.tools);
176
- const reasoningOn = Boolean(options.reasoning?.enabled);
177
- const defaultMax = reasoningOn ? 8192 : 4096;
178
- const effectiveMax = Math.max(16, options.maxTokens ?? defaultMax);
179
- const sampling = resolveSampling({
220
+ const plan = compileRequestPlan({
221
+ provider: "meta",
180
222
  model: options.model,
181
- reasoningEnabled: reasoningOn,
182
- requestedTemperature: options.temperature,
223
+ messages: options.messages,
224
+ stream: options.stream,
225
+ endpoint: baseUrl,
226
+ reasoning: options.reasoning,
227
+ tools: options.tools,
228
+ parallelToolCalls: options.parallelToolCalls,
229
+ temperature: options.temperature,
230
+ maxTokens: options.maxTokens,
183
231
  });
232
+ const reasoning = metaReasoningPayload(plan.controls.reasoning);
233
+ const input = toResponsesInput([...plan.timeline.messages], plan.images.visionAccepted, {
234
+ target: plan.replay.target,
235
+ observe: options.reasoningArtifactReplayObserver,
236
+ });
237
+ const tools = toResponsesTools(plan.tools.definitions.length ? [...plan.tools.definitions] : undefined);
238
+ const reasoningOn = Boolean(plan.controls.reasoning?.enabled);
239
+ const defaultMax = reasoningOn ? 8192 : 4096;
240
+ const effectiveMax = Math.max(16, plan.controls.requestedMaxTokens ?? defaultMax);
184
241
  const body = {
185
242
  model: options.model,
186
243
  input,
187
244
  store: false,
188
- prompt_cache_key: "clai",
245
+ // Turn and compaction requests share one bucket on purpose so a compaction
246
+ // prompt can reuse the turn's cached prefix. Auxiliary requests get their
247
+ // own bucket so their unrelated short prefix cannot evict it.
248
+ prompt_cache_key: options.purpose === "auxiliary" ? "clai-auxiliary" : "clai",
189
249
  prompt_cache_retention: "24h",
190
250
  include: ["reasoning.encrypted_content"],
191
251
  max_output_tokens: effectiveMax,
192
- temperature: sampling.temperature,
252
+ temperature: plan.controls.temperature,
193
253
  };
194
- if (sampling.topP !== undefined)
195
- body.top_p = sampling.topP;
254
+ if (plan.controls.topP !== undefined)
255
+ body.top_p = plan.controls.topP;
196
256
  if (reasoning)
197
257
  body.reasoning = reasoning;
198
258
  if (options.stream)
@@ -229,7 +289,9 @@ function parseResponsesOutput(data) {
229
289
  let reasoningSummary = "";
230
290
  const toolCalls = [];
231
291
  const reasoningItems = [];
232
- for (const item of output) {
292
+ const reasoningItemSequences = [];
293
+ const toolCallSequences = [];
294
+ for (const [sequence, item] of output.entries()) {
233
295
  if (!item || typeof item !== "object")
234
296
  continue;
235
297
  const obj = item;
@@ -248,12 +310,8 @@ function parseResponsesOutput(data) {
248
310
  if (s)
249
311
  reasoningSummary += s;
250
312
  if (typeof obj.encrypted_content === "string" && obj.encrypted_content) {
251
- reasoningItems.push({
252
- type: "reasoning",
253
- ...(typeof obj.id === "string" ? { id: obj.id } : {}),
254
- summary: Array.isArray(obj.summary) ? obj.summary : [],
255
- encrypted_content: obj.encrypted_content,
256
- });
313
+ reasoningItems.push({ ...obj });
314
+ reasoningItemSequences.push(sequence);
257
315
  }
258
316
  }
259
317
  else if (obj.type === "function_call") {
@@ -272,23 +330,52 @@ function parseResponsesOutput(data) {
272
330
  catch {
273
331
  args = parseToolArguments(rawArgs);
274
332
  }
333
+ const toolCallIndex = toolCalls.length;
275
334
  toolCalls.push({ id: callId, name: canonical, args, rawArguments: rawArgs });
335
+ toolCallSequences.push({ sequence, toolCallIndex });
276
336
  }
277
337
  }
338
+ const reasoningItemPositions = reasoningItemSequences.map((sequence) => {
339
+ const followingTool = toolCallSequences.find((toolCall) => toolCall.sequence > sequence);
340
+ return followingTool
341
+ ? { sequence, toolCallIndex: followingTool.toolCallIndex }
342
+ : { sequence };
343
+ });
278
344
  const usage = parseMetaUsage(data.usage);
279
- return { text, toolCalls, usage, reasoningSummary, reasoningItems };
345
+ return {
346
+ text,
347
+ toolCalls,
348
+ usage,
349
+ reasoningSummary,
350
+ reasoningItems,
351
+ reasoningItemPositions,
352
+ };
280
353
  }
281
- function foldResponsesReasoning(text, reasoningSummary, usage, effort) {
282
- if (reasoningSummary && reasoningSummary.trim()) {
283
- return `${wrapReasoning(reasoningSummary)}${text}`;
284
- }
285
- const tokens = usage?.reasoningTokens ?? 0;
286
- if (tokens > 0) {
287
- const effortText = effort ? ` at ${effort} effort` : "";
288
- 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.`;
289
- return `${wrapReasoning(note)}${text}`;
290
- }
291
- return text;
354
+ function metaReasoningArtifacts(model, items, positions) {
355
+ const provenance = createReasoningArtifactProvenance({
356
+ provider: "meta",
357
+ model,
358
+ dialect: "meta-responses",
359
+ endpoint: baseUrl,
360
+ });
361
+ const artifacts = items.map((item, index) => {
362
+ const position = positions[index] ?? { sequence: index };
363
+ const replayable = position.toolCallIndex !== undefined;
364
+ return createReasoningArtifact({
365
+ kind: "encrypted",
366
+ raw: item,
367
+ provenance,
368
+ replay: replayable
369
+ ? { scope: "tool-turn", persistence: "tool-turn" }
370
+ : { scope: "none", persistence: "never" },
371
+ position: {
372
+ sequence: position.sequence,
373
+ placement: replayable ? "before-tool-call" : "assistant",
374
+ ...(replayable ? { toolCallIndex: position.toolCallIndex } : {}),
375
+ },
376
+ });
377
+ });
378
+ return artifacts.length ? artifacts : undefined;
292
379
  }
293
380
  function readWithAbort(reader, signal) {
294
381
  if (signal.aborted) {
@@ -325,6 +412,7 @@ function readWithAbort(reader, signal) {
325
412
  }
326
413
  export const metaProvider = {
327
414
  id: "meta",
415
+ reasoningStyle: "meta",
328
416
  displayName: "Meta Model API",
329
417
  defaultModel: defaultModels.meta,
330
418
  envVar: "MODEL_API_KEY",
@@ -364,197 +452,49 @@ export const metaProvider = {
364
452
  if (!auth.apiKey)
365
453
  throw new Error("Meta Model API key is required");
366
454
  const model = request.model ?? defaultModels.meta;
367
- const supportsVision = modelAcceptsImages("meta", model);
368
- const body = buildResponsesBody({
369
- model,
370
- messages: request.messages,
371
- maxTokens: request.maxTokens,
372
- temperature: request.temperature,
373
- stream: false,
374
- reasoning: request.thinking,
375
- supportsVision,
376
- tools: request.tools,
377
- parallelToolCalls: request.parallelToolCalls,
378
- });
379
- let response;
380
- try {
381
- response = await fetch(`${baseUrl}/responses`, {
382
- method: "POST",
383
- signal: request.signal ?? null,
384
- headers: {
385
- "content-type": "application/json",
386
- accept: "application/json",
387
- authorization: `Bearer ${auth.apiKey}`,
388
- },
389
- body,
390
- verbose: process.env.CLAI_VERBOSE === "true",
391
- });
392
- }
393
- catch (error) {
394
- if (error instanceof Error && error.name === "AbortError")
395
- throw error;
396
- const msg = error instanceof Error ? error.message : String(error);
397
- throw new ProviderError(`Meta Model API request could not be sent (${msg}). Check connectivity to ${baseUrl}.`);
398
- }
399
- let data;
400
- try {
401
- data = await readJson(response);
402
- }
403
- catch (error) {
404
- if (error instanceof ProviderError) {
405
- throw new ProviderError(`Meta Model API (model=${model}): ${error.message}`, error.status, error.body, error.retryAfterSeconds);
406
- }
407
- throw error;
408
- }
409
- const parsed = parseResponsesOutput(data);
410
- const usage = parsed.usage ?? parseMetaUsage(data.usage);
411
- const effort = metaReasoningPayload(request.thinking)?.effort;
412
- const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usage, effort);
413
- if (!parsed.text.trim() && parsed.toolCalls.length === 0) {
414
- const respStatus = data.status;
415
- const details = data.incomplete_details;
416
- if (respStatus === "incomplete" && details?.reason === "max_output_tokens") {
417
- const retryRequest = incompleteBudgetRetry(request, Boolean(request.thinking?.enabled));
418
- if (retryRequest)
419
- return metaProvider.complete(retryRequest, auth);
420
- throw budgetExhaustedError(request, Boolean(request.thinking?.enabled), JSON.stringify(data));
421
- }
422
- }
423
- if (!full.trim() && parsed.toolCalls.length === 0) {
424
- 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.`);
425
- }
426
- return {
427
- text: full,
455
+ return runGenerationAttempt(request, {
428
456
  provider: "meta",
429
457
  model,
430
- ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
431
- ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
432
- ...(usage ? { usage } : {}),
433
- ...(parsed.reasoningItems.length
434
- ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
435
- : {}),
436
- };
437
- },
438
- async stream(request, auth, onToken) {
439
- if (!auth.apiKey)
440
- throw new Error("Meta Model API key is required");
441
- const model = request.model ?? defaultModels.meta;
442
- const supportsVision = modelAcceptsImages("meta", model);
443
- const reasoningOn = Boolean(request.thinking?.enabled);
444
- const idleTimeoutMs = THINKING_STREAM_IDLE_TIMEOUT_MS;
445
- const initialIdleTimeoutMs = THINKING_STREAM_INITIAL_IDLE_TIMEOUT_MS;
446
- const outputIdleTimeoutMs = Math.round(Math.max(idleTimeoutMs, initialIdleTimeoutMs) * 1.5);
447
- const idleController = new AbortController();
448
- let transportTimer;
449
- let outputTimer;
450
- let idleFired = false;
451
- let firedWatchdog;
452
- let firedBudgetMs = initialIdleTimeoutMs;
453
- let sawTransportActivity = false;
454
- let sawStreamProgress = false;
455
- const fireStall = (watchdog, budgetMs) => {
456
- if (idleFired)
457
- return;
458
- idleFired = true;
459
- firedWatchdog = watchdog;
460
- firedBudgetMs = budgetMs;
461
- idleController.abort();
462
- };
463
- const armTransportTimer = (budgetMs) => {
464
- if (transportTimer)
465
- clearTimeout(transportTimer);
466
- transportTimer = setTimeout(() => fireStall("transport", budgetMs), budgetMs);
467
- };
468
- const noteTransportActivity = () => {
469
- sawTransportActivity = true;
470
- armTransportTimer(idleTimeoutMs);
471
- };
472
- const resetIdleTimer = () => {
473
- sawStreamProgress = true;
474
- noteTransportActivity();
475
- if (outputTimer)
476
- clearTimeout(outputTimer);
477
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
478
- };
479
- armTransportTimer(initialIdleTimeoutMs);
480
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
481
- const clearIdleTimers = () => {
482
- if (transportTimer)
483
- clearTimeout(transportTimer);
484
- if (outputTimer)
485
- clearTimeout(outputTimer);
486
- transportTimer = undefined;
487
- outputTimer = undefined;
488
- };
489
- const onCallerAbort = () => idleController.abort(request.signal?.reason);
490
- request.signal?.addEventListener("abort", onCallerAbort, { once: true });
491
- const body = buildResponsesBody({
492
- model,
493
- messages: request.messages,
494
- maxTokens: request.maxTokens,
495
- temperature: request.temperature,
496
- stream: true,
497
- reasoning: request.thinking,
498
- supportsVision,
499
- tools: request.tools,
500
- parallelToolCalls: request.parallelToolCalls,
501
- });
502
- let response;
503
- let lastFetchError;
504
- for (let fetchAttempt = 0; fetchAttempt < 2; fetchAttempt++) {
505
- if (fetchAttempt > 0) {
506
- await new Promise((r) => setTimeout(r, 1000));
507
- if (request.signal?.aborted)
508
- throw request.signal.reason;
509
- if (idleFired)
510
- break;
511
- armTransportTimer(initialIdleTimeoutMs);
512
- if (outputTimer) {
513
- clearTimeout(outputTimer);
514
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
515
- }
516
- }
458
+ mode: "complete",
459
+ reason: request.attemptReason ?? "initial",
460
+ }, async () => {
461
+ const body = buildResponsesBody({
462
+ model,
463
+ messages: request.messages,
464
+ maxTokens: request.maxTokens,
465
+ temperature: request.temperature,
466
+ stream: false,
467
+ reasoning: request.thinking,
468
+ tools: request.tools,
469
+ parallelToolCalls: request.parallelToolCalls,
470
+ purpose: request.purpose,
471
+ reasoningArtifactReplayObserver: request.onReasoningArtifactReplayDecision,
472
+ });
473
+ let response;
517
474
  try {
518
- response = await fetch(`${baseUrl}/responses`, {
475
+ response = await generationFetch(`${baseUrl}/responses`, {
519
476
  method: "POST",
520
- signal: idleController.signal,
477
+ signal: request.signal ?? null,
521
478
  headers: {
522
479
  "content-type": "application/json",
523
- accept: "text/event-stream",
480
+ accept: "application/json",
524
481
  authorization: `Bearer ${auth.apiKey}`,
525
482
  },
526
483
  body,
527
484
  verbose: process.env.CLAI_VERBOSE === "true",
528
485
  });
529
- lastFetchError = undefined;
530
- break;
531
486
  }
532
487
  catch (error) {
533
- lastFetchError = error;
534
- if (idleFired) {
535
- clearIdleTimers();
536
- request.signal?.removeEventListener("abort", onCallerAbort);
537
- throw new ProviderError(`Meta Model API request timed out before any response (${Math.round(firedBudgetMs / 1000)}s) — no data arrived on the connection.`);
538
- }
539
- const msg = error instanceof Error ? error.message : String(error);
540
- const transient = /fetch failed|network error|etimedout|enotfound|econnreset|premature close|socket.*closed|aborted without reason/i.test(msg);
541
- if (!transient || sawStreamProgress)
488
+ if (error instanceof Error && error.name === "AbortError")
542
489
  throw error;
543
- continue;
490
+ if (isOperationPolicyError(error))
491
+ throw error;
492
+ const msg = error instanceof Error ? error.message : String(error);
493
+ throw new ProviderError(`Meta Model API request could not be sent (${msg}). Check connectivity to ${baseUrl}.`);
544
494
  }
545
- }
546
- if (!response) {
547
- clearIdleTimers();
548
- request.signal?.removeEventListener("abort", onCallerAbort);
549
- if (lastFetchError)
550
- throw lastFetchError;
551
- throw new ProviderError(`Meta Model API request failed before a response was received.`);
552
- }
553
- if (!response.ok) {
554
- clearIdleTimers();
555
- request.signal?.removeEventListener("abort", onCallerAbort);
495
+ let data;
556
496
  try {
557
- await readJson(response);
497
+ data = await readJson(response);
558
498
  }
559
499
  catch (error) {
560
500
  if (error instanceof ProviderError) {
@@ -562,500 +502,716 @@ export const metaProvider = {
562
502
  }
563
503
  throw error;
564
504
  }
565
- }
566
- if (!response.body) {
567
- clearIdleTimers();
568
- request.signal?.removeEventListener("abort", onCallerAbort);
569
- throw new ProviderError(`Meta Model API returned no stream body`);
570
- }
571
- const contentType = response.headers.get("content-type") ?? "";
572
- if (response.status === 202 || /\bapplication\/json\b/i.test(contentType)) {
573
- clearIdleTimers();
574
- request.signal?.removeEventListener("abort", onCallerAbort);
575
- const data = await readJson(response);
576
- if (response.status === 202) {
577
- const requestId = data.requestId ?? data.id;
578
- 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));
579
- }
580
505
  const parsed = parseResponsesOutput(data);
581
- const usageTmp = parsed.usage ?? parseMetaUsage(data.usage);
582
- const effortTmp = metaReasoningPayload(request.thinking)?.effort;
583
- const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usageTmp, effortTmp);
584
- const jsonStatus = data.status;
585
- const jsonDetails = data.incomplete_details;
586
- if (!parsed.text.trim() && parsed.toolCalls.length === 0 && jsonStatus === "incomplete" && jsonDetails?.reason === "max_output_tokens") {
587
- const retryRequest = incompleteBudgetRetry(request, reasoningOn);
588
- const streamMethod = metaProvider.stream;
589
- if (retryRequest && streamMethod)
590
- return streamMethod(retryRequest, auth, onToken);
591
- throw budgetExhaustedError(request, reasoningOn, JSON.stringify(data));
506
+ const reasoningArtifacts = metaReasoningArtifacts(model, parsed.reasoningItems, parsed.reasoningItemPositions);
507
+ const usage = parsed.usage ?? parseMetaUsage(data.usage);
508
+ if (!parsed.text.trim() && parsed.toolCalls.length === 0) {
509
+ const respStatus = data.status;
510
+ const details = data.incomplete_details;
511
+ if (respStatus === "incomplete" && details?.reason === "max_output_tokens") {
512
+ const retryRequest = incompleteBudgetRetry(request, Boolean(request.thinking?.enabled));
513
+ if (retryRequest) {
514
+ completeGenerationAttempt("failure", usage);
515
+ retryRequest.attemptReason = "provider-retry";
516
+ return metaProvider.complete(retryRequest, auth);
517
+ }
518
+ throw budgetExhaustedError(request, Boolean(request.thinking?.enabled), JSON.stringify(data));
519
+ }
592
520
  }
593
- if (full.trim() || parsed.toolCalls.length > 0) {
594
- if (full.trim())
595
- onToken(full);
596
- return {
597
- text: full,
598
- provider: "meta",
599
- model,
600
- ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
601
- ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
602
- ...(usageTmp ? { usage: usageTmp } : {}),
603
- ...(parsed.reasoningItems.length
604
- ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
605
- : {}),
606
- };
521
+ if (!parsed.text.trim() &&
522
+ parsed.toolCalls.length === 0 &&
523
+ !parsed.reasoningSummary.trim()) {
524
+ 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.`);
607
525
  }
608
- 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));
609
- }
610
- const decoder = new TextDecoder();
611
- const reader = response.body.getReader();
612
- let buffer = "";
613
- let full = "";
614
- let visible = "";
615
- let reasoningSeen = "";
616
- let inReasoning = false;
617
- let finishReason;
618
- let streamUsage;
619
- const toolCallState = new Map();
620
- const outputIndexToItemId = new Map();
621
- let responseId;
622
- const reasoningItems = [];
623
- const reasoningItemKeys = new Set();
624
- const noteReasoningItem = (item) => {
625
- const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : "";
626
- if (!encrypted)
627
- return;
628
- const id = typeof item.id === "string" ? item.id : undefined;
629
- const key = id ?? encrypted.slice(0, 64);
630
- if (reasoningItemKeys.has(key))
631
- return;
632
- reasoningItemKeys.add(key);
633
- reasoningItems.push({
634
- type: "reasoning",
635
- ...(id ? { id } : {}),
636
- summary: Array.isArray(item.summary) ? item.summary : [],
637
- encrypted_content: encrypted,
526
+ return {
527
+ text: parsed.text,
528
+ provider: "meta",
529
+ model,
530
+ ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
531
+ ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
532
+ ...(usage ? { usage } : {}),
533
+ ...(parsed.reasoningItems.length
534
+ ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
535
+ : {}),
536
+ ...(reasoningArtifacts ? { reasoningArtifacts } : {}),
537
+ };
538
+ });
539
+ },
540
+ async stream(request, auth, onToken) {
541
+ if (!auth.apiKey)
542
+ throw new Error("Meta Model API key is required");
543
+ const model = request.model ?? defaultModels.meta;
544
+ return runGenerationAttempt(request, {
545
+ provider: "meta",
546
+ model,
547
+ mode: "stream",
548
+ reason: request.attemptReason ?? "initial",
549
+ }, async () => {
550
+ const reasoningOn = Boolean(request.thinking?.enabled);
551
+ const idleTimeoutMs = THINKING_STREAM_IDLE_TIMEOUT_MS;
552
+ const initialIdleTimeoutMs = THINKING_STREAM_INITIAL_IDLE_TIMEOUT_MS;
553
+ const outputIdleTimeoutMs = Math.round(Math.max(idleTimeoutMs, initialIdleTimeoutMs) * 1.5);
554
+ const idleController = new AbortController();
555
+ let transportTimer;
556
+ let outputTimer;
557
+ let idleFired = false;
558
+ let firedWatchdog;
559
+ let firedBudgetMs = initialIdleTimeoutMs;
560
+ let sawTransportActivity = false;
561
+ let sawStreamProgress = false;
562
+ const fireStall = (watchdog, budgetMs) => {
563
+ if (idleFired)
564
+ return;
565
+ idleFired = true;
566
+ firedWatchdog = watchdog;
567
+ firedBudgetMs = budgetMs;
568
+ idleController.abort();
569
+ };
570
+ const armTransportTimer = (budgetMs) => {
571
+ if (transportTimer)
572
+ clearTimeout(transportTimer);
573
+ transportTimer = setTimeout(() => fireStall("transport", budgetMs), budgetMs);
574
+ };
575
+ const noteTransportActivity = () => {
576
+ sawTransportActivity = true;
577
+ armTransportTimer(idleTimeoutMs);
578
+ };
579
+ const resetIdleTimer = () => {
580
+ sawStreamProgress = true;
581
+ noteTransportActivity();
582
+ if (outputTimer)
583
+ clearTimeout(outputTimer);
584
+ outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
585
+ };
586
+ armTransportTimer(initialIdleTimeoutMs);
587
+ outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
588
+ const clearIdleTimers = () => {
589
+ if (transportTimer)
590
+ clearTimeout(transportTimer);
591
+ if (outputTimer)
592
+ clearTimeout(outputTimer);
593
+ transportTimer = undefined;
594
+ outputTimer = undefined;
595
+ };
596
+ const onCallerAbort = () => idleController.abort(request.signal?.reason);
597
+ request.signal?.addEventListener("abort", onCallerAbort, { once: true });
598
+ const body = buildResponsesBody({
599
+ model,
600
+ messages: request.messages,
601
+ maxTokens: request.maxTokens,
602
+ temperature: request.temperature,
603
+ stream: true,
604
+ reasoning: request.thinking,
605
+ tools: request.tools,
606
+ parallelToolCalls: request.parallelToolCalls,
607
+ purpose: request.purpose,
608
+ reasoningArtifactReplayObserver: request.onReasoningArtifactReplayDecision,
638
609
  });
639
- };
640
- const reasoningReplay = () => reasoningItems.length ? { reasoningBlock: { text: reasoningSeen, items: reasoningItems } } : {};
641
- const enterReasoning = () => {
642
- if (inReasoning)
643
- return;
644
- inReasoning = true;
645
- full += REASONING_OPEN;
646
- onToken(REASONING_OPEN);
647
- };
648
- const exitReasoning = () => {
649
- if (!inReasoning)
650
- return;
651
- inReasoning = false;
652
- full += REASONING_CLOSE;
653
- onToken(REASONING_CLOSE);
654
- };
655
- const emitVisible = (text) => {
656
- if (!text)
657
- return;
658
- if (inReasoning)
659
- exitReasoning();
660
- visible += text;
661
- full += text;
662
- onToken(text);
663
- };
664
- const emitReasoningDelta = (text) => {
665
- if (!text)
666
- return;
667
- enterReasoning();
668
- reasoningSeen += text;
669
- full += text;
670
- onToken(text);
671
- };
672
- const cleanup = () => {
673
- clearIdleTimers();
674
- request.signal?.removeEventListener("abort", onCallerAbort);
675
- idleController.signal.removeEventListener("abort", cancelReaderOnAbort);
676
- };
677
- const cancelReaderOnAbort = () => {
678
- reader.cancel().catch(() => undefined);
679
- };
680
- idleController.signal.addEventListener("abort", cancelReaderOnAbort, { once: true });
681
- const sseFrames = createSseFrameAssembler();
682
- try {
683
- while (true) {
684
- request.signal?.throwIfAborted();
685
- if (idleController.signal.aborted)
686
- throw new Error("Stream aborted");
687
- const { done, value } = await readWithAbort(reader, idleController.signal);
688
- request.signal?.throwIfAborted();
689
- if (idleController.signal.aborted)
690
- throw new Error("Stream aborted");
691
- if (done)
610
+ let response;
611
+ let lastFetchError;
612
+ for (let fetchAttempt = 0; fetchAttempt < 2; fetchAttempt++) {
613
+ if (fetchAttempt > 0) {
614
+ await new Promise((r) => setTimeout(r, 1000));
615
+ if (request.signal?.aborted)
616
+ throw request.signal.reason;
617
+ if (idleFired)
618
+ break;
619
+ armTransportTimer(initialIdleTimeoutMs);
620
+ if (outputTimer) {
621
+ clearTimeout(outputTimer);
622
+ outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
623
+ }
624
+ }
625
+ try {
626
+ response = await generationFetch(`${baseUrl}/responses`, {
627
+ method: "POST",
628
+ signal: idleController.signal,
629
+ headers: {
630
+ "content-type": "application/json",
631
+ accept: "text/event-stream",
632
+ authorization: `Bearer ${auth.apiKey}`,
633
+ },
634
+ body,
635
+ verbose: process.env.CLAI_VERBOSE === "true",
636
+ });
637
+ lastFetchError = undefined;
692
638
  break;
693
- if (value && value.byteLength > 0)
694
- noteTransportActivity();
695
- buffer += decoder.decode(value, { stream: true });
696
- const lines = buffer.split("\n");
697
- buffer = lines.pop() ?? "";
698
- for (const line of lines) {
699
- const payload = sseFrames.pushLine(line);
700
- if (payload === undefined)
701
- continue;
702
- if (payload === "[DONE]") {
703
- if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
704
- const effort = metaReasoningPayload(request.thinking)?.effort;
705
- const effortText = effort ? ` at ${effort} effort` : "";
706
- 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.`;
707
- emitReasoningDelta(note);
708
- exitReasoning();
709
- }
710
- else {
711
- exitReasoning();
712
- }
713
- cleanup();
714
- const toolCalls = [];
715
- for (const [, state] of toolCallState) {
716
- if (!state.name)
717
- continue;
718
- const canonical = state.name ? fromWireName(state.name) ?? state.name : state.name ?? "";
719
- const raw = state.arguments;
720
- let args;
721
- try {
722
- const parsed = JSON.parse(raw);
723
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
724
- args = parsed;
725
- else
726
- args = {};
727
- }
728
- catch {
729
- args = parseToolArguments(raw);
730
- }
731
- toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
732
- }
733
- if (!visible.trim() && toolCalls.length === 0) {
734
- if (reasoningSeen.trim()) {
735
- return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}), ...reasoningReplay() };
736
- }
737
- throw new ProviderError(`Meta Model API completed without a visible answer.`);
639
+ }
640
+ catch (error) {
641
+ lastFetchError = error;
642
+ if (idleFired) {
643
+ clearIdleTimers();
644
+ request.signal?.removeEventListener("abort", onCallerAbort);
645
+ throw new ProviderError(`Meta Model API request timed out before any response (${Math.round(firedBudgetMs / 1000)}s) — no data arrived on the connection.`);
646
+ }
647
+ const msg = error instanceof Error ? error.message : String(error);
648
+ const transient = /fetch failed|network error|etimedout|enotfound|econnreset|premature close|socket.*closed|aborted without reason/i.test(msg);
649
+ if (!transient || sawStreamProgress)
650
+ throw error;
651
+ continue;
652
+ }
653
+ }
654
+ if (!response) {
655
+ clearIdleTimers();
656
+ request.signal?.removeEventListener("abort", onCallerAbort);
657
+ if (lastFetchError)
658
+ throw lastFetchError;
659
+ throw new ProviderError(`Meta Model API request failed before a response was received.`);
660
+ }
661
+ if (!response.ok) {
662
+ clearIdleTimers();
663
+ request.signal?.removeEventListener("abort", onCallerAbort);
664
+ try {
665
+ await readJson(response);
666
+ }
667
+ catch (error) {
668
+ if (error instanceof ProviderError) {
669
+ throw new ProviderError(`Meta Model API (model=${model}): ${error.message}`, error.status, error.body, error.retryAfterSeconds);
670
+ }
671
+ throw error;
672
+ }
673
+ }
674
+ if (!response.body) {
675
+ clearIdleTimers();
676
+ request.signal?.removeEventListener("abort", onCallerAbort);
677
+ throw new ProviderError(`Meta Model API returned no stream body`);
678
+ }
679
+ const contentType = response.headers.get("content-type") ?? "";
680
+ if (response.status === 202 || /\bapplication\/json\b/i.test(contentType)) {
681
+ try {
682
+ const data = await readJson(response, idleController.signal);
683
+ if (response.status === 202) {
684
+ const requestId = data.requestId ?? data.id;
685
+ 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));
686
+ }
687
+ const parsed = parseResponsesOutput(data);
688
+ const reasoningArtifacts = metaReasoningArtifacts(model, parsed.reasoningItems, parsed.reasoningItemPositions);
689
+ const usageTmp = parsed.usage ?? parseMetaUsage(data.usage);
690
+ const jsonStatus = data.status;
691
+ const jsonDetails = data.incomplete_details;
692
+ if (!parsed.text.trim() && parsed.toolCalls.length === 0 && jsonStatus === "incomplete" && jsonDetails?.reason === "max_output_tokens") {
693
+ const retryRequest = incompleteBudgetRetry(request, reasoningOn);
694
+ const streamMethod = metaProvider.stream;
695
+ if (retryRequest && streamMethod) {
696
+ completeGenerationAttempt("failure", usageTmp);
697
+ retryRequest.attemptReason = "provider-retry";
698
+ return streamMethod(retryRequest, auth, onToken);
738
699
  }
700
+ completeGenerationAttempt("failure", usageTmp);
701
+ throw budgetExhaustedError(request, reasoningOn, JSON.stringify(data));
702
+ }
703
+ if (parsed.text.trim() ||
704
+ parsed.toolCalls.length > 0 ||
705
+ parsed.reasoningSummary.trim()) {
706
+ emitStreamReasoningArtifacts(request.onStreamEvent, reasoningArtifacts);
707
+ if (parsed.text)
708
+ onToken(parsed.text);
739
709
  return {
740
- text: full,
710
+ text: parsed.text,
741
711
  provider: "meta",
742
712
  model,
743
- ...(toolCalls.length ? { toolCalls } : {}),
744
- ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
745
- ...(streamUsage ? { usage: streamUsage } : {}),
746
- ...reasoningReplay(),
713
+ ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
714
+ ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
715
+ ...(usageTmp ? { usage: usageTmp } : {}),
716
+ ...(parsed.reasoningItems.length
717
+ ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
718
+ : {}),
719
+ ...(reasoningArtifacts ? { reasoningArtifacts } : {}),
747
720
  };
748
721
  }
749
- let parsed;
750
- try {
751
- parsed = JSON.parse(payload);
752
- }
753
- catch {
754
- continue;
722
+ 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));
723
+ }
724
+ catch (error) {
725
+ if (idleFired) {
726
+ const seconds = Math.round(firedBudgetMs / 1000);
727
+ if (firedWatchdog === "transport" || !sawTransportActivity) {
728
+ if (!sawTransportActivity) {
729
+ throw new ProviderError(`Meta Model API request timed out before any response (${seconds}s) — no data arrived on the connection.`);
730
+ }
731
+ throw new ProviderError(`Meta Model API stream transport timeout (${seconds}s) — no data arrived on the connection after it had started.`);
732
+ }
733
+ throw new ProviderError(`Meta Model API stream stalled — ${STREAM_STALL_MARKER} for ${seconds}s`);
755
734
  }
756
- if (parsed.error) {
757
- const rawDetail = typeof parsed.error === "string"
758
- ? parsed.error
759
- : parsed.error.message ?? parsed.error.type ?? "unknown error";
760
- const detail = rawDetail.trim().length <= 2 ? `${rawDetail} — ${payload.slice(0, 300)}` : rawDetail;
761
- throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 1000));
735
+ throw error;
736
+ }
737
+ finally {
738
+ clearIdleTimers();
739
+ request.signal?.removeEventListener("abort", onCallerAbort);
740
+ }
741
+ }
742
+ const decoder = new TextDecoder();
743
+ const reader = response.body.getReader();
744
+ let buffer = "";
745
+ let full = "";
746
+ let visible = "";
747
+ let reasoningSeen = "";
748
+ let finishReason;
749
+ let sawTerminalProof;
750
+ let streamUsage;
751
+ const toolCallState = new Map();
752
+ const outputIndexToItemId = new Map();
753
+ const outputIndexToToolCallIndex = new Map();
754
+ let responseId;
755
+ const reasoningItems = [];
756
+ const reasoningItemSequences = [];
757
+ const reasoningItemToolCallIndices = [];
758
+ const reasoningItemIndexes = new Map();
759
+ const noteReasoningItem = (item, sequence, toolCallIndex) => {
760
+ const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : "";
761
+ if (!encrypted)
762
+ return;
763
+ const id = typeof item.id === "string" ? item.id : undefined;
764
+ const key = id ?? encrypted.slice(0, 64);
765
+ const existingIndex = reasoningItemIndexes.get(key);
766
+ if (existingIndex !== undefined) {
767
+ reasoningItems[existingIndex] = { ...item };
768
+ if (sequence !== undefined)
769
+ reasoningItemSequences[existingIndex] = sequence;
770
+ if (toolCallIndex !== undefined) {
771
+ reasoningItemToolCallIndices[existingIndex] = toolCallIndex;
762
772
  }
763
- const type = parsed.type;
764
- if (type === "response.created" || type === "response.in_progress") {
765
- const resp = (parsed.response ?? parsed);
766
- if (typeof resp.id === "string")
767
- responseId = resp.id;
768
- continue;
773
+ return;
774
+ }
775
+ reasoningItemIndexes.set(key, reasoningItems.length);
776
+ reasoningItems.push({ ...item });
777
+ reasoningItemSequences.push(sequence ?? Number.MAX_SAFE_INTEGER);
778
+ reasoningItemToolCallIndices.push(toolCallIndex);
779
+ };
780
+ const reasoningReplay = () => {
781
+ if (!reasoningItems.length) {
782
+ return reasoningSeen ? { reasoningBlock: { text: reasoningSeen } } : {};
783
+ }
784
+ const toolCallSequences = [...outputIndexToToolCallIndex.entries()]
785
+ .map(([sequence, toolCallIndex]) => ({ sequence, toolCallIndex }))
786
+ .sort((left, right) => left.sequence - right.sequence);
787
+ const positions = reasoningItemSequences.map((sequence, index) => {
788
+ const storedToolCallIndex = reasoningItemToolCallIndices[index];
789
+ if (storedToolCallIndex !== undefined) {
790
+ return { sequence, toolCallIndex: storedToolCallIndex };
769
791
  }
770
- if (type === "response.output_item.added") {
771
- const item = parsed.item;
772
- if (!item)
792
+ const followingTool = toolCallSequences.find((toolCall) => toolCall.sequence > sequence);
793
+ return followingTool
794
+ ? { sequence, toolCallIndex: followingTool.toolCallIndex }
795
+ : { sequence };
796
+ });
797
+ const reasoningArtifacts = metaReasoningArtifacts(model, reasoningItems, positions);
798
+ emitStreamReasoningArtifacts(request.onStreamEvent, reasoningArtifacts);
799
+ return {
800
+ reasoningBlock: { text: reasoningSeen, items: reasoningItems },
801
+ ...(reasoningArtifacts ? { reasoningArtifacts } : {}),
802
+ };
803
+ };
804
+ const emitVisible = (text) => {
805
+ if (!text)
806
+ return;
807
+ visible += text;
808
+ full += text;
809
+ onToken(text);
810
+ };
811
+ const emitReasoningDelta = (text) => {
812
+ if (!text)
813
+ return;
814
+ reasoningSeen += text;
815
+ emitStreamReasoningDelta(request.onStreamEvent, text);
816
+ };
817
+ const cleanup = () => {
818
+ clearIdleTimers();
819
+ request.signal?.removeEventListener("abort", onCallerAbort);
820
+ idleController.signal.removeEventListener("abort", cancelReaderOnAbort);
821
+ };
822
+ const cancelReaderOnAbort = () => {
823
+ reader.cancel().catch(() => undefined);
824
+ };
825
+ idleController.signal.addEventListener("abort", cancelReaderOnAbort, { once: true });
826
+ const sseFrames = createSseFrameAssembler();
827
+ try {
828
+ while (true) {
829
+ request.signal?.throwIfAborted();
830
+ if (idleController.signal.aborted)
831
+ throw new Error("Stream aborted");
832
+ const { done, value } = await readWithAbort(reader, idleController.signal);
833
+ request.signal?.throwIfAborted();
834
+ if (idleController.signal.aborted)
835
+ throw new Error("Stream aborted");
836
+ if (done)
837
+ break;
838
+ if (value && value.byteLength > 0)
839
+ noteTransportActivity();
840
+ buffer += decoder.decode(value, { stream: true });
841
+ const lines = buffer.split("\n");
842
+ buffer = lines.pop() ?? "";
843
+ for (const line of lines) {
844
+ const payload = sseFrames.pushLine(line);
845
+ if (payload === undefined)
773
846
  continue;
774
- const outputIndex = typeof parsed.output_index === "number" ? parsed.output_index : undefined;
775
- const itemId = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
776
- if (outputIndex !== undefined && itemId)
777
- outputIndexToItemId.set(outputIndex, itemId);
778
- if (item.type === "function_call") {
779
- const id = typeof item.id === "string" ? item.id : typeof item.call_id === "string" ? item.call_id : itemId ?? `call_${toolCallState.size}`;
780
- const callId = typeof item.call_id === "string" ? item.call_id : id;
781
- const name = typeof item.name === "string" ? item.name : "";
782
- const args = typeof item.arguments === "string" ? item.arguments : "";
783
- toolCallState.set(id, { id, callId, name, arguments: args });
784
- resetIdleTimer();
785
- if (request.onToolCallDelta) {
786
- const canonical = name ? fromWireName(name) ?? name : undefined;
787
- request.onToolCallDelta({ index: toolCallState.size - 1, ...(callId ? { id: callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: args.length });
847
+ if (payload === "[DONE]") {
848
+ if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
849
+ const effort = metaReasoningPayload(request.thinking)?.effort;
850
+ const effortText = effort ? ` at ${effort} effort` : "";
851
+ 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.`;
852
+ emitReasoningDelta(note);
788
853
  }
789
- }
790
- else if (item.type === "reasoning") {
791
- const s = extractReasoningSummary(item);
792
- if (s) {
793
- resetIdleTimer();
794
- emitReasoningDelta(s);
854
+ cleanup();
855
+ const toolCalls = [];
856
+ for (const [, state] of toolCallState) {
857
+ if (!state.name)
858
+ continue;
859
+ const canonical = state.name ? fromWireName(state.name) ?? state.name : state.name ?? "";
860
+ const raw = state.arguments;
861
+ let args;
862
+ try {
863
+ const parsed = JSON.parse(raw);
864
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
865
+ args = parsed;
866
+ else
867
+ args = {};
868
+ }
869
+ catch {
870
+ args = parseToolArguments(raw);
871
+ }
872
+ toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
795
873
  }
796
- }
797
- else if (item.type === "message") {
798
- resetIdleTimer();
799
- }
800
- continue;
801
- }
802
- if (type === "response.output_item.done") {
803
- const item = parsed.item;
804
- if (item?.type === "reasoning")
805
- noteReasoningItem(item);
806
- if (item?.type === "function_call") {
807
- const id = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
808
- if (id && toolCallState.has(id)) {
809
- const state = toolCallState.get(id);
810
- if (typeof item.arguments === "string" && item.arguments.length > state.arguments.length)
811
- state.arguments = item.arguments;
812
- if (typeof item.name === "string" && !state.name)
813
- state.name = item.name;
814
- if (typeof item.call_id === "string" && !state.callId)
815
- state.callId = item.call_id;
874
+ if (!visible.trim() && toolCalls.length === 0) {
875
+ if (reasoningSeen.trim()) {
876
+ return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}), ...reasoningReplay() };
877
+ }
878
+ throw new ProviderError(`Meta Model API completed without a visible answer.`);
816
879
  }
817
- resetIdleTimer();
880
+ return {
881
+ text: full,
882
+ provider: "meta",
883
+ model,
884
+ ...(toolCalls.length ? { toolCalls } : {}),
885
+ ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
886
+ ...(streamUsage ? { usage: streamUsage } : {}),
887
+ ...reasoningReplay(),
888
+ };
818
889
  }
819
- if (item && typeof item.status === "string")
820
- finishReason = item.status;
821
- continue;
822
- }
823
- if (type === "response.content_part.added" || type === "response.content_part.done") {
824
- continue;
825
- }
826
- if (type === "response.output_text.delta") {
827
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
828
- if (delta) {
829
- resetIdleTimer();
830
- emitVisible(delta);
890
+ let parsed;
891
+ try {
892
+ parsed = JSON.parse(payload);
831
893
  }
832
- continue;
833
- }
834
- if (type === "response.reasoning_summary_text.delta") {
835
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
836
- if (delta) {
837
- resetIdleTimer();
838
- emitReasoningDelta(delta);
894
+ catch {
895
+ continue;
839
896
  }
840
- continue;
841
- }
842
- if (type === "response.reasoning_summary_text.done") {
843
- const textVal = typeof parsed.text === "string" ? parsed.text : "";
844
- if (textVal && !reasoningSeen.includes(textVal)) {
845
- const remaining = textVal.slice(reasoningSeen.length);
846
- if (remaining) {
847
- resetIdleTimer();
848
- emitReasoningDelta(remaining);
849
- }
897
+ if (parsed.error) {
898
+ const rawDetail = typeof parsed.error === "string"
899
+ ? parsed.error
900
+ : parsed.error.message ?? parsed.error.type ?? "unknown error";
901
+ const detail = rawDetail.trim().length <= 2 ? `${rawDetail} — ${payload.slice(0, 300)}` : rawDetail;
902
+ throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 1000));
850
903
  }
851
- exitReasoning();
852
- continue;
853
- }
854
- if (type === "response.function_call_arguments.delta") {
855
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
856
- const itemId = typeof parsed.item_id === "string" ? parsed.item_id : typeof parsed.itemId === "string" ? parsed.itemId : undefined;
857
- let targetId = itemId;
858
- if (!targetId && typeof parsed.output_index === "number")
859
- targetId = outputIndexToItemId.get(parsed.output_index);
860
- if (targetId) {
861
- const state = toolCallState.get(targetId);
862
- if (state) {
863
- state.arguments += delta;
904
+ const type = parsed.type;
905
+ if (type === "response.created" || type === "response.in_progress") {
906
+ const resp = (parsed.response ?? parsed);
907
+ if (typeof resp.id === "string")
908
+ responseId = resp.id;
909
+ continue;
910
+ }
911
+ if (type === "response.output_item.added") {
912
+ const item = parsed.item;
913
+ if (!item)
914
+ continue;
915
+ const outputIndex = typeof parsed.output_index === "number" ? parsed.output_index : undefined;
916
+ const itemId = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
917
+ if (outputIndex !== undefined && itemId)
918
+ outputIndexToItemId.set(outputIndex, itemId);
919
+ if (item.type === "function_call") {
920
+ const id = typeof item.id === "string" ? item.id : typeof item.call_id === "string" ? item.call_id : itemId ?? `call_${toolCallState.size}`;
921
+ const callId = typeof item.call_id === "string" ? item.call_id : id;
922
+ const name = typeof item.name === "string" ? item.name : "";
923
+ const args = typeof item.arguments === "string" ? item.arguments : "";
924
+ const toolCallIndex = toolCallState.size;
925
+ toolCallState.set(id, { id, callId, name, arguments: args });
926
+ if (outputIndex !== undefined) {
927
+ outputIndexToToolCallIndex.set(outputIndex, toolCallIndex);
928
+ }
864
929
  resetIdleTimer();
865
930
  if (request.onToolCallDelta) {
866
- const canonical = state.name ? fromWireName(state.name) ?? state.name : undefined;
867
- request.onToolCallDelta({ index: Array.from(toolCallState.keys()).indexOf(targetId), ...(state.callId ? { id: state.callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: state.arguments.length });
931
+ const canonical = name ? fromWireName(name) ?? name : undefined;
932
+ request.onToolCallDelta({ index: toolCallState.size - 1, ...(callId ? { id: callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: args.length });
933
+ }
934
+ }
935
+ else if (item.type === "reasoning") {
936
+ noteReasoningItem(item, outputIndex);
937
+ const s = extractReasoningSummary(item);
938
+ if (s) {
939
+ resetIdleTimer();
940
+ emitReasoningDelta(s);
868
941
  }
869
942
  }
870
- else {
871
- toolCallState.set(targetId, { id: targetId, callId: targetId, name: "", arguments: delta });
943
+ else if (item.type === "message") {
872
944
  resetIdleTimer();
873
945
  }
946
+ continue;
874
947
  }
875
- else if (delta) {
876
- const anyKey = Array.from(toolCallState.keys()).pop();
877
- if (anyKey) {
878
- const state = toolCallState.get(anyKey);
879
- state.arguments += delta;
948
+ if (type === "response.output_item.done") {
949
+ const item = parsed.item;
950
+ if (item?.type === "reasoning") {
951
+ noteReasoningItem(item, typeof parsed.output_index === "number"
952
+ ? parsed.output_index
953
+ : undefined);
954
+ }
955
+ if (item?.type === "function_call") {
956
+ const id = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
957
+ if (id && toolCallState.has(id)) {
958
+ const state = toolCallState.get(id);
959
+ if (typeof item.arguments === "string" && item.arguments.length > state.arguments.length)
960
+ state.arguments = item.arguments;
961
+ if (typeof item.name === "string" && !state.name)
962
+ state.name = item.name;
963
+ if (typeof item.call_id === "string" && !state.callId)
964
+ state.callId = item.call_id;
965
+ }
880
966
  resetIdleTimer();
881
967
  }
968
+ if (item && typeof item.status === "string")
969
+ finishReason = item.status;
970
+ continue;
882
971
  }
883
- continue;
884
- }
885
- if (type === "response.function_call_arguments.done") {
886
- const args = typeof parsed.arguments === "string" ? parsed.arguments : typeof parsed.argument === "string" ? parsed.argument : "";
887
- const itemId = typeof parsed.item_id === "string" ? parsed.item_id : undefined;
888
- let targetId = itemId;
889
- if (!targetId && typeof parsed.output_index === "number")
890
- targetId = outputIndexToItemId.get(parsed.output_index);
891
- if (targetId && toolCallState.has(targetId) && args) {
892
- toolCallState.get(targetId).arguments = args;
972
+ if (type === "response.content_part.added" || type === "response.content_part.done") {
973
+ continue;
893
974
  }
894
- else if (args && toolCallState.size > 0) {
895
- const lastKey = Array.from(toolCallState.keys()).pop();
896
- if (!toolCallState.get(lastKey).arguments)
897
- toolCallState.get(lastKey).arguments = args;
975
+ if (type === "response.output_text.delta") {
976
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
977
+ if (delta) {
978
+ resetIdleTimer();
979
+ emitVisible(delta);
980
+ }
981
+ continue;
898
982
  }
899
- resetIdleTimer();
900
- continue;
901
- }
902
- if (type === "response.completed") {
903
- const resp = (parsed.response ?? parsed);
904
- if (resp.usage) {
905
- const u = parseMetaUsage(resp.usage);
906
- if (u)
907
- streamUsage = u;
983
+ if (type === "response.reasoning_summary_text.delta") {
984
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
985
+ if (delta) {
986
+ resetIdleTimer();
987
+ emitReasoningDelta(delta);
988
+ }
989
+ continue;
908
990
  }
909
- if (typeof resp.status === "string")
910
- finishReason = resp.status;
911
- if (Array.isArray(resp.output)) {
912
- const out = parseResponsesOutput(resp);
913
- for (const item of out.reasoningItems)
914
- noteReasoningItem(item);
915
- if (out.reasoningSummary && !reasoningSeen.trim()) {
916
- emitReasoningDelta(out.reasoningSummary);
917
- exitReasoning();
991
+ if (type === "response.reasoning_summary_text.done") {
992
+ const textVal = typeof parsed.text === "string" ? parsed.text : "";
993
+ if (textVal && !reasoningSeen.includes(textVal)) {
994
+ const remaining = textVal.slice(reasoningSeen.length);
995
+ if (remaining) {
996
+ resetIdleTimer();
997
+ emitReasoningDelta(remaining);
998
+ }
918
999
  }
919
- if (out.text && !visible.trim()) {
920
- emitVisible(out.text);
1000
+ continue;
1001
+ }
1002
+ if (type === "response.function_call_arguments.delta") {
1003
+ const delta = typeof parsed.delta === "string" ? parsed.delta : "";
1004
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : typeof parsed.itemId === "string" ? parsed.itemId : undefined;
1005
+ let targetId = itemId;
1006
+ if (!targetId && typeof parsed.output_index === "number")
1007
+ targetId = outputIndexToItemId.get(parsed.output_index);
1008
+ if (targetId) {
1009
+ const state = toolCallState.get(targetId);
1010
+ if (state) {
1011
+ state.arguments += delta;
1012
+ resetIdleTimer();
1013
+ if (request.onToolCallDelta) {
1014
+ const canonical = state.name ? fromWireName(state.name) ?? state.name : undefined;
1015
+ request.onToolCallDelta({ index: Array.from(toolCallState.keys()).indexOf(targetId), ...(state.callId ? { id: state.callId } : {}), ...(canonical ? { name: canonical } : {}), argumentsBytes: state.arguments.length });
1016
+ }
1017
+ }
1018
+ else {
1019
+ toolCallState.set(targetId, { id: targetId, callId: targetId, name: "", arguments: delta });
1020
+ resetIdleTimer();
1021
+ }
921
1022
  }
922
- for (const tc of out.toolCalls) {
923
- const exists = Array.from(toolCallState.values()).some((s) => s.callId === tc.id);
924
- if (!exists) {
925
- const id = tc.id;
926
- toolCallState.set(id, { id, callId: tc.id, name: toWireName(tc.name), arguments: tc.rawArguments ?? JSON.stringify(tc.args) });
1023
+ else if (delta) {
1024
+ const anyKey = Array.from(toolCallState.keys()).pop();
1025
+ if (anyKey) {
1026
+ const state = toolCallState.get(anyKey);
1027
+ state.arguments += delta;
1028
+ resetIdleTimer();
927
1029
  }
928
1030
  }
1031
+ continue;
929
1032
  }
930
- continue;
931
- }
932
- if (type === "response.failed" || type === "response.incomplete") {
933
- const resp = (parsed.response ?? parsed);
934
- if (type === "response.incomplete") {
935
- const details = resp.incomplete_details;
936
- const reason = typeof details?.reason === "string" ? details.reason : "";
1033
+ if (type === "response.function_call_arguments.done") {
1034
+ const args = typeof parsed.arguments === "string" ? parsed.arguments : typeof parsed.argument === "string" ? parsed.argument : "";
1035
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : undefined;
1036
+ let targetId = itemId;
1037
+ if (!targetId && typeof parsed.output_index === "number")
1038
+ targetId = outputIndexToItemId.get(parsed.output_index);
1039
+ if (targetId && toolCallState.has(targetId) && args) {
1040
+ toolCallState.get(targetId).arguments = args;
1041
+ }
1042
+ else if (args && toolCallState.size > 0) {
1043
+ const lastKey = Array.from(toolCallState.keys()).pop();
1044
+ if (!toolCallState.get(lastKey).arguments)
1045
+ toolCallState.get(lastKey).arguments = args;
1046
+ }
1047
+ resetIdleTimer();
1048
+ continue;
1049
+ }
1050
+ if (type === "response.completed") {
1051
+ sawTerminalProof = "response-completed";
1052
+ const resp = (parsed.response ?? parsed);
937
1053
  if (resp.usage) {
938
1054
  const u = parseMetaUsage(resp.usage);
939
1055
  if (u)
940
1056
  streamUsage = u;
941
1057
  }
942
- if (reason === "max_output_tokens" && !visible.trim() && toolCallState.size === 0) {
943
- const retryRequest = incompleteBudgetRetry(request, reasoningOn);
944
- const streamMethod = metaProvider.stream;
945
- if (retryRequest && streamMethod && !request.signal?.aborted) {
946
- exitReasoning();
947
- cleanup();
948
- return streamMethod(retryRequest, auth, onToken);
1058
+ if (typeof resp.status === "string")
1059
+ finishReason = resp.status;
1060
+ if (Array.isArray(resp.output)) {
1061
+ const out = parseResponsesOutput(resp);
1062
+ for (const [index, item] of out.reasoningItems.entries()) {
1063
+ const position = out.reasoningItemPositions[index];
1064
+ noteReasoningItem(item, position?.sequence, position?.toolCallIndex);
1065
+ }
1066
+ if (out.reasoningSummary && !reasoningSeen.trim()) {
1067
+ emitReasoningDelta(out.reasoningSummary);
1068
+ }
1069
+ if (out.text && !visible.trim()) {
1070
+ emitVisible(out.text);
1071
+ }
1072
+ for (const tc of out.toolCalls) {
1073
+ const exists = Array.from(toolCallState.values()).some((s) => s.callId === tc.id);
1074
+ if (!exists) {
1075
+ const id = tc.id;
1076
+ toolCallState.set(id, { id, callId: tc.id, name: toWireName(tc.name), arguments: tc.rawArguments ?? JSON.stringify(tc.args) });
1077
+ }
949
1078
  }
950
- throw budgetExhaustedError(request, reasoningOn, payload);
951
1079
  }
952
- finishReason = "incomplete";
953
1080
  continue;
954
1081
  }
955
- const err = resp.error;
956
- const rawDetail = err?.message ?? err?.code ?? type;
957
- const rawStr = String(rawDetail);
958
- const detail = rawStr.trim().length <= 2 ? `${rawStr} — ${payload.slice(0, 300)}` : rawStr;
959
- throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 1000));
960
- }
961
- const usageField = parsed.usage;
962
- if (usageField) {
963
- const u = parseMetaUsage(usageField);
964
- if (u) {
965
- streamUsage = u;
966
- resetIdleTimer();
1082
+ if (type === "response.failed" || type === "response.incomplete") {
1083
+ const resp = (parsed.response ?? parsed);
1084
+ if (type === "response.incomplete") {
1085
+ const details = resp.incomplete_details;
1086
+ const reason = typeof details?.reason === "string" ? details.reason : "";
1087
+ if (resp.usage) {
1088
+ const u = parseMetaUsage(resp.usage);
1089
+ if (u)
1090
+ streamUsage = u;
1091
+ }
1092
+ if (reason === "max_output_tokens" && !visible.trim() && toolCallState.size === 0) {
1093
+ const retryRequest = reasoningSeen.trim()
1094
+ ? undefined
1095
+ : incompleteBudgetRetry(request, reasoningOn);
1096
+ const streamMethod = metaProvider.stream;
1097
+ if (retryRequest && streamMethod && !request.signal?.aborted) {
1098
+ cleanup();
1099
+ completeGenerationAttempt("failure", streamUsage);
1100
+ retryRequest.attemptReason = "provider-retry";
1101
+ return streamMethod(retryRequest, auth, onToken);
1102
+ }
1103
+ completeGenerationAttempt("failure", streamUsage);
1104
+ throw budgetExhaustedError(request, reasoningOn, payload);
1105
+ }
1106
+ finishReason = "incomplete";
1107
+ sawTerminalProof = "response-incomplete";
1108
+ continue;
1109
+ }
1110
+ const err = resp.error;
1111
+ const rawDetail = err?.message ?? err?.code ?? type;
1112
+ const rawStr = String(rawDetail);
1113
+ const detail = rawStr.trim().length <= 2 ? `${rawStr} — ${payload.slice(0, 300)}` : rawStr;
1114
+ throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 1000));
1115
+ }
1116
+ const usageField = parsed.usage;
1117
+ if (usageField) {
1118
+ const u = parseMetaUsage(usageField);
1119
+ if (u) {
1120
+ streamUsage = u;
1121
+ resetIdleTimer();
1122
+ }
1123
+ }
1124
+ const choice = parsed.choices;
1125
+ if (choice) {
1126
+ const chunkUsage = parseMetaUsage(parsed.usage);
1127
+ if (chunkUsage)
1128
+ streamUsage = chunkUsage;
967
1129
  }
968
- }
969
- const choice = parsed.choices;
970
- if (choice) {
971
- const chunkUsage = parseMetaUsage(parsed.usage);
972
- if (chunkUsage)
973
- streamUsage = chunkUsage;
974
1130
  }
975
1131
  }
976
- }
977
- if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
978
- const effort = metaReasoningPayload(request.thinking)?.effort;
979
- const effortText = effort ? ` at ${effort} effort` : "";
980
- 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.`;
981
- if (!inReasoning) {
982
- full += REASONING_OPEN;
983
- visible = full;
984
- onToken(REASONING_OPEN);
1132
+ if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
1133
+ const effort = metaReasoningPayload(request.thinking)?.effort;
1134
+ const effortText = effort ? ` at ${effort} effort` : "";
1135
+ 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.`;
1136
+ reasoningSeen += note;
1137
+ emitStreamReasoningDelta(request.onStreamEvent, note);
985
1138
  }
986
- full += note;
987
- reasoningSeen += note;
988
- onToken(note);
989
- full += REASONING_CLOSE;
990
- onToken(REASONING_CLOSE);
991
- inReasoning = false;
992
- }
993
- else {
994
- exitReasoning();
995
- }
996
- cleanup();
997
- const toolCalls = [];
998
- for (const [, state] of toolCallState) {
999
- if (!state.name && !state.arguments)
1000
- continue;
1001
- const name = state.name || "";
1002
- const canonical = name ? fromWireName(name) ?? name : "";
1003
- if (!canonical)
1004
- continue;
1005
- const raw = state.arguments;
1006
- let args;
1007
- try {
1008
- const parsed = JSON.parse(raw || "{}");
1009
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
1010
- args = parsed;
1011
- else
1012
- args = {};
1139
+ cleanup();
1140
+ let metaToolArgumentBytes = 0;
1141
+ for (const state of toolCallState.values()) {
1142
+ metaToolArgumentBytes += state.arguments.length;
1013
1143
  }
1014
- catch {
1015
- args = parseToolArguments(raw);
1144
+ requireTerminalProof({
1145
+ provider: "Meta Model API",
1146
+ policy: META_STREAM_TERMINAL,
1147
+ signal: sawTerminalProof,
1148
+ answerBytes: full.length,
1149
+ reasoningBytes: reasoningSeen.length,
1150
+ toolArgumentBytes: metaToolArgumentBytes,
1151
+ });
1152
+ const toolCalls = [];
1153
+ for (const [, state] of toolCallState) {
1154
+ if (!state.name && !state.arguments)
1155
+ continue;
1156
+ const name = state.name || "";
1157
+ const canonical = name ? fromWireName(name) ?? name : "";
1158
+ if (!canonical)
1159
+ continue;
1160
+ const raw = state.arguments;
1161
+ let args;
1162
+ try {
1163
+ const parsed = JSON.parse(raw || "{}");
1164
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
1165
+ args = parsed;
1166
+ else
1167
+ args = {};
1168
+ }
1169
+ catch {
1170
+ args = parseToolArguments(raw);
1171
+ }
1172
+ toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
1016
1173
  }
1017
- toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
1018
- }
1019
- if (!visible.trim() && toolCalls.length === 0) {
1020
- if (reasoningSeen.trim()) {
1021
- return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}), ...reasoningReplay() };
1174
+ if (!visible.trim() && toolCalls.length === 0) {
1175
+ if (reasoningSeen.trim()) {
1176
+ return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}), ...reasoningReplay() };
1177
+ }
1178
+ throw new ProviderError(`Meta Model API completed without a visible answer.`);
1022
1179
  }
1023
- throw new ProviderError(`Meta Model API completed without a visible answer.`);
1180
+ return {
1181
+ text: full,
1182
+ provider: "meta",
1183
+ model,
1184
+ ...(toolCalls.length ? { toolCalls } : {}),
1185
+ ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
1186
+ ...(streamUsage ? { usage: streamUsage } : {}),
1187
+ ...reasoningReplay(),
1188
+ };
1024
1189
  }
1025
- return {
1026
- text: full,
1027
- provider: "meta",
1028
- model,
1029
- ...(toolCalls.length ? { toolCalls } : {}),
1030
- ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
1031
- ...(streamUsage ? { usage: streamUsage } : {}),
1032
- ...reasoningReplay(),
1033
- };
1034
- }
1035
- catch (error) {
1036
- if (idleFired) {
1037
- const seconds = Math.round(firedBudgetMs / 1000);
1038
- if (firedWatchdog === "transport" || !sawTransportActivity) {
1039
- if (!sawTransportActivity) {
1040
- throw new ProviderError(`Meta Model API request timed out before any response (${seconds}s) — no data arrived on the connection.`);
1190
+ catch (error) {
1191
+ if (idleFired) {
1192
+ const seconds = Math.round(firedBudgetMs / 1000);
1193
+ if (firedWatchdog === "transport" || !sawTransportActivity) {
1194
+ if (!sawTransportActivity) {
1195
+ throw new ProviderError(`Meta Model API request timed out before any response (${seconds}s) no data arrived on the connection.`);
1196
+ }
1197
+ throw new ProviderError(`Meta Model API stream transport timeout (${seconds}s) — no data arrived on the connection after it had started.`);
1041
1198
  }
1042
- throw new ProviderError(`Meta Model API stream transport timeout (${seconds}s) — no data arrived on the connection after it had started.`);
1199
+ throw new ProviderError(`Meta Model API stream stalled ${STREAM_STALL_MARKER} for ${seconds}s` +
1200
+ (sawStreamProgress
1201
+ ? " 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."
1202
+ : " — the connection stayed open but the model never produced anything. Try another model, or disable thinking with /effort off."));
1043
1203
  }
1044
- throw new ProviderError(`Meta Model API stream stalled — ${STREAM_STALL_MARKER} for ${seconds}s` +
1045
- (sawStreamProgress
1046
- ? " 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."
1047
- : " — the connection stayed open but the model never produced anything. Try another model, or disable thinking with /effort off."));
1204
+ throw error;
1048
1205
  }
1049
- throw error;
1050
- }
1051
- finally {
1052
- cleanup();
1053
- void reader.cancel().catch(() => undefined);
1054
- try {
1055
- reader.releaseLock();
1206
+ finally {
1207
+ cleanup();
1208
+ void reader.cancel().catch(() => undefined);
1209
+ try {
1210
+ reader.releaseLock();
1211
+ }
1212
+ catch { }
1056
1213
  }
1057
- catch { }
1058
- }
1214
+ });
1059
1215
  },
1060
1216
  };
1061
1217
  //# sourceMappingURL=meta.js.map