@pentoshi/clai 4.0.3 → 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 (314) hide show
  1. package/README.md +45 -9
  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/confirm-port.js +6 -3
  6. package/dist/agent/confirm-port.js.map +1 -1
  7. package/dist/agent/context-breakdown.js +2 -0
  8. package/dist/agent/context-breakdown.js.map +1 -1
  9. package/dist/agent/context-manager.d.ts +23 -9
  10. package/dist/agent/context-manager.js +72 -43
  11. package/dist/agent/context-manager.js.map +1 -1
  12. package/dist/agent/events.d.ts +3 -0
  13. package/dist/agent/loop-guard.d.ts +2 -0
  14. package/dist/agent/loop-guard.js +48 -3
  15. package/dist/agent/loop-guard.js.map +1 -1
  16. package/dist/agent/prompt-composer.js +6 -6
  17. package/dist/agent/prompt-composer.js.map +1 -1
  18. package/dist/agent/reliability-policy.d.ts +1 -0
  19. package/dist/agent/reliability-policy.js +6 -3
  20. package/dist/agent/reliability-policy.js.map +1 -1
  21. package/dist/agent/request-accounting.d.ts +97 -0
  22. package/dist/agent/request-accounting.js +207 -0
  23. package/dist/agent/request-accounting.js.map +1 -0
  24. package/dist/agent/request-budget.d.ts +2 -2
  25. package/dist/agent/request-budget.js +2 -4
  26. package/dist/agent/request-budget.js.map +1 -1
  27. package/dist/agent/runner.d.ts +8 -2
  28. package/dist/agent/runner.js +412 -181
  29. package/dist/agent/runner.js.map +1 -1
  30. package/dist/agent/session-title.d.ts +0 -26
  31. package/dist/agent/session-title.js +0 -80
  32. package/dist/agent/session-title.js.map +1 -1
  33. package/dist/agent/tool-history.d.ts +2 -2
  34. package/dist/agent/tool-history.js +19 -6
  35. package/dist/agent/tool-history.js.map +1 -1
  36. package/dist/agent/turn-outcome.d.ts +1 -1
  37. package/dist/agent/turn-outcome.js +2 -12
  38. package/dist/agent/turn-outcome.js.map +1 -1
  39. package/dist/app/adapters/agent-event-adapter.js +19 -0
  40. package/dist/app/adapters/agent-event-adapter.js.map +1 -1
  41. package/dist/app/adapters/current-agent-adapter.js +4 -0
  42. package/dist/app/adapters/current-agent-adapter.js.map +1 -1
  43. package/dist/app/commands/catalog.js +23 -16
  44. package/dist/app/commands/catalog.js.map +1 -1
  45. package/dist/app/controllers/cancel-coordinator.d.ts +51 -0
  46. package/dist/app/controllers/cancel-coordinator.js +48 -0
  47. package/dist/app/controllers/cancel-coordinator.js.map +1 -0
  48. package/dist/app/controllers/session-compact-helper.d.ts +19 -2
  49. package/dist/app/controllers/session-compact-helper.js +100 -127
  50. package/dist/app/controllers/session-compact-helper.js.map +1 -1
  51. package/dist/app/controllers/session-context-usage.d.ts +32 -15
  52. package/dist/app/controllers/session-context-usage.js +186 -51
  53. package/dist/app/controllers/session-context-usage.js.map +1 -1
  54. package/dist/app/controllers/session-controller.d.ts +22 -11
  55. package/dist/app/controllers/session-controller.js +137 -92
  56. package/dist/app/controllers/session-controller.js.map +1 -1
  57. package/dist/app/controllers/session-naming.d.ts +31 -0
  58. package/dist/app/controllers/session-naming.js +173 -0
  59. package/dist/app/controllers/session-naming.js.map +1 -0
  60. package/dist/app/controllers/session-persistence.d.ts +2 -2
  61. package/dist/app/controllers/session-persistence.js +3 -6
  62. package/dist/app/controllers/session-persistence.js.map +1 -1
  63. package/dist/app/controllers/session-turn-request.d.ts +2 -1
  64. package/dist/app/controllers/session-turn-request.js +3 -0
  65. package/dist/app/controllers/session-turn-request.js.map +1 -1
  66. package/dist/app/controllers/turn-continuation.d.ts +0 -6
  67. package/dist/app/controllers/turn-continuation.js +4 -21
  68. package/dist/app/controllers/turn-continuation.js.map +1 -1
  69. package/dist/app/controllers/turn-controller.d.ts +2 -1
  70. package/dist/app/controllers/turn-controller.js +1 -0
  71. package/dist/app/controllers/turn-controller.js.map +1 -1
  72. package/dist/app/events/app-event.d.ts +9 -0
  73. package/dist/app/events/app-event.js.map +1 -1
  74. package/dist/app/ports/agent-port.d.ts +8 -1
  75. package/dist/app/ports/transcript-item.d.ts +9 -1
  76. package/dist/classic/app/ClassicApp.js +3 -3
  77. package/dist/classic/app/ClassicApp.js.map +1 -1
  78. package/dist/classic/app/app-wiring.js +1 -2
  79. package/dist/classic/app/app-wiring.js.map +1 -1
  80. package/dist/classic/app/wiring-interactions.js +4 -9
  81. package/dist/classic/app/wiring-interactions.js.map +1 -1
  82. package/dist/classic/blocks/TurnSummaryBlock.d.ts +3 -0
  83. package/dist/classic/blocks/TurnSummaryBlock.js +6 -0
  84. package/dist/classic/blocks/TurnSummaryBlock.js.map +1 -0
  85. package/dist/classic/blocks/tool-lines.js +2 -1
  86. package/dist/classic/blocks/tool-lines.js.map +1 -1
  87. package/dist/classic/blocks/turn-summary-lines.d.ts +3 -0
  88. package/dist/classic/blocks/turn-summary-lines.js +7 -0
  89. package/dist/classic/blocks/turn-summary-lines.js.map +1 -0
  90. package/dist/classic/chrome/status-rows.d.ts +0 -1
  91. package/dist/classic/chrome/status-rows.js +0 -12
  92. package/dist/classic/chrome/status-rows.js.map +1 -1
  93. package/dist/classic/feed/Feed.js +2 -0
  94. package/dist/classic/feed/Feed.js.map +1 -1
  95. package/dist/classic/feed/feed-blocks.d.ts +1 -1
  96. package/dist/classic/feed/feed-blocks.js +3 -0
  97. package/dist/classic/feed/feed-blocks.js.map +1 -1
  98. package/dist/classic/input/cancel-ladder.d.ts +2 -19
  99. package/dist/classic/input/cancel-ladder.js +13 -20
  100. package/dist/classic/input/cancel-ladder.js.map +1 -1
  101. package/dist/commands/providers.js +2 -0
  102. package/dist/commands/providers.js.map +1 -1
  103. package/dist/llm/adapters/anthropic-tools.d.ts +31 -14
  104. package/dist/llm/adapters/anthropic-tools.js +124 -29
  105. package/dist/llm/adapters/anthropic-tools.js.map +1 -1
  106. package/dist/llm/adapters/gemini-tools.d.ts +16 -4
  107. package/dist/llm/adapters/gemini-tools.js +117 -16
  108. package/dist/llm/adapters/gemini-tools.js.map +1 -1
  109. package/dist/llm/adapters/openai-tools.d.ts +13 -2
  110. package/dist/llm/adapters/openai-tools.js +58 -11
  111. package/dist/llm/adapters/openai-tools.js.map +1 -1
  112. package/dist/llm/agentrouter.js +68 -45
  113. package/dist/llm/agentrouter.js.map +1 -1
  114. package/dist/llm/anthropic.js +69 -36
  115. package/dist/llm/anthropic.js.map +1 -1
  116. package/dist/llm/aws-mantle.js +70 -32
  117. package/dist/llm/aws-mantle.js.map +1 -1
  118. package/dist/llm/bynara.js +26 -69
  119. package/dist/llm/bynara.js.map +1 -1
  120. package/dist/llm/capabilities.d.ts +2 -1
  121. package/dist/llm/capabilities.js +56 -1
  122. package/dist/llm/capabilities.js.map +1 -1
  123. package/dist/llm/context-snapshot.d.ts +100 -0
  124. package/dist/llm/context-snapshot.js +277 -0
  125. package/dist/llm/context-snapshot.js.map +1 -0
  126. package/dist/llm/context-windows.d.ts +10 -0
  127. package/dist/llm/context-windows.js +129 -0
  128. package/dist/llm/context-windows.js.map +1 -0
  129. package/dist/llm/custom-provider-profile.d.ts +75 -0
  130. package/dist/llm/custom-provider-profile.js +558 -0
  131. package/dist/llm/custom-provider-profile.js.map +1 -0
  132. package/dist/llm/custom-providers.d.ts +13 -1
  133. package/dist/llm/custom-providers.js +77 -25
  134. package/dist/llm/custom-providers.js.map +1 -1
  135. package/dist/llm/effort-fallback.d.ts +44 -0
  136. package/dist/llm/effort-fallback.js +101 -0
  137. package/dist/llm/effort-fallback.js.map +1 -0
  138. package/dist/llm/fireworks.d.ts +3 -0
  139. package/dist/llm/fireworks.js +103 -0
  140. package/dist/llm/fireworks.js.map +1 -0
  141. package/dist/llm/free.js +4 -0
  142. package/dist/llm/free.js.map +1 -1
  143. package/dist/llm/gemini.d.ts +1 -1
  144. package/dist/llm/gemini.js +89 -51
  145. package/dist/llm/gemini.js.map +1 -1
  146. package/dist/llm/groq.js +4 -0
  147. package/dist/llm/groq.js.map +1 -1
  148. package/dist/llm/hetzner.d.ts +3 -0
  149. package/dist/llm/hetzner.js +98 -0
  150. package/dist/llm/hetzner.js.map +1 -0
  151. package/dist/llm/http.d.ts +60 -10
  152. package/dist/llm/http.js +465 -156
  153. package/dist/llm/http.js.map +1 -1
  154. package/dist/llm/kimchi.js +4 -0
  155. package/dist/llm/kimchi.js.map +1 -1
  156. package/dist/llm/lightning.js +4 -0
  157. package/dist/llm/lightning.js.map +1 -1
  158. package/dist/llm/meta.d.ts +2 -0
  159. package/dist/llm/meta.js +839 -624
  160. package/dist/llm/meta.js.map +1 -1
  161. package/dist/llm/modal.js +4 -0
  162. package/dist/llm/modal.js.map +1 -1
  163. package/dist/llm/model-families.d.ts +9 -0
  164. package/dist/llm/model-families.js +39 -0
  165. package/dist/llm/model-families.js.map +1 -0
  166. package/dist/llm/nvidia.js +4 -0
  167. package/dist/llm/nvidia.js.map +1 -1
  168. package/dist/llm/ollama.js +44 -40
  169. package/dist/llm/ollama.js.map +1 -1
  170. package/dist/llm/openai.js +4 -0
  171. package/dist/llm/openai.js.map +1 -1
  172. package/dist/llm/openrouter.js +4 -0
  173. package/dist/llm/openrouter.js.map +1 -1
  174. package/dist/llm/operation-ledger.d.ts +60 -0
  175. package/dist/llm/operation-ledger.js +136 -0
  176. package/dist/llm/operation-ledger.js.map +1 -0
  177. package/dist/llm/operation-usage.d.ts +32 -0
  178. package/dist/llm/operation-usage.js +172 -0
  179. package/dist/llm/operation-usage.js.map +1 -0
  180. package/dist/llm/orcarouter.d.ts +7 -0
  181. package/dist/llm/orcarouter.js +166 -0
  182. package/dist/llm/orcarouter.js.map +1 -0
  183. package/dist/llm/provider-profile-layers.d.ts +7 -0
  184. package/dist/llm/provider-profile-layers.js +961 -0
  185. package/dist/llm/provider-profile-layers.js.map +1 -0
  186. package/dist/llm/provider-profile.d.ts +181 -0
  187. package/dist/llm/provider-profile.js +329 -0
  188. package/dist/llm/provider-profile.js.map +1 -0
  189. package/dist/llm/provider-profiles.d.ts +13 -0
  190. package/dist/llm/provider-profiles.js +86 -0
  191. package/dist/llm/provider-profiles.js.map +1 -0
  192. package/dist/llm/provider.d.ts +2 -0
  193. package/dist/llm/provider.js +198 -21
  194. package/dist/llm/provider.js.map +1 -1
  195. package/dist/llm/qwen-cloud.js +4 -0
  196. package/dist/llm/qwen-cloud.js.map +1 -1
  197. package/dist/llm/reasoning-artifacts.d.ts +101 -0
  198. package/dist/llm/reasoning-artifacts.js +440 -0
  199. package/dist/llm/reasoning-artifacts.js.map +1 -0
  200. package/dist/llm/request-fingerprint.d.ts +10 -0
  201. package/dist/llm/request-fingerprint.js +313 -0
  202. package/dist/llm/request-fingerprint.js.map +1 -0
  203. package/dist/llm/request-plan.d.ts +119 -0
  204. package/dist/llm/request-plan.js +279 -0
  205. package/dist/llm/request-plan.js.map +1 -0
  206. package/dist/llm/router.d.ts +19 -1
  207. package/dist/llm/router.js +504 -102
  208. package/dist/llm/router.js.map +1 -1
  209. package/dist/llm/stream-events.d.ts +47 -0
  210. package/dist/llm/stream-events.js +55 -0
  211. package/dist/llm/stream-events.js.map +1 -0
  212. package/dist/llm/stream-terminal.d.ts +30 -0
  213. package/dist/llm/stream-terminal.js +51 -0
  214. package/dist/llm/stream-terminal.js.map +1 -0
  215. package/dist/llm/token-estimate-calibration.d.ts +23 -0
  216. package/dist/llm/token-estimate-calibration.js +140 -0
  217. package/dist/llm/token-estimate-calibration.js.map +1 -0
  218. package/dist/llm/token-usage.d.ts +35 -5
  219. package/dist/llm/token-usage.js +212 -162
  220. package/dist/llm/token-usage.js.map +1 -1
  221. package/dist/llm/tokenrouter.js +13 -6
  222. package/dist/llm/tokenrouter.js.map +1 -1
  223. package/dist/modes/ask.js +11 -5
  224. package/dist/modes/ask.js.map +1 -1
  225. package/dist/noninteractive/start-noninteractive.js +31 -2
  226. package/dist/noninteractive/start-noninteractive.js.map +1 -1
  227. package/dist/noninteractive/stream-blocks.js +15 -4
  228. package/dist/noninteractive/stream-blocks.js.map +1 -1
  229. package/dist/prompts/embedded.js +1 -1
  230. package/dist/prompts/embedded.js.map +1 -1
  231. package/dist/prompts/system.agent.md +6 -0
  232. package/dist/store/config.js +7 -1
  233. package/dist/store/config.js.map +1 -1
  234. package/dist/store/history.d.ts +2 -0
  235. package/dist/store/history.js +19 -10
  236. package/dist/store/history.js.map +1 -1
  237. package/dist/store/plan.js +20 -14
  238. package/dist/store/plan.js.map +1 -1
  239. package/dist/tools/elevated-shell.d.ts +1 -1
  240. package/dist/tools/elevated-shell.js +24 -72
  241. package/dist/tools/elevated-shell.js.map +1 -1
  242. package/dist/tools/fs.d.ts +5 -3
  243. package/dist/tools/fs.js +8 -5
  244. package/dist/tools/fs.js.map +1 -1
  245. package/dist/tools/nmap-runner.js +30 -36
  246. package/dist/tools/nmap-runner.js.map +1 -1
  247. package/dist/tools/sudo-session.d.ts +83 -0
  248. package/dist/tools/sudo-session.js +124 -0
  249. package/dist/tools/sudo-session.js.map +1 -0
  250. package/dist/tui-v2/app/App.js +9 -24
  251. package/dist/tui-v2/app/App.js.map +1 -1
  252. package/dist/tui-v2/components/plan/plan-view.js +5 -3
  253. package/dist/tui-v2/components/plan/plan-view.js.map +1 -1
  254. package/dist/tui-v2/components/status/context-limit-chip.js +2 -2
  255. package/dist/tui-v2/components/status/context-limit-chip.js.map +1 -1
  256. package/dist/tui-v2/components/status/status-line.js +1 -14
  257. package/dist/tui-v2/components/status/status-line.js.map +1 -1
  258. package/dist/tui-v2/components/transcript/intro-card.js +2 -1
  259. package/dist/tui-v2/components/transcript/intro-card.js.map +1 -1
  260. package/dist/tui-v2/components/transcript/notice-row.d.ts +3 -1
  261. package/dist/tui-v2/components/transcript/notice-row.js +28 -3
  262. package/dist/tui-v2/components/transcript/notice-row.js.map +1 -1
  263. package/dist/tui-v2/components/transcript/thinking-block.d.ts +0 -5
  264. package/dist/tui-v2/components/transcript/thinking-block.js +3 -8
  265. package/dist/tui-v2/components/transcript/thinking-block.js.map +1 -1
  266. package/dist/tui-v2/components/transcript/tool-card.js +16 -7
  267. package/dist/tui-v2/components/transcript/tool-card.js.map +1 -1
  268. package/dist/tui-v2/components/transcript/transcript-row.d.ts +0 -2
  269. package/dist/tui-v2/components/transcript/transcript-row.js +7 -3
  270. package/dist/tui-v2/components/transcript/transcript-row.js.map +1 -1
  271. package/dist/tui-v2/components/transcript/transcript-view.js +1 -3
  272. package/dist/tui-v2/components/transcript/transcript-view.js.map +1 -1
  273. package/dist/tui-v2/composer/composer-editor.js +2 -1
  274. package/dist/tui-v2/composer/composer-editor.js.map +1 -1
  275. package/dist/types.d.ts +156 -1
  276. package/dist/types.js +3 -0
  277. package/dist/types.js.map +1 -1
  278. package/dist/ui-core/bootstrap/composition-root.d.ts +2 -0
  279. package/dist/ui-core/bootstrap/composition-root.js +26 -3
  280. package/dist/ui-core/bootstrap/composition-root.js.map +1 -1
  281. package/dist/ui-core/commands/picker-commands.js +11 -5
  282. package/dist/ui-core/commands/picker-commands.js.map +1 -1
  283. package/dist/ui-core/commands/session-commands.js +41 -5
  284. package/dist/ui-core/commands/session-commands.js.map +1 -1
  285. package/dist/ui-core/plan/plan-lifecycle.js +3 -2
  286. package/dist/ui-core/plan/plan-lifecycle.js.map +1 -1
  287. package/dist/ui-core/rendering/context-limit.js +4 -1
  288. package/dist/ui-core/rendering/context-limit.js.map +1 -1
  289. package/dist/ui-core/rendering/duration.d.ts +5 -0
  290. package/dist/ui-core/rendering/duration.js +43 -0
  291. package/dist/ui-core/rendering/duration.js.map +1 -0
  292. package/dist/ui-core/rendering/intro-header.js +2 -2
  293. package/dist/ui-core/rendering/intro-header.js.map +1 -1
  294. package/dist/ui-core/rendering/theme.d.ts +1 -0
  295. package/dist/ui-core/rendering/theme.js +2 -0
  296. package/dist/ui-core/rendering/theme.js.map +1 -1
  297. package/dist/ui-core/rendering/transcript-semantic.js +2 -0
  298. package/dist/ui-core/rendering/transcript-semantic.js.map +1 -1
  299. package/dist/ui-core/state/transcript-compaction.js +2 -0
  300. package/dist/ui-core/state/transcript-compaction.js.map +1 -1
  301. package/dist/ui-core/state/transcript-hydrate.js +34 -4
  302. package/dist/ui-core/state/transcript-hydrate.js.map +1 -1
  303. package/dist/ui-core/state/transcript-reducer.d.ts +5 -0
  304. package/dist/ui-core/state/transcript-reducer.js +96 -8
  305. package/dist/ui-core/state/transcript-reducer.js.map +1 -1
  306. package/dist/ui-core/state/transcript-store.d.ts +10 -1
  307. package/dist/ui-core/state/transcript-store.js +22 -1
  308. package/dist/ui-core/state/transcript-store.js.map +1 -1
  309. package/dist/ui-core/state/transcript-types.d.ts +9 -1
  310. package/dist/ui-core/state/transcript-types.js +3 -1
  311. package/dist/ui-core/state/transcript-types.js.map +1 -1
  312. package/dist/version.generated.d.ts +2 -2
  313. package/dist/version.generated.js +2 -2
  314. 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)
@@ -204,13 +264,34 @@ function buildResponsesBody(options) {
204
264
  }
205
265
  return JSON.stringify(body);
206
266
  }
267
+ const META_MAX_OUTPUT_TOKENS_CAP = 65536;
268
+ const MAX_INCOMPLETE_BUDGET_RETRIES = 2;
269
+ const incompleteBudgetRetries = new WeakMap();
270
+ function incompleteBudgetRetry(request, reasoningOn) {
271
+ const used = incompleteBudgetRetries.get(request) ?? 0;
272
+ const currentBudget = Math.max(16, request.maxTokens ?? (reasoningOn ? 8192 : 4096));
273
+ const nextBudget = Math.min(currentBudget * 2, META_MAX_OUTPUT_TOKENS_CAP);
274
+ if (used >= MAX_INCOMPLETE_BUDGET_RETRIES || nextBudget <= currentBudget)
275
+ return undefined;
276
+ const retryRequest = { ...request, maxTokens: nextBudget };
277
+ incompleteBudgetRetries.set(retryRequest, used + 1);
278
+ return retryRequest;
279
+ }
280
+ function budgetExhaustedError(request, reasoningOn, payload) {
281
+ const currentBudget = Math.max(16, request.maxTokens ?? (reasoningOn ? 8192 : 4096));
282
+ const effort = metaReasoningPayload(request.thinking)?.effort;
283
+ const retried = (incompleteBudgetRetries.get(request) ?? 0) > 0;
284
+ return new ProviderError(`Meta Model API spent the entire output budget (${currentBudget} tokens${effort ? `, mostly on reasoning at ${effort} effort` : ""}) without producing an answer${retried ? ", even after raising max_output_tokens" : ""}. Lower the effort with /effort high or raise max_tokens.`, undefined, payload.slice(0, 1000));
285
+ }
207
286
  function parseResponsesOutput(data) {
208
287
  const output = Array.isArray(data.output) ? data.output : [];
209
288
  let text = "";
210
289
  let reasoningSummary = "";
211
290
  const toolCalls = [];
212
291
  const reasoningItems = [];
213
- for (const item of output) {
292
+ const reasoningItemSequences = [];
293
+ const toolCallSequences = [];
294
+ for (const [sequence, item] of output.entries()) {
214
295
  if (!item || typeof item !== "object")
215
296
  continue;
216
297
  const obj = item;
@@ -229,12 +310,8 @@ function parseResponsesOutput(data) {
229
310
  if (s)
230
311
  reasoningSummary += s;
231
312
  if (typeof obj.encrypted_content === "string" && obj.encrypted_content) {
232
- reasoningItems.push({
233
- type: "reasoning",
234
- ...(typeof obj.id === "string" ? { id: obj.id } : {}),
235
- summary: Array.isArray(obj.summary) ? obj.summary : [],
236
- encrypted_content: obj.encrypted_content,
237
- });
313
+ reasoningItems.push({ ...obj });
314
+ reasoningItemSequences.push(sequence);
238
315
  }
239
316
  }
240
317
  else if (obj.type === "function_call") {
@@ -253,23 +330,52 @@ function parseResponsesOutput(data) {
253
330
  catch {
254
331
  args = parseToolArguments(rawArgs);
255
332
  }
333
+ const toolCallIndex = toolCalls.length;
256
334
  toolCalls.push({ id: callId, name: canonical, args, rawArguments: rawArgs });
335
+ toolCallSequences.push({ sequence, toolCallIndex });
257
336
  }
258
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
+ });
259
344
  const usage = parseMetaUsage(data.usage);
260
- return { text, toolCalls, usage, reasoningSummary, reasoningItems };
345
+ return {
346
+ text,
347
+ toolCalls,
348
+ usage,
349
+ reasoningSummary,
350
+ reasoningItems,
351
+ reasoningItemPositions,
352
+ };
261
353
  }
262
- function foldResponsesReasoning(text, reasoningSummary, usage, effort) {
263
- if (reasoningSummary && reasoningSummary.trim()) {
264
- return `${wrapReasoning(reasoningSummary)}${text}`;
265
- }
266
- const tokens = usage?.reasoningTokens ?? 0;
267
- if (tokens > 0) {
268
- const effortText = effort ? ` at ${effort} effort` : "";
269
- 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.`;
270
- return `${wrapReasoning(note)}${text}`;
271
- }
272
- 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;
273
379
  }
274
380
  function readWithAbort(reader, signal) {
275
381
  if (signal.aborted) {
@@ -306,6 +412,7 @@ function readWithAbort(reader, signal) {
306
412
  }
307
413
  export const metaProvider = {
308
414
  id: "meta",
415
+ reasoningStyle: "meta",
309
416
  displayName: "Meta Model API",
310
417
  defaultModel: defaultModels.meta,
311
418
  envVar: "MODEL_API_KEY",
@@ -345,187 +452,49 @@ export const metaProvider = {
345
452
  if (!auth.apiKey)
346
453
  throw new Error("Meta Model API key is required");
347
454
  const model = request.model ?? defaultModels.meta;
348
- const supportsVision = modelAcceptsImages("meta", model);
349
- const body = buildResponsesBody({
350
- model,
351
- messages: request.messages,
352
- maxTokens: request.maxTokens,
353
- temperature: request.temperature,
354
- stream: false,
355
- reasoning: request.thinking,
356
- supportsVision,
357
- tools: request.tools,
358
- parallelToolCalls: request.parallelToolCalls,
359
- });
360
- let response;
361
- try {
362
- response = await fetch(`${baseUrl}/responses`, {
363
- method: "POST",
364
- signal: request.signal ?? null,
365
- headers: {
366
- "content-type": "application/json",
367
- accept: "application/json",
368
- authorization: `Bearer ${auth.apiKey}`,
369
- },
370
- body,
371
- verbose: process.env.CLAI_VERBOSE === "true",
372
- });
373
- }
374
- catch (error) {
375
- if (error instanceof Error && error.name === "AbortError")
376
- throw error;
377
- const msg = error instanceof Error ? error.message : String(error);
378
- throw new ProviderError(`Meta Model API request could not be sent (${msg}). Check connectivity to ${baseUrl}.`);
379
- }
380
- let data;
381
- try {
382
- data = await readJson(response);
383
- }
384
- catch (error) {
385
- if (error instanceof ProviderError) {
386
- throw new ProviderError(`Meta Model API (model=${model}): ${error.message}`, error.status, error.body, error.retryAfterSeconds);
387
- }
388
- throw error;
389
- }
390
- const parsed = parseResponsesOutput(data);
391
- const usage = parsed.usage ?? parseMetaUsage(data.usage);
392
- const effort = metaReasoningPayload(request.thinking)?.effort;
393
- const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usage, effort);
394
- if (!full.trim() && parsed.toolCalls.length === 0) {
395
- 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.`);
396
- }
397
- return {
398
- text: full,
455
+ return runGenerationAttempt(request, {
399
456
  provider: "meta",
400
457
  model,
401
- ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
402
- ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
403
- ...(usage ? { usage } : {}),
404
- ...(parsed.reasoningItems.length
405
- ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
406
- : {}),
407
- };
408
- },
409
- async stream(request, auth, onToken) {
410
- if (!auth.apiKey)
411
- throw new Error("Meta Model API key is required");
412
- const model = request.model ?? defaultModels.meta;
413
- const supportsVision = modelAcceptsImages("meta", model);
414
- const reasoningOn = Boolean(request.thinking?.enabled);
415
- const idleTimeoutMs = THINKING_STREAM_IDLE_TIMEOUT_MS;
416
- const initialIdleTimeoutMs = THINKING_STREAM_INITIAL_IDLE_TIMEOUT_MS;
417
- const outputIdleTimeoutMs = Math.round(Math.max(idleTimeoutMs, initialIdleTimeoutMs) * 1.5);
418
- const idleController = new AbortController();
419
- let transportTimer;
420
- let outputTimer;
421
- let idleFired = false;
422
- let firedWatchdog;
423
- let firedBudgetMs = initialIdleTimeoutMs;
424
- let sawTransportActivity = false;
425
- let sawStreamProgress = false;
426
- const fireStall = (watchdog, budgetMs) => {
427
- if (idleFired)
428
- return;
429
- idleFired = true;
430
- firedWatchdog = watchdog;
431
- firedBudgetMs = budgetMs;
432
- idleController.abort();
433
- };
434
- const armTransportTimer = (budgetMs) => {
435
- if (transportTimer)
436
- clearTimeout(transportTimer);
437
- transportTimer = setTimeout(() => fireStall("transport", budgetMs), budgetMs);
438
- };
439
- const noteTransportActivity = () => {
440
- sawTransportActivity = true;
441
- armTransportTimer(idleTimeoutMs);
442
- };
443
- const resetIdleTimer = () => {
444
- sawStreamProgress = true;
445
- noteTransportActivity();
446
- if (outputTimer)
447
- clearTimeout(outputTimer);
448
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
449
- };
450
- armTransportTimer(initialIdleTimeoutMs);
451
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
452
- const clearIdleTimers = () => {
453
- if (transportTimer)
454
- clearTimeout(transportTimer);
455
- if (outputTimer)
456
- clearTimeout(outputTimer);
457
- transportTimer = undefined;
458
- outputTimer = undefined;
459
- };
460
- const onCallerAbort = () => idleController.abort(request.signal?.reason);
461
- request.signal?.addEventListener("abort", onCallerAbort, { once: true });
462
- const body = buildResponsesBody({
463
- model,
464
- messages: request.messages,
465
- maxTokens: request.maxTokens,
466
- temperature: request.temperature,
467
- stream: true,
468
- reasoning: request.thinking,
469
- supportsVision,
470
- tools: request.tools,
471
- parallelToolCalls: request.parallelToolCalls,
472
- });
473
- let response;
474
- let lastFetchError;
475
- for (let fetchAttempt = 0; fetchAttempt < 2; fetchAttempt++) {
476
- if (fetchAttempt > 0) {
477
- await new Promise((r) => setTimeout(r, 1000));
478
- if (request.signal?.aborted)
479
- throw request.signal.reason;
480
- if (idleFired)
481
- break;
482
- armTransportTimer(initialIdleTimeoutMs);
483
- if (outputTimer) {
484
- clearTimeout(outputTimer);
485
- outputTimer = setTimeout(() => fireStall("output", outputIdleTimeoutMs), outputIdleTimeoutMs);
486
- }
487
- }
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;
488
474
  try {
489
- response = await fetch(`${baseUrl}/responses`, {
475
+ response = await generationFetch(`${baseUrl}/responses`, {
490
476
  method: "POST",
491
- signal: idleController.signal,
477
+ signal: request.signal ?? null,
492
478
  headers: {
493
479
  "content-type": "application/json",
494
- accept: "text/event-stream",
480
+ accept: "application/json",
495
481
  authorization: `Bearer ${auth.apiKey}`,
496
482
  },
497
483
  body,
498
484
  verbose: process.env.CLAI_VERBOSE === "true",
499
485
  });
500
- lastFetchError = undefined;
501
- break;
502
486
  }
503
487
  catch (error) {
504
- lastFetchError = error;
505
- if (idleFired) {
506
- clearIdleTimers();
507
- request.signal?.removeEventListener("abort", onCallerAbort);
508
- throw new ProviderError(`Meta Model API request timed out before any response (${Math.round(firedBudgetMs / 1000)}s) — no data arrived on the connection.`);
509
- }
510
- const msg = error instanceof Error ? error.message : String(error);
511
- const transient = /fetch failed|network error|etimedout|enotfound|econnreset|premature close|socket.*closed|aborted without reason/i.test(msg);
512
- if (!transient || sawStreamProgress)
488
+ if (error instanceof Error && error.name === "AbortError")
513
489
  throw error;
514
- 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}.`);
515
494
  }
516
- }
517
- if (!response) {
518
- clearIdleTimers();
519
- request.signal?.removeEventListener("abort", onCallerAbort);
520
- if (lastFetchError)
521
- throw lastFetchError;
522
- throw new ProviderError(`Meta Model API request failed before a response was received.`);
523
- }
524
- if (!response.ok) {
525
- clearIdleTimers();
526
- request.signal?.removeEventListener("abort", onCallerAbort);
495
+ let data;
527
496
  try {
528
- await readJson(response);
497
+ data = await readJson(response);
529
498
  }
530
499
  catch (error) {
531
500
  if (error instanceof ProviderError) {
@@ -533,470 +502,716 @@ export const metaProvider = {
533
502
  }
534
503
  throw error;
535
504
  }
536
- }
537
- if (!response.body) {
538
- clearIdleTimers();
539
- request.signal?.removeEventListener("abort", onCallerAbort);
540
- throw new ProviderError(`Meta Model API returned no stream body`);
541
- }
542
- const contentType = response.headers.get("content-type") ?? "";
543
- if (response.status === 202 || /\bapplication\/json\b/i.test(contentType)) {
544
- clearIdleTimers();
545
- request.signal?.removeEventListener("abort", onCallerAbort);
546
- const data = await readJson(response);
547
- if (response.status === 202) {
548
- const requestId = data.requestId ?? data.id;
549
- 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));
550
- }
551
505
  const parsed = parseResponsesOutput(data);
552
- const usageTmp = parsed.usage ?? parseMetaUsage(data.usage);
553
- const effortTmp = metaReasoningPayload(request.thinking)?.effort;
554
- const full = foldResponsesReasoning(parsed.text, parsed.reasoningSummary, usageTmp, effortTmp);
555
- if (full.trim() || parsed.toolCalls.length > 0) {
556
- if (full.trim())
557
- onToken(full);
558
- return {
559
- text: full,
560
- provider: "meta",
561
- model,
562
- ...(parsed.toolCalls.length ? { toolCalls: parsed.toolCalls } : {}),
563
- ...(parsed.toolCalls.length ? { finishReason: "tool_calls" } : { finishReason: "stop" }),
564
- ...(usageTmp ? { usage: usageTmp } : {}),
565
- ...(parsed.reasoningItems.length
566
- ? { reasoningBlock: { text: parsed.reasoningSummary, items: parsed.reasoningItems } }
567
- : {}),
568
- };
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
+ }
569
520
  }
570
- 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));
571
- }
572
- const decoder = new TextDecoder();
573
- const reader = response.body.getReader();
574
- let buffer = "";
575
- let full = "";
576
- let visible = "";
577
- let reasoningSeen = "";
578
- let inReasoning = false;
579
- let finishReason;
580
- let streamUsage;
581
- const toolCallState = new Map();
582
- const outputIndexToItemId = new Map();
583
- let responseId;
584
- const reasoningItems = [];
585
- const reasoningItemKeys = new Set();
586
- const noteReasoningItem = (item) => {
587
- const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : "";
588
- if (!encrypted)
589
- return;
590
- const id = typeof item.id === "string" ? item.id : undefined;
591
- const key = id ?? encrypted.slice(0, 64);
592
- if (reasoningItemKeys.has(key))
593
- return;
594
- reasoningItemKeys.add(key);
595
- reasoningItems.push({
596
- type: "reasoning",
597
- ...(id ? { id } : {}),
598
- summary: Array.isArray(item.summary) ? item.summary : [],
599
- encrypted_content: encrypted,
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.`);
525
+ }
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,
600
609
  });
601
- };
602
- const reasoningReplay = () => reasoningItems.length ? { reasoningBlock: { text: reasoningSeen, items: reasoningItems } } : {};
603
- const enterReasoning = () => {
604
- if (inReasoning)
605
- return;
606
- inReasoning = true;
607
- full += REASONING_OPEN;
608
- onToken(REASONING_OPEN);
609
- };
610
- const exitReasoning = () => {
611
- if (!inReasoning)
612
- return;
613
- inReasoning = false;
614
- full += REASONING_CLOSE;
615
- onToken(REASONING_CLOSE);
616
- };
617
- const emitVisible = (text) => {
618
- if (!text)
619
- return;
620
- if (inReasoning)
621
- exitReasoning();
622
- visible += text;
623
- full += text;
624
- onToken(text);
625
- };
626
- const emitReasoningDelta = (text) => {
627
- if (!text)
628
- return;
629
- enterReasoning();
630
- reasoningSeen += text;
631
- full += text;
632
- onToken(text);
633
- };
634
- const cleanup = () => {
635
- clearIdleTimers();
636
- request.signal?.removeEventListener("abort", onCallerAbort);
637
- idleController.signal.removeEventListener("abort", cancelReaderOnAbort);
638
- };
639
- const cancelReaderOnAbort = () => {
640
- reader.cancel().catch(() => undefined);
641
- };
642
- idleController.signal.addEventListener("abort", cancelReaderOnAbort, { once: true });
643
- const sseFrames = createSseFrameAssembler();
644
- try {
645
- while (true) {
646
- request.signal?.throwIfAborted();
647
- if (idleController.signal.aborted)
648
- throw new Error("Stream aborted");
649
- const { done, value } = await readWithAbort(reader, idleController.signal);
650
- request.signal?.throwIfAborted();
651
- if (idleController.signal.aborted)
652
- throw new Error("Stream aborted");
653
- 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;
654
638
  break;
655
- if (value && value.byteLength > 0)
656
- noteTransportActivity();
657
- buffer += decoder.decode(value, { stream: true });
658
- const lines = buffer.split("\n");
659
- buffer = lines.pop() ?? "";
660
- for (const line of lines) {
661
- const payload = sseFrames.pushLine(line);
662
- if (payload === undefined)
663
- continue;
664
- if (payload === "[DONE]") {
665
- if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
666
- const effort = metaReasoningPayload(request.thinking)?.effort;
667
- const effortText = effort ? ` at ${effort} effort` : "";
668
- 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.`;
669
- emitReasoningDelta(note);
670
- exitReasoning();
671
- }
672
- else {
673
- exitReasoning();
674
- }
675
- cleanup();
676
- const toolCalls = [];
677
- for (const [, state] of toolCallState) {
678
- if (!state.name)
679
- continue;
680
- const canonical = state.name ? fromWireName(state.name) ?? state.name : state.name ?? "";
681
- const raw = state.arguments;
682
- let args;
683
- try {
684
- const parsed = JSON.parse(raw);
685
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
686
- args = parsed;
687
- else
688
- args = {};
689
- }
690
- catch {
691
- args = parseToolArguments(raw);
692
- }
693
- toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
694
- }
695
- if (!visible.trim() && toolCalls.length === 0) {
696
- if (reasoningSeen.trim()) {
697
- return { text: full, provider: "meta", model, finishReason: finishReason ?? "stop", ...(streamUsage ? { usage: streamUsage } : {}), ...reasoningReplay() };
698
- }
699
- 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);
700
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);
701
709
  return {
702
- text: full,
710
+ text: parsed.text,
703
711
  provider: "meta",
704
712
  model,
705
- ...(toolCalls.length ? { toolCalls } : {}),
706
- ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
707
- ...(streamUsage ? { usage: streamUsage } : {}),
708
- ...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 } : {}),
709
720
  };
710
721
  }
711
- let parsed;
712
- try {
713
- parsed = JSON.parse(payload);
714
- }
715
- catch {
716
- 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`);
717
734
  }
718
- if (parsed.error) {
719
- const rawDetail = typeof parsed.error === "string"
720
- ? parsed.error
721
- : parsed.error.message ?? parsed.error.type ?? "unknown error";
722
- const detail = rawDetail.trim().length <= 2 ? `${rawDetail} — ${payload.slice(0, 300)}` : rawDetail;
723
- 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;
724
772
  }
725
- const type = parsed.type;
726
- if (type === "response.created" || type === "response.in_progress") {
727
- const resp = (parsed.response ?? parsed);
728
- if (typeof resp.id === "string")
729
- responseId = resp.id;
730
- 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 };
731
791
  }
732
- if (type === "response.output_item.added") {
733
- const item = parsed.item;
734
- 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)
735
846
  continue;
736
- const outputIndex = typeof parsed.output_index === "number" ? parsed.output_index : undefined;
737
- const itemId = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
738
- if (outputIndex !== undefined && itemId)
739
- outputIndexToItemId.set(outputIndex, itemId);
740
- if (item.type === "function_call") {
741
- const id = typeof item.id === "string" ? item.id : typeof item.call_id === "string" ? item.call_id : itemId ?? `call_${toolCallState.size}`;
742
- const callId = typeof item.call_id === "string" ? item.call_id : id;
743
- const name = typeof item.name === "string" ? item.name : "";
744
- const args = typeof item.arguments === "string" ? item.arguments : "";
745
- toolCallState.set(id, { id, callId, name, arguments: args });
746
- resetIdleTimer();
747
- if (request.onToolCallDelta) {
748
- const canonical = name ? fromWireName(name) ?? name : undefined;
749
- 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);
750
853
  }
751
- }
752
- else if (item.type === "reasoning") {
753
- const s = extractReasoningSummary(item);
754
- if (s) {
755
- resetIdleTimer();
756
- 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 });
757
873
  }
758
- }
759
- else if (item.type === "message") {
760
- resetIdleTimer();
761
- }
762
- continue;
763
- }
764
- if (type === "response.output_item.done") {
765
- const item = parsed.item;
766
- if (item?.type === "reasoning")
767
- noteReasoningItem(item);
768
- if (item?.type === "function_call") {
769
- const id = typeof item.id === "string" ? item.id : typeof parsed.item_id === "string" ? parsed.item_id : undefined;
770
- if (id && toolCallState.has(id)) {
771
- const state = toolCallState.get(id);
772
- if (typeof item.arguments === "string" && item.arguments.length > state.arguments.length)
773
- state.arguments = item.arguments;
774
- if (typeof item.name === "string" && !state.name)
775
- state.name = item.name;
776
- if (typeof item.call_id === "string" && !state.callId)
777
- 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.`);
778
879
  }
779
- 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
+ };
780
889
  }
781
- if (item && typeof item.status === "string")
782
- finishReason = item.status;
783
- continue;
784
- }
785
- if (type === "response.content_part.added" || type === "response.content_part.done") {
786
- continue;
787
- }
788
- if (type === "response.output_text.delta") {
789
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
790
- if (delta) {
791
- resetIdleTimer();
792
- emitVisible(delta);
890
+ let parsed;
891
+ try {
892
+ parsed = JSON.parse(payload);
793
893
  }
794
- continue;
795
- }
796
- if (type === "response.reasoning_summary_text.delta") {
797
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
798
- if (delta) {
799
- resetIdleTimer();
800
- emitReasoningDelta(delta);
894
+ catch {
895
+ continue;
801
896
  }
802
- continue;
803
- }
804
- if (type === "response.reasoning_summary_text.done") {
805
- const textVal = typeof parsed.text === "string" ? parsed.text : "";
806
- if (textVal && !reasoningSeen.includes(textVal)) {
807
- const remaining = textVal.slice(reasoningSeen.length);
808
- if (remaining) {
809
- resetIdleTimer();
810
- emitReasoningDelta(remaining);
811
- }
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));
812
903
  }
813
- exitReasoning();
814
- continue;
815
- }
816
- if (type === "response.function_call_arguments.delta") {
817
- const delta = typeof parsed.delta === "string" ? parsed.delta : "";
818
- const itemId = typeof parsed.item_id === "string" ? parsed.item_id : typeof parsed.itemId === "string" ? parsed.itemId : undefined;
819
- let targetId = itemId;
820
- if (!targetId && typeof parsed.output_index === "number")
821
- targetId = outputIndexToItemId.get(parsed.output_index);
822
- if (targetId) {
823
- const state = toolCallState.get(targetId);
824
- if (state) {
825
- 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
+ }
826
929
  resetIdleTimer();
827
930
  if (request.onToolCallDelta) {
828
- const canonical = state.name ? fromWireName(state.name) ?? state.name : undefined;
829
- 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);
830
941
  }
831
942
  }
832
- else {
833
- toolCallState.set(targetId, { id: targetId, callId: targetId, name: "", arguments: delta });
943
+ else if (item.type === "message") {
834
944
  resetIdleTimer();
835
945
  }
946
+ continue;
836
947
  }
837
- else if (delta) {
838
- const anyKey = Array.from(toolCallState.keys()).pop();
839
- if (anyKey) {
840
- const state = toolCallState.get(anyKey);
841
- 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
+ }
842
966
  resetIdleTimer();
843
967
  }
968
+ if (item && typeof item.status === "string")
969
+ finishReason = item.status;
970
+ continue;
844
971
  }
845
- continue;
846
- }
847
- if (type === "response.function_call_arguments.done") {
848
- const args = typeof parsed.arguments === "string" ? parsed.arguments : typeof parsed.argument === "string" ? parsed.argument : "";
849
- const itemId = typeof parsed.item_id === "string" ? parsed.item_id : undefined;
850
- let targetId = itemId;
851
- if (!targetId && typeof parsed.output_index === "number")
852
- targetId = outputIndexToItemId.get(parsed.output_index);
853
- if (targetId && toolCallState.has(targetId) && args) {
854
- toolCallState.get(targetId).arguments = args;
972
+ if (type === "response.content_part.added" || type === "response.content_part.done") {
973
+ continue;
855
974
  }
856
- else if (args && toolCallState.size > 0) {
857
- const lastKey = Array.from(toolCallState.keys()).pop();
858
- if (!toolCallState.get(lastKey).arguments)
859
- 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;
860
982
  }
861
- resetIdleTimer();
862
- continue;
863
- }
864
- if (type === "response.completed") {
865
- const resp = (parsed.response ?? parsed);
866
- if (resp.usage) {
867
- const u = parseMetaUsage(resp.usage);
868
- if (u)
869
- 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;
870
990
  }
871
- if (typeof resp.status === "string")
872
- finishReason = resp.status;
873
- if (Array.isArray(resp.output)) {
874
- const out = parseResponsesOutput(resp);
875
- for (const item of out.reasoningItems)
876
- noteReasoningItem(item);
877
- if (out.reasoningSummary && !reasoningSeen.trim()) {
878
- emitReasoningDelta(out.reasoningSummary);
879
- 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
+ }
880
999
  }
881
- if (out.text && !visible.trim()) {
882
- 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
+ }
883
1022
  }
884
- for (const tc of out.toolCalls) {
885
- const exists = Array.from(toolCallState.values()).some((s) => s.callId === tc.id);
886
- if (!exists) {
887
- const id = tc.id;
888
- 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();
889
1029
  }
890
1030
  }
1031
+ continue;
891
1032
  }
892
- continue;
893
- }
894
- if (type === "response.failed" || type === "response.incomplete") {
895
- const resp = (parsed.response ?? parsed);
896
- const err = resp.error;
897
- const rawDetail = err?.message ?? err?.code ?? type;
898
- const rawStr = String(rawDetail);
899
- const detail = rawStr.trim().length <= 2 ? `${rawStr} — ${payload.slice(0, 300)}` : rawStr;
900
- throw new ProviderError(`Meta Model API stream error: ${detail}`, undefined, payload.slice(0, 1000));
901
- }
902
- const usageField = parsed.usage;
903
- if (usageField) {
904
- const u = parseMetaUsage(usageField);
905
- if (u) {
906
- streamUsage = u;
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
+ }
907
1047
  resetIdleTimer();
1048
+ continue;
1049
+ }
1050
+ if (type === "response.completed") {
1051
+ sawTerminalProof = "response-completed";
1052
+ const resp = (parsed.response ?? parsed);
1053
+ if (resp.usage) {
1054
+ const u = parseMetaUsage(resp.usage);
1055
+ if (u)
1056
+ streamUsage = u;
1057
+ }
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
+ }
1078
+ }
1079
+ }
1080
+ continue;
1081
+ }
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;
908
1129
  }
909
- }
910
- const choice = parsed.choices;
911
- if (choice) {
912
- const chunkUsage = parseMetaUsage(parsed.usage);
913
- if (chunkUsage)
914
- streamUsage = chunkUsage;
915
1130
  }
916
1131
  }
917
- }
918
- if (!reasoningSeen.trim() && streamUsage?.reasoningTokens && streamUsage.reasoningTokens > 0 && (visible.trim() || toolCallState.size > 0)) {
919
- const effort = metaReasoningPayload(request.thinking)?.effort;
920
- const effortText = effort ? ` at ${effort} effort` : "";
921
- 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.`;
922
- if (!inReasoning) {
923
- full += REASONING_OPEN;
924
- visible = full;
925
- 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);
926
1138
  }
927
- full += note;
928
- reasoningSeen += note;
929
- onToken(note);
930
- full += REASONING_CLOSE;
931
- onToken(REASONING_CLOSE);
932
- inReasoning = false;
933
- }
934
- else {
935
- exitReasoning();
936
- }
937
- cleanup();
938
- const toolCalls = [];
939
- for (const [, state] of toolCallState) {
940
- if (!state.name && !state.arguments)
941
- continue;
942
- const name = state.name || "";
943
- const canonical = name ? fromWireName(name) ?? name : "";
944
- if (!canonical)
945
- continue;
946
- const raw = state.arguments;
947
- let args;
948
- try {
949
- const parsed = JSON.parse(raw || "{}");
950
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
951
- args = parsed;
952
- else
953
- args = {};
1139
+ cleanup();
1140
+ let metaToolArgumentBytes = 0;
1141
+ for (const state of toolCallState.values()) {
1142
+ metaToolArgumentBytes += state.arguments.length;
954
1143
  }
955
- catch {
956
- 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 });
957
1173
  }
958
- toolCalls.push({ id: state.callId ?? state.id ?? `call_${toolCalls.length}`, name: canonical, args, rawArguments: raw });
959
- }
960
- if (!visible.trim() && toolCalls.length === 0) {
961
- if (reasoningSeen.trim()) {
962
- 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.`);
963
1179
  }
964
- 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
+ };
965
1189
  }
966
- return {
967
- text: full,
968
- provider: "meta",
969
- model,
970
- ...(toolCalls.length ? { toolCalls } : {}),
971
- ...(finishReason ? { finishReason } : toolCalls.length ? { finishReason: "tool_calls" } : {}),
972
- ...(streamUsage ? { usage: streamUsage } : {}),
973
- ...reasoningReplay(),
974
- };
975
- }
976
- catch (error) {
977
- if (idleFired) {
978
- const seconds = Math.round(firedBudgetMs / 1000);
979
- if (firedWatchdog === "transport" || !sawTransportActivity) {
980
- if (!sawTransportActivity) {
981
- 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.`);
982
1198
  }
983
- 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."));
984
1203
  }
985
- throw new ProviderError(`Meta Model API stream stalled — ${STREAM_STALL_MARKER} for ${seconds}s` +
986
- (sawStreamProgress
987
- ? " 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."
988
- : " — the connection stayed open but the model never produced anything. Try another model, or disable thinking with /effort off."));
1204
+ throw error;
989
1205
  }
990
- throw error;
991
- }
992
- finally {
993
- cleanup();
994
- void reader.cancel().catch(() => undefined);
995
- try {
996
- reader.releaseLock();
1206
+ finally {
1207
+ cleanup();
1208
+ void reader.cancel().catch(() => undefined);
1209
+ try {
1210
+ reader.releaseLock();
1211
+ }
1212
+ catch { }
997
1213
  }
998
- catch { }
999
- }
1214
+ });
1000
1215
  },
1001
1216
  };
1002
1217
  //# sourceMappingURL=meta.js.map