mixdog 0.7.18 → 0.8.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 (844) hide show
  1. package/README.md +37 -331
  2. package/package.json +67 -99
  3. package/scripts/boot-smoke.mjs +94 -0
  4. package/scripts/build-tui.mjs +52 -0
  5. package/scripts/compact-smoke.mjs +199 -0
  6. package/scripts/lead-workflow-smoke.mjs +598 -0
  7. package/scripts/live-worker-smoke.mjs +239 -0
  8. package/scripts/output-style-smoke.mjs +101 -0
  9. package/scripts/smoke-loop-report.mjs +221 -0
  10. package/scripts/smoke-loop.mjs +201 -0
  11. package/scripts/smoke.mjs +113 -0
  12. package/scripts/tool-failures.mjs +143 -0
  13. package/scripts/tool-smoke.mjs +456 -0
  14. package/src/agents/debugger/AGENT.md +3 -0
  15. package/src/agents/debugger/agent.json +6 -0
  16. package/src/agents/explore/AGENT.md +4 -0
  17. package/src/agents/explore/agent.json +6 -0
  18. package/src/agents/heavy-worker/AGENT.md +3 -0
  19. package/src/agents/heavy-worker/agent.json +6 -0
  20. package/src/agents/maintainer/AGENT.md +3 -0
  21. package/src/agents/maintainer/agent.json +6 -0
  22. package/src/agents/reviewer/AGENT.md +3 -0
  23. package/src/agents/reviewer/agent.json +6 -0
  24. package/src/agents/scheduler-task.md +3 -0
  25. package/src/agents/web-researcher/AGENT.md +3 -0
  26. package/src/agents/web-researcher/agent.json +6 -0
  27. package/src/agents/webhook-handler.md +3 -0
  28. package/src/agents/worker/AGENT.md +3 -0
  29. package/src/agents/worker/agent.json +6 -0
  30. package/src/app.mjs +90 -0
  31. package/src/cli.mjs +11 -0
  32. package/src/defaults/hidden-roles.json +72 -0
  33. package/src/defaults/mixdog-config.template.json +15 -0
  34. package/src/hooks/lib/permission-evaluator.cjs +488 -0
  35. package/src/hooks/lib/settings-loader.cjs +112 -0
  36. package/src/lib/keychain-cjs.cjs +332 -0
  37. package/src/lib/plugin-paths.cjs +28 -0
  38. package/src/lib/rules-builder.cjs +315 -0
  39. package/src/mixdog-session-runtime.mjs +3704 -0
  40. package/src/output-styles/default.md +38 -0
  41. package/src/output-styles/extreme-simple.md +17 -0
  42. package/src/output-styles/simple.md +17 -0
  43. package/src/repl.mjs +322 -0
  44. package/src/rules/bridge/00-common.md +5 -0
  45. package/src/rules/bridge/20-skip-protocol.md +11 -0
  46. package/src/rules/bridge/30-explorer.md +4 -0
  47. package/src/rules/bridge/40-cycle1-agent.md +28 -0
  48. package/src/rules/bridge/41-cycle2-agent.md +59 -0
  49. package/src/rules/lead/00-tool-lead.md +5 -0
  50. package/src/rules/lead/01-general.md +5 -0
  51. package/src/rules/lead/02-channels.md +3 -0
  52. package/src/rules/lead/04-workflow.md +12 -0
  53. package/src/rules/shared/00-language.md +3 -0
  54. package/src/rules/shared/01-tool.md +3 -0
  55. package/src/runtime/agent/orchestrator/bridge-trace.mjs +814 -0
  56. package/src/runtime/agent/orchestrator/cache-mtime.mjs +60 -0
  57. package/src/runtime/agent/orchestrator/config.mjs +446 -0
  58. package/src/runtime/agent/orchestrator/context/collect.mjs +796 -0
  59. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +417 -0
  60. package/src/runtime/agent/orchestrator/internal-roles.mjs +188 -0
  61. package/src/runtime/agent/orchestrator/internal-tools.mjs +88 -0
  62. package/src/runtime/agent/orchestrator/mcp/client.mjs +345 -0
  63. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2104 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +784 -0
  65. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +341 -0
  66. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1679 -0
  67. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +959 -0
  68. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +213 -0
  69. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +38 -0
  70. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +471 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +615 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +808 -0
  73. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +1719 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2587 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1953 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +136 -0
  77. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +317 -0
  78. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +109 -0
  79. package/src/runtime/agent/orchestrator/providers/registry.mjs +247 -0
  80. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +332 -0
  81. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +11 -0
  82. package/src/runtime/agent/orchestrator/providers/trace-utils.mjs +50 -0
  83. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  84. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +318 -0
  85. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +367 -0
  86. package/src/runtime/agent/orchestrator/session/compact.mjs +882 -0
  87. package/src/runtime/agent/orchestrator/session/context-utils.mjs +233 -0
  88. package/src/runtime/agent/orchestrator/session/loop.mjs +2320 -0
  89. package/src/runtime/agent/orchestrator/session/manager.mjs +2960 -0
  90. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  91. package/src/runtime/agent/orchestrator/session/store.mjs +663 -0
  92. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  93. package/src/runtime/agent/orchestrator/smart-bridge/bridge-llm.mjs +339 -0
  94. package/src/runtime/agent/orchestrator/smart-bridge/cache-strategy.mjs +419 -0
  95. package/src/runtime/agent/orchestrator/stall-policy.mjs +227 -0
  96. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +235 -0
  97. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +723 -0
  98. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +389 -0
  99. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +637 -0
  100. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +165 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +104 -0
  102. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +194 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +596 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +110 -0
  105. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +153 -0
  106. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  107. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +189 -0
  108. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +731 -0
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +168 -0
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +465 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +160 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +982 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1087 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +231 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +223 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin.mjs +478 -0
  118. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  119. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4102 -0
  120. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  121. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +154 -0
  122. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +26 -0
  123. package/src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs +143 -0
  124. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +18 -0
  125. package/src/runtime/agent/orchestrator/tools/patch.mjs +2772 -0
  126. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +114 -0
  127. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +880 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +312 -0
  129. package/src/runtime/channels/backends/discord.mjs +781 -0
  130. package/src/runtime/channels/data/voice-runtime-manifest.json +138 -0
  131. package/src/runtime/channels/index.mjs +3309 -0
  132. package/src/runtime/channels/lib/config.mjs +285 -0
  133. package/src/runtime/channels/lib/drop-trace.mjs +71 -0
  134. package/src/runtime/channels/lib/event-pipeline.mjs +81 -0
  135. package/src/runtime/channels/lib/holidays.mjs +138 -0
  136. package/src/runtime/channels/lib/hook-pipe-server.mjs +671 -0
  137. package/src/runtime/channels/lib/output-forwarder.mjs +765 -0
  138. package/src/runtime/channels/lib/runtime-paths.mjs +497 -0
  139. package/src/runtime/channels/lib/scheduler.mjs +710 -0
  140. package/src/runtime/channels/lib/session-discovery.mjs +102 -0
  141. package/src/runtime/channels/lib/state-file.mjs +68 -0
  142. package/src/runtime/channels/lib/status-snapshot.mjs +224 -0
  143. package/src/runtime/channels/lib/tool-format.mjs +124 -0
  144. package/src/runtime/channels/lib/transcript-discovery.mjs +195 -0
  145. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +734 -0
  146. package/src/runtime/channels/lib/webhook.mjs +1288 -0
  147. package/src/runtime/channels/tool-defs.mjs +177 -0
  148. package/src/runtime/lib/keychain-cjs.cjs +289 -0
  149. package/src/runtime/memory/data/runtime-manifest.json +40 -0
  150. package/src/runtime/memory/index.mjs +3600 -0
  151. package/src/runtime/memory/lib/core-memory-store.mjs +336 -0
  152. package/src/runtime/memory/lib/embedding-provider.mjs +275 -0
  153. package/src/runtime/memory/lib/embedding-worker.mjs +331 -0
  154. package/src/runtime/memory/lib/memory-cycle-requests.mjs +276 -0
  155. package/src/runtime/memory/lib/memory-cycle1.mjs +783 -0
  156. package/src/runtime/memory/lib/memory-cycle2.mjs +1389 -0
  157. package/src/runtime/memory/lib/memory-cycle3.mjs +646 -0
  158. package/src/runtime/memory/lib/memory-embed.mjs +300 -0
  159. package/src/runtime/memory/lib/memory-ops-policy.mjs +149 -0
  160. package/src/runtime/memory/lib/memory-recall-store.mjs +644 -0
  161. package/src/runtime/memory/lib/memory.mjs +418 -0
  162. package/src/runtime/memory/lib/pg/adapter.mjs +314 -0
  163. package/src/runtime/memory/lib/pg/process.mjs +366 -0
  164. package/src/runtime/memory/lib/pg/supervisor.mjs +495 -0
  165. package/src/runtime/memory/lib/runtime-fetcher.mjs +464 -0
  166. package/src/runtime/memory/lib/trace-store.mjs +734 -0
  167. package/src/runtime/memory/tool-defs.mjs +79 -0
  168. package/src/runtime/search/index.mjs +925 -0
  169. package/src/runtime/search/lib/config.mjs +61 -0
  170. package/src/runtime/search/lib/web-tools.mjs +1278 -0
  171. package/src/runtime/search/tool-defs.mjs +64 -0
  172. package/src/runtime/shared/atomic-file.mjs +435 -0
  173. package/src/runtime/shared/background-tasks.mjs +376 -0
  174. package/src/runtime/shared/child-guardian.mjs +98 -0
  175. package/src/runtime/shared/config.mjs +393 -0
  176. package/src/runtime/shared/err-text.mjs +121 -0
  177. package/src/runtime/shared/launcher-control.mjs +259 -0
  178. package/src/runtime/shared/llm/http-agent.mjs +129 -0
  179. package/src/runtime/shared/open-url.mjs +37 -0
  180. package/src/runtime/shared/plugin-paths.mjs +25 -0
  181. package/src/runtime/shared/process-shutdown.mjs +147 -0
  182. package/src/runtime/shared/schedules-store.mjs +70 -0
  183. package/src/runtime/shared/tool-execution-contract.mjs +104 -0
  184. package/src/runtime/shared/tool-surface.mjs +950 -0
  185. package/src/runtime/shared/user-cwd.mjs +221 -0
  186. package/src/runtime/shared/user-data-guard.mjs +232 -0
  187. package/src/runtime/shared/workspace-router.mjs +259 -0
  188. package/src/standalone/bridge-tool.mjs +1414 -0
  189. package/src/standalone/channel-admin.mjs +366 -0
  190. package/src/standalone/channel-worker-preload.cjs +3 -0
  191. package/src/standalone/channel-worker.mjs +353 -0
  192. package/src/standalone/explore-tool.mjs +233 -0
  193. package/src/standalone/hook-bus.mjs +246 -0
  194. package/src/standalone/plugin-admin.mjs +247 -0
  195. package/src/standalone/provider-admin.mjs +338 -0
  196. package/src/standalone/seeds.mjs +94 -0
  197. package/src/standalone/usage-dashboard.mjs +510 -0
  198. package/src/tui/App.jsx +5438 -0
  199. package/src/tui/components/AnsiText.jsx +199 -0
  200. package/src/tui/components/ContextPanel.jsx +217 -0
  201. package/src/tui/components/Markdown.jsx +205 -0
  202. package/src/tui/components/MarkdownTable.jsx +204 -0
  203. package/src/tui/components/Message.jsx +103 -0
  204. package/src/tui/components/Picker.jsx +317 -0
  205. package/src/tui/components/PromptInput.jsx +584 -0
  206. package/src/tui/components/QueuedCommands.jsx +47 -0
  207. package/src/tui/components/SlashCommandPalette.jsx +114 -0
  208. package/src/tui/components/Spinner.jsx +317 -0
  209. package/src/tui/components/StatusLine.jsx +87 -0
  210. package/src/tui/components/TextEntryPanel.jsx +323 -0
  211. package/src/tui/components/ToolExecution.jsx +772 -0
  212. package/src/tui/components/TurnDone.jsx +78 -0
  213. package/src/tui/components/UsagePanel.jsx +331 -0
  214. package/src/tui/dist/index.mjs +12359 -0
  215. package/src/tui/engine.mjs +2410 -0
  216. package/src/tui/figures.mjs +50 -0
  217. package/src/tui/hooks/useEngine.mjs +16 -0
  218. package/src/tui/index.jsx +254 -0
  219. package/src/tui/input-editing.mjs +242 -0
  220. package/src/tui/markdown/format-token.mjs +194 -0
  221. package/src/tui/paste-attachments.mjs +198 -0
  222. package/src/tui/runtime/shared/process-shutdown.mjs +1 -0
  223. package/src/tui/spinner-verbs.mjs +45 -0
  224. package/src/tui/theme.mjs +67 -0
  225. package/src/tui/time-format.mjs +53 -0
  226. package/src/ui/ansi.mjs +115 -0
  227. package/src/ui/markdown.mjs +195 -0
  228. package/src/ui/statusline.mjs +730 -0
  229. package/src/ui/tool-card.mjs +101 -0
  230. package/src/vendor/statusline/bin/statusline-lib.mjs +805 -0
  231. package/src/vendor/statusline/bin/statusline-route.mjs +596 -0
  232. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +285 -0
  233. package/src/vendor/statusline/src/gateway/claude-current.mjs +320 -0
  234. package/src/vendor/statusline/src/gateway/route-meta.mjs +753 -0
  235. package/src/vendor/statusline/src/gateway/session-routes.mjs +244 -0
  236. package/src/workflows/default/WORKFLOW.md +7 -0
  237. package/src/workflows/default/workflow.json +14 -0
  238. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  239. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  240. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  241. package/vendor/ink/build/colorize.d.ts +3 -0
  242. package/vendor/ink/build/colorize.js +48 -0
  243. package/vendor/ink/build/colorize.js.map +1 -0
  244. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  245. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  246. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  247. package/vendor/ink/build/components/AnimationContext.d.ts +9 -0
  248. package/vendor/ink/build/components/AnimationContext.js +13 -0
  249. package/vendor/ink/build/components/AnimationContext.js.map +1 -0
  250. package/vendor/ink/build/components/App.d.ts +24 -0
  251. package/vendor/ink/build/components/App.js +554 -0
  252. package/vendor/ink/build/components/App.js.map +1 -0
  253. package/vendor/ink/build/components/AppContext.d.ts +80 -0
  254. package/vendor/ink/build/components/AppContext.js +25 -0
  255. package/vendor/ink/build/components/AppContext.js.map +1 -0
  256. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  257. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  258. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  259. package/vendor/ink/build/components/Box.d.ts +130 -0
  260. package/vendor/ink/build/components/Box.js +34 -0
  261. package/vendor/ink/build/components/Box.js.map +1 -0
  262. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  263. package/vendor/ink/build/components/CursorContext.js +8 -0
  264. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  265. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  266. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  267. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  268. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  269. package/vendor/ink/build/components/ErrorOverview.js +90 -0
  270. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  271. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  272. package/vendor/ink/build/components/FocusContext.js +17 -0
  273. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  274. package/vendor/ink/build/components/Newline.d.ts +13 -0
  275. package/vendor/ink/build/components/Newline.js +8 -0
  276. package/vendor/ink/build/components/Newline.js.map +1 -0
  277. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  278. package/vendor/ink/build/components/Spacer.js +11 -0
  279. package/vendor/ink/build/components/Spacer.js.map +1 -0
  280. package/vendor/ink/build/components/Static.d.ts +24 -0
  281. package/vendor/ink/build/components/Static.js +28 -0
  282. package/vendor/ink/build/components/Static.js.map +1 -0
  283. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  284. package/vendor/ink/build/components/StderrContext.js +13 -0
  285. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  286. package/vendor/ink/build/components/StdinContext.d.ts +28 -0
  287. package/vendor/ink/build/components/StdinContext.js +20 -0
  288. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  289. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  290. package/vendor/ink/build/components/StdoutContext.js +13 -0
  291. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  292. package/vendor/ink/build/components/Text.d.ts +55 -0
  293. package/vendor/ink/build/components/Text.js +50 -0
  294. package/vendor/ink/build/components/Text.js.map +1 -0
  295. package/vendor/ink/build/components/Transform.d.ts +16 -0
  296. package/vendor/ink/build/components/Transform.js +15 -0
  297. package/vendor/ink/build/components/Transform.js.map +1 -0
  298. package/vendor/ink/build/cursor-helpers.d.ts +39 -0
  299. package/vendor/ink/build/cursor-helpers.js +62 -0
  300. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  301. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  302. package/vendor/ink/build/devtools-window-polyfill.js +68 -0
  303. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  304. package/vendor/ink/build/devtools.d.ts +1 -0
  305. package/vendor/ink/build/devtools.js +36 -0
  306. package/vendor/ink/build/devtools.js.map +1 -0
  307. package/vendor/ink/build/dom.d.ts +62 -0
  308. package/vendor/ink/build/dom.js +143 -0
  309. package/vendor/ink/build/dom.js.map +1 -0
  310. package/vendor/ink/build/get-max-width.d.ts +3 -0
  311. package/vendor/ink/build/get-max-width.js +10 -0
  312. package/vendor/ink/build/get-max-width.js.map +1 -0
  313. package/vendor/ink/build/hooks/use-animation.d.ts +49 -0
  314. package/vendor/ink/build/hooks/use-animation.js +87 -0
  315. package/vendor/ink/build/hooks/use-animation.js.map +1 -0
  316. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  317. package/vendor/ink/build/hooks/use-app.js +8 -0
  318. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  319. package/vendor/ink/build/hooks/use-box-metrics.d.ts +59 -0
  320. package/vendor/ink/build/hooks/use-box-metrics.js +81 -0
  321. package/vendor/ink/build/hooks/use-box-metrics.js.map +1 -0
  322. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  323. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  324. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  325. package/vendor/ink/build/hooks/use-focus-manager.d.ts +43 -0
  326. package/vendor/ink/build/hooks/use-focus-manager.js +18 -0
  327. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  328. package/vendor/ink/build/hooks/use-focus.d.ts +30 -0
  329. package/vendor/ink/build/hooks/use-focus.js +43 -0
  330. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  331. package/vendor/ink/build/hooks/use-input.d.ts +132 -0
  332. package/vendor/ink/build/hooks/use-input.js +126 -0
  333. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  334. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +6 -0
  335. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +12 -0
  336. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  337. package/vendor/ink/build/hooks/use-paste.d.ts +35 -0
  338. package/vendor/ink/build/hooks/use-paste.js +62 -0
  339. package/vendor/ink/build/hooks/use-paste.js.map +1 -0
  340. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  341. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  342. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  343. package/vendor/ink/build/hooks/use-stdin.d.ts +7 -0
  344. package/vendor/ink/build/hooks/use-stdin.js +9 -0
  345. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  346. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  347. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  348. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  349. package/vendor/ink/build/hooks/use-window-size.d.ts +18 -0
  350. package/vendor/ink/build/hooks/use-window-size.js +22 -0
  351. package/vendor/ink/build/hooks/use-window-size.js.map +1 -0
  352. package/vendor/ink/build/index.d.ts +42 -0
  353. package/vendor/ink/build/index.js +24 -0
  354. package/vendor/ink/build/index.js.map +1 -0
  355. package/vendor/ink/build/ink.d.ts +146 -0
  356. package/vendor/ink/build/ink.js +1022 -0
  357. package/vendor/ink/build/ink.js.map +1 -0
  358. package/vendor/ink/build/input-parser.d.ts +10 -0
  359. package/vendor/ink/build/input-parser.js +194 -0
  360. package/vendor/ink/build/input-parser.js.map +1 -0
  361. package/vendor/ink/build/instances.d.ts +3 -0
  362. package/vendor/ink/build/instances.js +8 -0
  363. package/vendor/ink/build/instances.js.map +1 -0
  364. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  365. package/vendor/ink/build/kitty-keyboard.js +32 -0
  366. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  367. package/vendor/ink/build/log-update.d.ts +20 -0
  368. package/vendor/ink/build/log-update.js +261 -0
  369. package/vendor/ink/build/log-update.js.map +1 -0
  370. package/vendor/ink/build/measure-element.d.ts +20 -0
  371. package/vendor/ink/build/measure-element.js +13 -0
  372. package/vendor/ink/build/measure-element.js.map +1 -0
  373. package/vendor/ink/build/measure-text.d.ts +6 -0
  374. package/vendor/ink/build/measure-text.js +21 -0
  375. package/vendor/ink/build/measure-text.js.map +1 -0
  376. package/vendor/ink/build/output.d.ts +35 -0
  377. package/vendor/ink/build/output.js +328 -0
  378. package/vendor/ink/build/output.js.map +1 -0
  379. package/vendor/ink/build/parse-keypress.d.ts +20 -0
  380. package/vendor/ink/build/parse-keypress.js +495 -0
  381. package/vendor/ink/build/parse-keypress.js.map +1 -0
  382. package/vendor/ink/build/reconciler.d.ts +4 -0
  383. package/vendor/ink/build/reconciler.js +306 -0
  384. package/vendor/ink/build/reconciler.js.map +1 -0
  385. package/vendor/ink/build/render-background.d.ts +4 -0
  386. package/vendor/ink/build/render-background.js +25 -0
  387. package/vendor/ink/build/render-background.js.map +1 -0
  388. package/vendor/ink/build/render-border.d.ts +4 -0
  389. package/vendor/ink/build/render-border.js +84 -0
  390. package/vendor/ink/build/render-border.js.map +1 -0
  391. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  392. package/vendor/ink/build/render-node-to-output.js +162 -0
  393. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  394. package/vendor/ink/build/render-to-string.d.ts +38 -0
  395. package/vendor/ink/build/render-to-string.js +116 -0
  396. package/vendor/ink/build/render-to-string.js.map +1 -0
  397. package/vendor/ink/build/render.d.ts +176 -0
  398. package/vendor/ink/build/render.js +71 -0
  399. package/vendor/ink/build/render.js.map +1 -0
  400. package/vendor/ink/build/renderer.d.ts +8 -0
  401. package/vendor/ink/build/renderer.js +64 -0
  402. package/vendor/ink/build/renderer.js.map +1 -0
  403. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  404. package/vendor/ink/build/sanitize-ansi.js +27 -0
  405. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  406. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  407. package/vendor/ink/build/squash-text-nodes.js +36 -0
  408. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  409. package/vendor/ink/build/styles.d.ts +302 -0
  410. package/vendor/ink/build/styles.js +303 -0
  411. package/vendor/ink/build/styles.js.map +1 -0
  412. package/vendor/ink/build/utils.d.ts +9 -0
  413. package/vendor/ink/build/utils.js +19 -0
  414. package/vendor/ink/build/utils.js.map +1 -0
  415. package/vendor/ink/build/wrap-text.d.ts +3 -0
  416. package/vendor/ink/build/wrap-text.js +38 -0
  417. package/vendor/ink/build/wrap-text.js.map +1 -0
  418. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  419. package/vendor/ink/build/write-synchronized.js +9 -0
  420. package/vendor/ink/build/write-synchronized.js.map +1 -0
  421. package/vendor/ink/license +10 -0
  422. package/vendor/ink/package.json +137 -0
  423. package/.claude-plugin/marketplace.json +0 -34
  424. package/.claude-plugin/plugin.json +0 -20
  425. package/.gitattributes +0 -34
  426. package/.mcp.json +0 -14
  427. package/ARCHITECTURE.md +0 -77
  428. package/CHANGELOG.md +0 -30
  429. package/CONTRIBUTING.md +0 -45
  430. package/DATA-FLOW.md +0 -79
  431. package/LICENSE +0 -21
  432. package/SECURITY.md +0 -138
  433. package/UNINSTALL.md +0 -115
  434. package/agents/maintenance.md +0 -5
  435. package/agents/memory-classification.md +0 -30
  436. package/agents/scheduler-task.md +0 -18
  437. package/agents/webhook-handler.md +0 -27
  438. package/agents/worker.md +0 -24
  439. package/bin/bridge +0 -133
  440. package/bin/statusline-launcher.mjs +0 -82
  441. package/bin/statusline-lib.mjs +0 -581
  442. package/bin/statusline-route.mjs +0 -273
  443. package/bin/statusline.mjs +0 -638
  444. package/bun.lock +0 -927
  445. package/commands/config.md +0 -16
  446. package/commands/doctor.md +0 -13
  447. package/commands/model.md +0 -61
  448. package/commands/setup.md +0 -17
  449. package/defaults/hidden-roles.json +0 -68
  450. package/defaults/memory-chunk-prompt.md +0 -63
  451. package/defaults/mixdog-config.template.json +0 -27
  452. package/defaults/user-workflow.json +0 -8
  453. package/defaults/user-workflow.md +0 -17
  454. package/hooks/hooks.json +0 -73
  455. package/hooks/lib/active-instance.cjs +0 -77
  456. package/hooks/lib/permission-evaluator.cjs +0 -411
  457. package/hooks/lib/permission-route.cjs +0 -63
  458. package/hooks/lib/settings-loader.cjs +0 -117
  459. package/hooks/post-tool-use.cjs +0 -84
  460. package/hooks/pre-mcp-sandbox.cjs +0 -158
  461. package/hooks/pre-tool-subagent.cjs +0 -258
  462. package/hooks/session-start.cjs +0 -1493
  463. package/hooks/shim-launcher.cjs +0 -65
  464. package/hooks/turn-timer.cjs +0 -82
  465. package/lib/claude-md-writer.cjs +0 -386
  466. package/lib/keychain-cjs.cjs +0 -290
  467. package/lib/plugin-paths.cjs +0 -69
  468. package/lib/rules-builder.cjs +0 -241
  469. package/native/README.md +0 -117
  470. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  471. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  472. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  473. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  474. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  475. package/prompts/code-review.txt +0 -16
  476. package/prompts/security-audit.txt +0 -17
  477. package/rules/bridge/00-common.md +0 -39
  478. package/rules/bridge/20-skip-protocol.md +0 -18
  479. package/rules/bridge/30-explorer.md +0 -33
  480. package/rules/bridge/40-cycle1-agent.md +0 -52
  481. package/rules/bridge/41-cycle2-agent.md +0 -62
  482. package/rules/lead/00-tool-lead.md +0 -61
  483. package/rules/lead/01-general.md +0 -26
  484. package/rules/lead/02-channels.md +0 -49
  485. package/rules/lead/03-team.md +0 -27
  486. package/rules/lead/04-workflow.md +0 -20
  487. package/rules/shared/00-language.md +0 -14
  488. package/rules/shared/01-tool.md +0 -138
  489. package/scripts/bootstrap.mjs +0 -130
  490. package/scripts/bridge-unify-smoke.mjs +0 -308
  491. package/scripts/build-runtime-linux.sh +0 -348
  492. package/scripts/build-runtime-macos.sh +0 -217
  493. package/scripts/build-runtime-windows.ps1 +0 -242
  494. package/scripts/builtin-utils-smoke.mjs +0 -398
  495. package/scripts/bump.mjs +0 -80
  496. package/scripts/check-json.mjs +0 -45
  497. package/scripts/check-syntax-changed.mjs +0 -102
  498. package/scripts/check-syntax.mjs +0 -58
  499. package/scripts/code-graph-batch.test.mjs +0 -33
  500. package/scripts/config-preserve-smoke.mjs +0 -180
  501. package/scripts/doctor.mjs +0 -489
  502. package/scripts/edit-normalize-fuzz.mjs +0 -130
  503. package/scripts/edit-normalize-smoke.mjs +0 -401
  504. package/scripts/edit-operation-smoke.mjs +0 -369
  505. package/scripts/edit2-smoke.mjs +0 -63
  506. package/scripts/ensure-deps.mjs +0 -259
  507. package/scripts/fuzzy-e2e.mjs +0 -28
  508. package/scripts/fuzzy-smoke.mjs +0 -26
  509. package/scripts/gateway-model.mjs +0 -596
  510. package/scripts/generate-runtime-manifest.mjs +0 -166
  511. package/scripts/guard-smoke.mjs +0 -66
  512. package/scripts/hidden-role-schema-smoke.mjs +0 -162
  513. package/scripts/hook-routing-smoke.mjs +0 -29
  514. package/scripts/inject-input.ps1 +0 -204
  515. package/scripts/io-complex-smoke.mjs +0 -667
  516. package/scripts/io-explore-bench.mjs +0 -424
  517. package/scripts/io-guardrails-smoke.mjs +0 -205
  518. package/scripts/io-mini-bench-baseline.json +0 -11
  519. package/scripts/io-mini-bench.mjs +0 -216
  520. package/scripts/io-route-harness.mjs +0 -933
  521. package/scripts/io-telemetry-report.mjs +0 -691
  522. package/scripts/lib/gateway-inventory.mjs +0 -178
  523. package/scripts/lib/gateway-settings.mjs +0 -78
  524. package/scripts/mutation-bench.mjs +0 -564
  525. package/scripts/mutation-io-smoke.mjs +0 -1097
  526. package/scripts/native-patch-bridge-smoke.mjs +0 -288
  527. package/scripts/native-patch-smoke.mjs +0 -304
  528. package/scripts/openai-oauth-catalog-smoke.mjs +0 -53
  529. package/scripts/patch-interior-context-smoke.mjs +0 -49
  530. package/scripts/patch-newline-utf8-smoke.mjs +0 -157
  531. package/scripts/perf-hook-smoke.mjs +0 -71
  532. package/scripts/permission-eval-smoke.mjs +0 -443
  533. package/scripts/prep-patch.mjs +0 -53
  534. package/scripts/prep-shim.mjs +0 -96
  535. package/scripts/provider-cache-smoke.mjs +0 -687
  536. package/scripts/report-runtime-health.mjs +0 -132
  537. package/scripts/resolve-bun.mjs +0 -60
  538. package/scripts/run-mcp.mjs +0 -1473
  539. package/scripts/salvage-v4a-shatter.test.mjs +0 -58
  540. package/scripts/scoped-cache-io-smoke.mjs +0 -103
  541. package/scripts/shell-policy-round3-smoke.mjs +0 -46
  542. package/scripts/smoke-runtime-negative.ps1 +0 -100
  543. package/scripts/smoke-runtime-negative.sh +0 -95
  544. package/scripts/stall-policy-smoke.mjs +0 -50
  545. package/scripts/start-memory-worker.mjs +0 -23
  546. package/scripts/statusline-launcher-smoke.mjs +0 -235
  547. package/scripts/stress-atomic-write.mjs +0 -1028
  548. package/scripts/test-fault-inject.mjs +0 -164
  549. package/scripts/test-large-file.mjs +0 -174
  550. package/scripts/tool-edge-smoke.mjs +0 -209
  551. package/scripts/uninstall.mjs +0 -238
  552. package/scripts/webhook-selfheal-smoke.mjs +0 -27
  553. package/scripts/write-overwrite-guard-smoke.mjs +0 -56
  554. package/server-main.mjs +0 -3350
  555. package/server.mjs +0 -468
  556. package/setup/config-merge.mjs +0 -246
  557. package/setup/install.mjs +0 -574
  558. package/setup/launch-core.mjs +0 -617
  559. package/setup/launch.mjs +0 -101
  560. package/setup/locate-claude.mjs +0 -56
  561. package/setup/mixdog-cli.mjs +0 -122
  562. package/setup/setup-server.mjs +0 -3305
  563. package/setup/setup.html +0 -3740
  564. package/setup/tui.mjs +0 -325
  565. package/skills/retro-skill-proposer/SKILL.md +0 -92
  566. package/skills/schedule-add/SKILL.md +0 -77
  567. package/skills/setup/SKILL.md +0 -356
  568. package/skills/webhook-add/SKILL.md +0 -81
  569. package/src/agent/bridge-stall-watchdog.mjs +0 -337
  570. package/src/agent/index.mjs +0 -2229
  571. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +0 -1010
  572. package/src/agent/orchestrator/bridge-retry.mjs +0 -220
  573. package/src/agent/orchestrator/bridge-trace.mjs +0 -601
  574. package/src/agent/orchestrator/cache-mtime.mjs +0 -58
  575. package/src/agent/orchestrator/config.mjs +0 -405
  576. package/src/agent/orchestrator/context/collect.mjs +0 -651
  577. package/src/agent/orchestrator/dispatch-persist.mjs +0 -549
  578. package/src/agent/orchestrator/drain-registry.mjs +0 -50
  579. package/src/agent/orchestrator/explore-validator.mjs +0 -8
  580. package/src/agent/orchestrator/internal-roles.mjs +0 -118
  581. package/src/agent/orchestrator/internal-tools.mjs +0 -88
  582. package/src/agent/orchestrator/jobs.mjs +0 -116
  583. package/src/agent/orchestrator/mcp/client.mjs +0 -364
  584. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +0 -1884
  585. package/src/agent/orchestrator/providers/anthropic.mjs +0 -598
  586. package/src/agent/orchestrator/providers/gemini.mjs +0 -1530
  587. package/src/agent/orchestrator/providers/grok-oauth.mjs +0 -779
  588. package/src/agent/orchestrator/providers/model-catalog.mjs +0 -374
  589. package/src/agent/orchestrator/providers/openai-compat-stream.mjs +0 -366
  590. package/src/agent/orchestrator/providers/openai-compat.mjs +0 -1511
  591. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -1891
  592. package/src/agent/orchestrator/providers/openai-oauth.mjs +0 -1456
  593. package/src/agent/orchestrator/providers/openai-ws.mjs +0 -127
  594. package/src/agent/orchestrator/providers/registry.mjs +0 -192
  595. package/src/agent/orchestrator/providers/retry-classifier.mjs +0 -325
  596. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +0 -142
  597. package/src/agent/orchestrator/session/cache/read-cache.mjs +0 -319
  598. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +0 -361
  599. package/src/agent/orchestrator/session/loop.mjs +0 -1619
  600. package/src/agent/orchestrator/session/manager.mjs +0 -1991
  601. package/src/agent/orchestrator/session/result-classification.mjs +0 -65
  602. package/src/agent/orchestrator/session/store.mjs +0 -632
  603. package/src/agent/orchestrator/session/stream-watchdog.mjs +0 -130
  604. package/src/agent/orchestrator/session/tool-result-offload.mjs +0 -166
  605. package/src/agent/orchestrator/session/trim.mjs +0 -491
  606. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +0 -115
  607. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +0 -331
  608. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +0 -150
  609. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +0 -228
  610. package/src/agent/orchestrator/smart-bridge/index.mjs +0 -215
  611. package/src/agent/orchestrator/smart-bridge/profiles.mjs +0 -37
  612. package/src/agent/orchestrator/smart-bridge/registry.mjs +0 -348
  613. package/src/agent/orchestrator/stall-policy.mjs +0 -201
  614. package/src/agent/orchestrator/tool-loop-guard.mjs +0 -75
  615. package/src/agent/orchestrator/tools/bash-session.mjs +0 -722
  616. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +0 -511
  617. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -480
  618. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +0 -256
  619. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +0 -58
  620. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +0 -240
  621. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +0 -113
  622. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +0 -74
  623. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +0 -242
  624. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +0 -211
  625. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +0 -1364
  626. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +0 -126
  627. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +0 -141
  628. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +0 -194
  629. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +0 -60
  630. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +0 -168
  631. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +0 -173
  632. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +0 -48
  633. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -99
  634. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +0 -193
  635. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +0 -597
  636. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  637. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +0 -300
  638. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +0 -152
  639. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +0 -118
  640. package/src/agent/orchestrator/tools/builtin/read-open.mjs +0 -190
  641. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +0 -728
  642. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +0 -173
  643. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +0 -602
  644. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +0 -196
  645. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +0 -422
  646. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +0 -158
  647. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +0 -869
  648. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +0 -962
  649. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +0 -223
  650. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +0 -206
  651. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +0 -401
  652. package/src/agent/orchestrator/tools/builtin.mjs +0 -503
  653. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +0 -24
  654. package/src/agent/orchestrator/tools/code-graph.mjs +0 -4095
  655. package/src/agent/orchestrator/tools/cwd-tool.mjs +0 -298
  656. package/src/agent/orchestrator/tools/destructive-warning.mjs +0 -323
  657. package/src/agent/orchestrator/tools/edit-normalize.mjs +0 -603
  658. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +0 -154
  659. package/src/agent/orchestrator/tools/graph-manifest.json +0 -26
  660. package/src/agent/orchestrator/tools/host-input.mjs +0 -204
  661. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +0 -143
  662. package/src/agent/orchestrator/tools/patch-manifest.json +0 -26
  663. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +0 -20
  664. package/src/agent/orchestrator/tools/patch.mjs +0 -2754
  665. package/src/agent/orchestrator/tools/progress-message.mjs +0 -118
  666. package/src/agent/orchestrator/tools/shell-command.mjs +0 -865
  667. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +0 -7
  668. package/src/agent/orchestrator/tools/shell-snapshot.mjs +0 -313
  669. package/src/agent/orchestrator/workflow-store.mjs +0 -93
  670. package/src/agent/tool-defs.mjs +0 -110
  671. package/src/channels/backends/discord.mjs +0 -784
  672. package/src/channels/data/voice-runtime-manifest.json +0 -138
  673. package/src/channels/index.mjs +0 -3268
  674. package/src/channels/lib/config.mjs +0 -292
  675. package/src/channels/lib/drop-trace.mjs +0 -71
  676. package/src/channels/lib/event-pipeline.mjs +0 -81
  677. package/src/channels/lib/holidays.mjs +0 -138
  678. package/src/channels/lib/hook-pipe-server.mjs +0 -822
  679. package/src/channels/lib/output-forwarder.mjs +0 -765
  680. package/src/channels/lib/runtime-paths.mjs +0 -552
  681. package/src/channels/lib/scheduler.mjs +0 -723
  682. package/src/channels/lib/session-discovery.mjs +0 -103
  683. package/src/channels/lib/state-file.mjs +0 -68
  684. package/src/channels/lib/status-snapshot.mjs +0 -219
  685. package/src/channels/lib/tool-format.mjs +0 -140
  686. package/src/channels/lib/transcript-discovery.mjs +0 -195
  687. package/src/channels/lib/voice-runtime-fetcher.mjs +0 -734
  688. package/src/channels/lib/webhook.mjs +0 -1318
  689. package/src/channels/tool-defs.mjs +0 -170
  690. package/src/daemon/host.mjs +0 -118
  691. package/src/daemon/mcp-transport.mjs +0 -47
  692. package/src/daemon/session.mjs +0 -100
  693. package/src/daemon/thin-client.mjs +0 -71
  694. package/src/daemon/transport.mjs +0 -163
  695. package/src/gateway/claude-current.mjs +0 -255
  696. package/src/gateway/oauth-usage.mjs +0 -598
  697. package/src/gateway/route-meta.mjs +0 -629
  698. package/src/gateway/server.mjs +0 -713
  699. package/src/memory/data/runtime-manifest.json +0 -40
  700. package/src/memory/index.mjs +0 -3332
  701. package/src/memory/lib/core-memory-store.mjs +0 -330
  702. package/src/memory/lib/embedding-provider.mjs +0 -269
  703. package/src/memory/lib/embedding-worker.mjs +0 -323
  704. package/src/memory/lib/memory-cycle1.mjs +0 -645
  705. package/src/memory/lib/memory-cycle2.mjs +0 -1284
  706. package/src/memory/lib/memory-cycle3.mjs +0 -540
  707. package/src/memory/lib/memory-embed.mjs +0 -299
  708. package/src/memory/lib/memory-ops-policy.mjs +0 -190
  709. package/src/memory/lib/memory-recall-store.mjs +0 -638
  710. package/src/memory/lib/memory.mjs +0 -412
  711. package/src/memory/lib/pg/adapter.mjs +0 -308
  712. package/src/memory/lib/pg/process.mjs +0 -360
  713. package/src/memory/lib/pg/supervisor.mjs +0 -396
  714. package/src/memory/lib/runtime-fetcher.mjs +0 -458
  715. package/src/memory/lib/trace-store.mjs +0 -728
  716. package/src/memory/tool-defs.mjs +0 -79
  717. package/src/search/index.mjs +0 -1173
  718. package/src/search/lib/backends/anthropic-oauth.mjs +0 -98
  719. package/src/search/lib/backends/exa.mjs +0 -50
  720. package/src/search/lib/backends/firecrawl.mjs +0 -61
  721. package/src/search/lib/backends/gemini-api.mjs +0 -83
  722. package/src/search/lib/backends/grok-oauth.mjs +0 -86
  723. package/src/search/lib/backends/index.mjs +0 -150
  724. package/src/search/lib/backends/openai-api.mjs +0 -144
  725. package/src/search/lib/backends/openai-oauth.mjs +0 -102
  726. package/src/search/lib/backends/openai-web-search.mjs +0 -76
  727. package/src/search/lib/backends/tavily.mjs +0 -55
  728. package/src/search/lib/backends/xai-api.mjs +0 -113
  729. package/src/search/lib/config.mjs +0 -192
  730. package/src/search/lib/provider-usage.mjs +0 -67
  731. package/src/search/lib/providers.mjs +0 -47
  732. package/src/search/lib/search-intent.mjs +0 -109
  733. package/src/search/lib/setup-handler.mjs +0 -261
  734. package/src/search/lib/web-tools.mjs +0 -1219
  735. package/src/search/tool-defs.mjs +0 -83
  736. package/src/setup/defender-exclusion.mjs +0 -183
  737. package/src/shared/atomic-file.mjs +0 -436
  738. package/src/shared/config.mjs +0 -372
  739. package/src/shared/daemon-recycle.mjs +0 -108
  740. package/src/shared/disable-claude-builtins.mjs +0 -91
  741. package/src/shared/err-text.mjs +0 -12
  742. package/src/shared/llm/http-agent.mjs +0 -123
  743. package/src/shared/open-url.mjs +0 -62
  744. package/src/shared/plugin-paths.mjs +0 -58
  745. package/src/shared/schedules-store.mjs +0 -70
  746. package/src/shared/seed.mjs +0 -161
  747. package/src/shared/user-cwd.mjs +0 -225
  748. package/src/shared/user-data-guard.mjs +0 -244
  749. package/src/status/aggregator.mjs +0 -584
  750. package/src/status/server.mjs +0 -413
  751. package/tools.json +0 -1671
  752. /package/{defaults → src/defaults}/cycle3-review-prompt.md +0 -0
  753. /package/{defaults → src/defaults}/memory-promote-prompt.md +0 -0
  754. /package/{hooks → src/hooks}/lib/permission-rules.cjs +0 -0
  755. /package/{lib → src/lib}/config-cjs.cjs +0 -0
  756. /package/{lib → src/lib}/hook-pipe-path.cjs +0 -0
  757. /package/{lib → src/lib}/mixdog-debug.cjs +0 -0
  758. /package/{lib → src/lib}/text-utils.cjs +0 -0
  759. /package/{rules → src/rules}/bridge/42-cycle3-agent.md +0 -0
  760. /package/src/{agent → runtime/agent}/orchestrator/activity-bus.mjs +0 -0
  761. /package/src/{agent → runtime/agent}/orchestrator/providers/anthropic-betas.mjs +0 -0
  762. /package/src/{agent → runtime/agent}/orchestrator/session/abort-lookup.mjs +0 -0
  763. /package/src/{agent → runtime/agent}/orchestrator/session/cache/post-edit-marks.mjs +0 -0
  764. /package/src/{agent → runtime/agent}/orchestrator/session/cache/scoped-cache-outcome.mjs +0 -0
  765. /package/src/{agent → runtime/agent}/orchestrator/session/cache/util.mjs +0 -0
  766. /package/src/{agent → runtime/agent}/orchestrator/session/read-dedup.mjs +0 -0
  767. /package/src/{agent → runtime/agent}/orchestrator/session/save-session-worker.mjs +0 -0
  768. /package/src/{agent → runtime/agent}/orchestrator/smart-bridge/session-builder.mjs +0 -0
  769. /package/src/{agent → runtime/agent}/orchestrator/tools/bash-policy-scan.mjs +0 -0
  770. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/advisory-lock.mjs +0 -0
  771. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/atomic-write.mjs +0 -0
  772. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/binary-file.mjs +0 -0
  773. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cache-layers.mjs +0 -0
  774. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cwd-utils.mjs +0 -0
  775. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/device-paths.mjs +0 -0
  776. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -0
  777. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diff-utils.mjs +0 -0
  778. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/fs-reachability.mjs +0 -0
  779. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/grep-formatting.mjs +0 -0
  780. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/hash-utils.mjs +0 -0
  781. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/list-formatting.mjs +0 -0
  782. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/open-config-tool.mjs +0 -0
  783. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-locks.mjs +0 -0
  784. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-utils.mjs +0 -0
  785. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-args.mjs +0 -0
  786. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-batch.mjs +0 -0
  787. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-constants.mjs +0 -0
  788. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image-resize.mjs +0 -0
  789. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image.mjs +0 -0
  790. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-lines.mjs +0 -0
  791. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-mode-tool.mjs +0 -0
  792. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-range-index.mjs +0 -0
  793. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-ranges.mjs +0 -0
  794. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-special-files.mjs +0 -0
  795. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-tool.mjs +0 -0
  796. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-windows.mjs +0 -0
  797. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-analysis.mjs +0 -0
  798. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-output.mjs +0 -0
  799. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-helpers.mjs +0 -0
  800. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-validation.mjs +0 -0
  801. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/text-stats.mjs +0 -0
  802. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/windows-roots.mjs +0 -0
  803. /package/src/{agent → runtime/agent}/orchestrator/tools/code-graph-prewarm-worker.mjs +0 -0
  804. /package/src/{agent → runtime/agent}/orchestrator/tools/env-scrub.mjs +0 -0
  805. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-content-cache.mjs +0 -0
  806. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-planner.mjs +0 -0
  807. /package/src/{agent → runtime/agent}/orchestrator/tools/next-call-utils.mjs +0 -0
  808. /package/src/{agent → runtime/agent}/orchestrator/tools/result-compression.mjs +0 -0
  809. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-exec-policy.mjs +0 -0
  810. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy-danger-target.mjs +0 -0
  811. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy.mjs +0 -0
  812. /package/src/{channels → runtime/channels}/lib/cli-worker-host.mjs +0 -0
  813. /package/src/{channels → runtime/channels}/lib/config-lock.mjs +0 -0
  814. /package/src/{channels → runtime/channels}/lib/event-queue.mjs +0 -0
  815. /package/src/{channels → runtime/channels}/lib/executor.mjs +0 -0
  816. /package/src/{channels → runtime/channels}/lib/format.mjs +0 -0
  817. /package/src/{channels → runtime/channels}/lib/interaction-workflows.mjs +0 -0
  818. /package/src/{channels → runtime/channels}/lib/memory-client.mjs +0 -0
  819. /package/src/{channels → runtime/channels}/lib/session-control.mjs +0 -0
  820. /package/src/{channels → runtime/channels}/lib/settings.mjs +0 -0
  821. /package/src/{channels → runtime/channels}/lib/whisper-server.mjs +0 -0
  822. /package/src/{memory → runtime/memory}/lib/agent-ipc.mjs +0 -0
  823. /package/src/{memory → runtime/memory}/lib/bridge-trace-queries.mjs +0 -0
  824. /package/src/{memory → runtime/memory}/lib/llm-worker-host.mjs +0 -0
  825. /package/src/{memory → runtime/memory}/lib/memory-cycle.mjs +0 -0
  826. /package/src/{memory → runtime/memory}/lib/memory-extraction.mjs +0 -0
  827. /package/src/{memory → runtime/memory}/lib/memory-maintenance-store.mjs +0 -0
  828. /package/src/{memory → runtime/memory}/lib/memory-recall-id-patch.mjs +0 -0
  829. /package/src/{memory → runtime/memory}/lib/memory-recall-read-query.mjs +0 -0
  830. /package/src/{memory → runtime/memory}/lib/memory-recall-scope-filter.mjs +0 -0
  831. /package/src/{memory → runtime/memory}/lib/memory-retrievers.mjs +0 -0
  832. /package/src/{memory → runtime/memory}/lib/memory-score.mjs +0 -0
  833. /package/src/{memory → runtime/memory}/lib/memory-text-utils.mjs +0 -0
  834. /package/src/{memory → runtime/memory}/lib/model-profile.mjs +0 -0
  835. /package/src/{memory → runtime/memory}/lib/project-id-resolver.mjs +0 -0
  836. /package/src/{search → runtime/search}/lib/cache.mjs +0 -0
  837. /package/src/{search → runtime/search}/lib/formatter.mjs +0 -0
  838. /package/src/{search → runtime/search}/lib/state.mjs +0 -0
  839. /package/src/{shared → runtime/shared}/abort-controller.mjs +0 -0
  840. /package/src/{shared → runtime/shared}/llm/cost.mjs +0 -0
  841. /package/src/{shared → runtime/shared}/llm/index.mjs +0 -0
  842. /package/src/{shared → runtime/shared}/llm/pid-cleanup.mjs +0 -0
  843. /package/src/{shared → runtime/shared}/llm/usage-log.mjs +0 -0
  844. /package/src/{shared → runtime/shared}/wsl.mjs +0 -0
@@ -1,1530 +0,0 @@
1
- import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai';
2
- import { createHash } from 'crypto';
3
- import { readFileSync, existsSync } from 'fs';
4
- import { join } from 'path';
5
- import { loadConfig, getPluginData } from '../config.mjs';
6
- import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
7
- import { withRetry } from './retry-classifier.mjs';
8
- import { traceBridgeUsage, appendBridgeTrace } from '../bridge-trace.mjs';
9
- import {
10
- PROVIDER_FIRST_BYTE_TIMEOUT_MS,
11
- PROVIDER_NONSTREAM_TOTAL_TIMEOUT_MS,
12
- PROVIDER_GENERATE_TOTAL_TIMEOUT_MS,
13
- PROVIDER_MAX_BEFORE_WARN_MS,
14
- PROVIDER_SSE_IDLE_TIMEOUT_MS,
15
- PROVIDER_SSE_IDLE_WATCHDOG_ENABLED,
16
- providerTimeoutError,
17
- resolveTimeoutMs,
18
- createTimeoutSignal,
19
- } from '../stall-policy.mjs';
20
- import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
21
-
22
- const MODELS = [
23
- { id: 'gemini-3-flash-preview', name: 'Gemini 3 Flash Preview', provider: 'gemini', contextWindow: 1048576 },
24
- { id: 'gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro Preview', provider: 'gemini', contextWindow: 1048576 },
25
- { id: 'gemini-3-pro-preview', name: 'Gemini 3 Pro Preview', provider: 'gemini', contextWindow: 1048576 },
26
- { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', provider: 'gemini', contextWindow: 1048576 },
27
- { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', provider: 'gemini', contextWindow: 1048576 },
28
- ];
29
-
30
- const DEFAULT_MODEL = MODELS[0].id;
31
-
32
- // --- Model catalog cache (24h disk TTL) ---
33
- // Gemini's /models has no `created` timestamp, so latest-resolution is
34
- // VERSION-based (parse gemini-X.Y) rather than release-date based.
35
- const MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
36
-
37
- // De-dupes concurrent force-refreshes so they share one HTTP round-trip,
38
- // mirroring anthropic-oauth's _modelRefreshInFlight.
39
- let _modelRefreshInFlight = null;
40
-
41
- function _modelCachePath() {
42
- return join(getPluginData(), 'gemini-models.json');
43
- }
44
-
45
- function _loadModelCache() {
46
- const path = _modelCachePath();
47
- if (!existsSync(path)) return null;
48
- try {
49
- const raw = JSON.parse(readFileSync(path, 'utf-8'));
50
- if (!raw?.fetchedAt || !Array.isArray(raw.models)) return null;
51
- if (Date.now() - raw.fetchedAt > MODEL_CACHE_TTL_MS) return null;
52
- return raw.models;
53
- } catch { return null; }
54
- }
55
-
56
- function _saveModelCache(models) {
57
- try {
58
- writeJsonAtomicSync(_modelCachePath(), { fetchedAt: Date.now(), models }, { lock: true, fsyncDir: true });
59
- } catch { /* best-effort */ }
60
- }
61
-
62
- // Mirror of anthropic-oauth.mjs _compareVersion: compare two gemini ids by the
63
- // X.Y version embedded in the id (gemini-3.5-flash -> [3, 5]). Falls back to a
64
- // lexicographic tiebreak so ordering is total.
65
- function _compareVersion(a, b) {
66
- const na = (a.match(/gemini-(\d+)(?:\.(\d+))?/) || []).slice(1).map(Number);
67
- const nb = (b.match(/gemini-(\d+)(?:\.(\d+))?/) || []).slice(1).map(Number);
68
- for (let i = 0; i < Math.max(na.length, nb.length); i++) {
69
- if ((na[i] || 0) !== (nb[i] || 0)) return (na[i] || 0) - (nb[i] || 0);
70
- }
71
- return a.localeCompare(b);
72
- }
73
-
74
- // Per family, mark the highest-version model as latest:true.
75
- function _markLatestGemini(models) {
76
- const byFamily = new Map();
77
- for (const m of models) {
78
- if (!m?.id) continue;
79
- const cur = byFamily.get(m.family);
80
- if (!cur || _compareVersion(m.id, cur.id) > 0) {
81
- byFamily.set(m.family, m);
82
- }
83
- }
84
- for (const m of byFamily.values()) m.latest = true;
85
- }
86
-
87
- // Newest chat model by VERSION in the 'gemini-flash' family, read from the
88
- // on-disk catalog cache. Returns null until cached; callers warm via
89
- // ensureLatestGeminiModel when null.
90
- export function resolveLatestGeminiModel() {
91
- const cached = _loadModelCache();
92
- if (!Array.isArray(cached)) return null;
93
- let best = null;
94
- for (const m of cached) {
95
- if (!m?.id || m.family !== 'gemini-flash') continue;
96
- if (!best || _compareVersion(m.id, best.id) > 0) best = m;
97
- }
98
- return best?.id || null;
99
- }
100
-
101
- export async function ensureLatestGeminiModel(provider) {
102
- let m = resolveLatestGeminiModel();
103
- if (m) return m;
104
- await provider._refreshModelCache();
105
- m = resolveLatestGeminiModel();
106
- if (m) return m;
107
- throw new Error('[gemini] model catalog unavailable after warmup — cannot resolve default model');
108
- }
109
-
110
- const GEMINI_FIRST_BYTE_TIMEOUT_MS = resolveTimeoutMs(
111
- 'MIXDOG_GEMINI_FIRST_BYTE_TIMEOUT_MS',
112
- PROVIDER_FIRST_BYTE_TIMEOUT_MS,
113
- { minMs: 30_000, maxMs: PROVIDER_MAX_BEFORE_WARN_MS },
114
- );
115
- const GEMINI_GENERATE_TOTAL_TIMEOUT_MS = resolveTimeoutMs(
116
- 'MIXDOG_GEMINI_GENERATE_TOTAL_TIMEOUT_MS',
117
- PROVIDER_GENERATE_TOTAL_TIMEOUT_MS,
118
- { minMs: 30_000, maxMs: PROVIDER_MAX_BEFORE_WARN_MS },
119
- );
120
-
121
- function traceHash(value) {
122
- return createHash('sha256')
123
- .update(String(value ?? ''))
124
- .digest('hex')
125
- .slice(0, 16);
126
- }
127
-
128
- function stableTraceStringify(value, seen = new WeakSet()) {
129
- if (value === null || typeof value !== 'object') {
130
- if (typeof value === 'bigint') return JSON.stringify(String(value));
131
- if (typeof value === 'undefined' || typeof value === 'function') return 'null';
132
- return JSON.stringify(value);
133
- }
134
- if (seen.has(value)) return JSON.stringify('[Circular]');
135
- seen.add(value);
136
- if (Array.isArray(value)) {
137
- const serialized = '[' + value.map(v => stableTraceStringify(v, seen)).join(',') + ']';
138
- seen.delete(value);
139
- return serialized;
140
- }
141
- const parts = [];
142
- for (const key of Object.keys(value).sort()) {
143
- const v = value[key];
144
- if (typeof v === 'undefined' || typeof v === 'function') continue;
145
- parts.push(JSON.stringify(key) + ':' + stableTraceStringify(v, seen));
146
- }
147
- seen.delete(value);
148
- return '{' + parts.join(',') + '}';
149
- }
150
-
151
- function traceTextShape(text) {
152
- const value = String(text ?? '');
153
- return { chars: value.length, hash: traceHash(value) };
154
- }
155
-
156
- function summarizeTracePart(part) {
157
- if (!part || typeof part !== 'object') return { type: typeof part };
158
- if ('text' in part) {
159
- return { type: 'text', ...traceTextShape(part.text) };
160
- }
161
- if (part.functionCall) {
162
- return {
163
- type: 'functionCall',
164
- name: part.functionCall.name || null,
165
- argsHash: traceHash(stableTraceStringify(part.functionCall.args || {})),
166
- };
167
- }
168
- if (part.functionResponse) {
169
- const response = part.functionResponse.response || {};
170
- const responseShape = stableTraceStringify(response);
171
- return {
172
- type: 'functionResponse',
173
- name: part.functionResponse.name || null,
174
- responseChars: responseShape.length,
175
- responseHash: traceHash(responseShape),
176
- };
177
- }
178
- return { type: Object.keys(part).sort().join('|') || 'unknown' };
179
- }
180
-
181
- function summarizeTraceContents(contents) {
182
- const summaries = (contents || []).map((content, index) => ({
183
- index,
184
- role: content?.role || null,
185
- parts: Array.isArray(content?.parts) ? content.parts.map(summarizeTracePart) : [],
186
- }));
187
- if (summaries.length <= 12) return summaries;
188
- return [
189
- ...summaries.slice(0, 8),
190
- { omittedTurns: summaries.length - 12 },
191
- ...summaries.slice(-4),
192
- ];
193
- }
194
-
195
- function summarizeTraceTools(tools) {
196
- return (tools || []).map(t => ({
197
- name: t?.name || null,
198
- description: t?.description || '',
199
- inputSchema: t?.inputSchema || null,
200
- }));
201
- }
202
-
203
- // Gemini cachedContents API rejects prefixes below the model-specific minimum
204
- // (Gemini 2.0/2.5 = 2048 tokens, Gemini 3.x = 4096 tokens) with HTTP 400
205
- // "Cached content is too small". Estimating chars/4 ≈ tokens lets us skip the
206
- // roundtrip when the prefix cannot satisfy the invariant. Estimate undercount
207
- // is harmless (we attempt and get the same 400 we would have skipped); overcount
208
- // is harmless (we skip, fall back to implicit cache).
209
- function _estimateGeminiCacheTokens(systemInstruction, geminiTools, contents) {
210
- let chars = 0;
211
- if (typeof systemInstruction === 'string') chars += systemInstruction.length;
212
- if (Array.isArray(geminiTools) && geminiTools.length) {
213
- try { chars += JSON.stringify(geminiTools).length; } catch {}
214
- }
215
- if (Array.isArray(contents) && contents.length > 1) {
216
- for (let i = 0; i < contents.length - 1; i++) {
217
- try { chars += JSON.stringify(contents[i]?.parts ?? '').length; } catch {}
218
- }
219
- }
220
- return Math.ceil(chars / 4);
221
- }
222
-
223
- function _geminiCacheMinTokens(model) {
224
- return /^gemini-3/i.test(String(model || '')) ? 4096 : 2048;
225
- }
226
-
227
- function _geminiCachePrefixCount(contents) {
228
- return Array.isArray(contents) && contents.length > 1 ? contents.length - 1 : 0;
229
- }
230
-
231
- function _geminiCachePrefixContents(contents, prefixCount) {
232
- if (!Array.isArray(contents) || prefixCount <= 0) return [];
233
- return contents.slice(0, prefixCount).map(c => {
234
- const r = c?.role;
235
- const safeRole = (r === 'model' || r === 'user') ? r : 'user';
236
- return {
237
- role: safeRole,
238
- parts: Array.isArray(c?.parts) ? c.parts : [],
239
- };
240
- });
241
- }
242
-
243
- function _geminiCachePrefixHash({ model, systemInstruction, geminiTools, contents, prefixCount }) {
244
- return traceHash(stableTraceStringify({
245
- model: model || null,
246
- systemInstruction: systemInstruction || '',
247
- tools: geminiTools || [],
248
- contents: _geminiCachePrefixContents(contents, prefixCount),
249
- }));
250
- }
251
-
252
- function _resolveGeminiCacheUsage({ usageMetadata, cachedContent, providerState }) {
253
- const inputTokens = Number(usageMetadata?.promptTokenCount || usageMetadata?.totalTokenCount || 0) || 0;
254
- const reportedCachedTokens = Number(usageMetadata?.cachedContentTokenCount || 0) || 0;
255
- const cachedFallbackTokens = cachedContent
256
- ? Number(providerState?.gemini?.cacheTokenSize || 0) || 0
257
- : 0;
258
- const rawCachedTokens = reportedCachedTokens > 0 ? reportedCachedTokens : cachedFallbackTokens;
259
- const cachedTokens = inputTokens > 0 ? Math.min(rawCachedTokens, inputTokens) : rawCachedTokens;
260
- const cacheTokenSource = reportedCachedTokens > 0
261
- ? 'usage_metadata'
262
- : (cachedFallbackTokens > 0 ? 'cache_create_fallback' : 'none');
263
- return {
264
- inputTokens,
265
- reportedCachedTokens,
266
- cachedFallbackTokens,
267
- cachedTokens,
268
- cacheTokenSource,
269
- };
270
- }
271
-
272
- function writeGeminiCacheTrace({ opts, model, systemInstruction, tools, contents, usageMetadata, cachedContent }) {
273
- if (process.env.MIXDOG_GEMINI_CACHE_TRACE !== '1') return;
274
- try {
275
- const session = opts?.session || {};
276
- const {
277
- inputTokens,
278
- reportedCachedTokens,
279
- cachedFallbackTokens,
280
- cachedTokens,
281
- cacheTokenSource,
282
- } = _resolveGeminiCacheUsage({
283
- usageMetadata,
284
- cachedContent,
285
- providerState: opts?.providerState,
286
- });
287
- const toolShape = summarizeTraceTools(tools);
288
- const trace = {
289
- event: 'generate',
290
- provider: 'gemini',
291
- model,
292
- owner: session.owner || null,
293
- role: session.role || null,
294
- permission: session.permission || null,
295
- toolPermission: session.toolPermission || null,
296
- profileId: session.profileId || null,
297
- sourceType: session.sourceType || null,
298
- sourceName: session.sourceName || null,
299
- sessionIdHash: opts?.sessionId ? traceHash(opts.sessionId) : null,
300
- providerCacheKeyHash: opts?.providerCacheKey ? traceHash(opts.providerCacheKey) : null,
301
- promptCacheKeyHash: opts?.promptCacheKey ? traceHash(opts.promptCacheKey) : null,
302
- systemChars: systemInstruction ? systemInstruction.length : 0,
303
- systemHash: systemInstruction ? traceHash(systemInstruction) : null,
304
- toolCount: Array.isArray(tools) ? tools.length : 0,
305
- toolSchemaHash: traceHash(stableTraceStringify(toolShape)),
306
- contentTurnCount: Array.isArray(contents) ? contents.length : 0,
307
- contents: summarizeTraceContents(contents),
308
- inputTokens,
309
- cachedTokens,
310
- reportedCachedTokens,
311
- cachedFallbackTokens,
312
- cacheTokenSource,
313
- cacheAttached: !!cachedContent,
314
- cachePrefixContentCount: opts?.providerState?.gemini?.cachePrefixContentCount ?? null,
315
- cacheHitRate: inputTokens > 0 ? Number((cachedTokens / inputTokens).toFixed(6)) : null,
316
- };
317
- process.stderr.write(`[gemini-cache-trace] ${JSON.stringify(trace)}\n`);
318
- } catch (err) {
319
- process.stderr.write(`[gemini-cache-trace] failed: ${err?.message || err}\n`);
320
- }
321
- }
322
-
323
- function geminiTimeoutError(label, timeoutMs) {
324
- const err = providerTimeoutError(label, timeoutMs);
325
- err.name = 'GeminiTimeoutError';
326
- err.code = 'EGEMINITIMEOUT';
327
- return err;
328
- }
329
-
330
- function runGeminiOperationWithTimeout({ label, timeoutMs, signal, run }) {
331
- const ac = new AbortController();
332
- let settled = false;
333
- let timer = null;
334
- let abortListener = null;
335
- return new Promise((resolve, reject) => {
336
- const finish = (fn, value) => {
337
- if (settled) return;
338
- settled = true;
339
- if (timer) clearTimeout(timer);
340
- if (abortListener && signal) {
341
- try { signal.removeEventListener('abort', abortListener); } catch {}
342
- }
343
- fn(value);
344
- };
345
- const abort = (reason) => {
346
- try { ac.abort(reason); } catch {}
347
- finish(reject, reason instanceof Error ? reason : new Error(String(reason || `${label} aborted`)));
348
- };
349
- if (signal) {
350
- abortListener = () => abort(signal.reason);
351
- if (signal.aborted) { abortListener(); return; }
352
- signal.addEventListener('abort', abortListener, { once: true });
353
- }
354
- timer = setTimeout(() => {
355
- abort(geminiTimeoutError(label, timeoutMs));
356
- }, timeoutMs);
357
- if (timer.unref) timer.unref();
358
- Promise.resolve()
359
- .then(() => run(ac.signal))
360
- .then((value) => finish(resolve, value), (err) => finish(reject, err));
361
- });
362
- }
363
-
364
- function geminiTruncatedStreamError(message) {
365
- return Object.assign(
366
- new Error(message),
367
- { name: 'TruncatedStreamError', code: 'TRUNCATED_STREAM', truncatedStream: true },
368
- );
369
- }
370
-
371
- /**
372
- * Aggregate streamed GenerateContentResponse chunks into one response object
373
- * (same shape as a non-streaming generateContent JSON body).
374
- * Mirrors @google/generative-ai aggregateResponses().
375
- */
376
- function aggregateGeminiStreamChunks(responses) {
377
- const lastResponse = responses[responses.length - 1];
378
- const aggregatedResponse = {
379
- promptFeedback: lastResponse?.promptFeedback,
380
- };
381
- for (const response of responses) {
382
- if (response?.candidates) {
383
- let candidateIndex = 0;
384
- for (const candidate of response.candidates) {
385
- if (!aggregatedResponse.candidates) aggregatedResponse.candidates = [];
386
- if (!aggregatedResponse.candidates[candidateIndex]) {
387
- aggregatedResponse.candidates[candidateIndex] = { index: candidateIndex };
388
- }
389
- const aggCand = aggregatedResponse.candidates[candidateIndex];
390
- aggCand.citationMetadata = candidate.citationMetadata;
391
- aggCand.groundingMetadata = candidate.groundingMetadata;
392
- aggCand.finishReason = candidate.finishReason;
393
- aggCand.finishMessage = candidate.finishMessage;
394
- aggCand.safetyRatings = candidate.safetyRatings;
395
- if (candidate.content?.parts) {
396
- if (!aggCand.content) {
397
- aggCand.content = {
398
- role: candidate.content.role || 'user',
399
- parts: [],
400
- };
401
- }
402
- for (const part of candidate.content.parts) {
403
- const newPart = {};
404
- if (part.text) newPart.text = part.text;
405
- if (part.functionCall) newPart.functionCall = part.functionCall;
406
- if (part.thoughtSignature) newPart.thoughtSignature = part.thoughtSignature;
407
- if (part.thought_signature) newPart.thought_signature = part.thought_signature;
408
- if (part.executableCode) newPart.executableCode = part.executableCode;
409
- if (part.codeExecutionResult) newPart.codeExecutionResult = part.codeExecutionResult;
410
- if (Object.keys(newPart).length === 0) newPart.text = '';
411
- aggCand.content.parts.push(newPart);
412
- }
413
- }
414
- candidateIndex++;
415
- }
416
- }
417
- if (response?.usageMetadata) aggregatedResponse.usageMetadata = response.usageMetadata;
418
- }
419
- return aggregatedResponse;
420
- }
421
-
422
- function assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label }) {
423
- if (!sawStreamChunk) {
424
- throw geminiTruncatedStreamError(`${label} truncated: empty stream`);
425
- }
426
- if (!finishReason) {
427
- throw geminiTruncatedStreamError(`${label} truncated: no finishReason`);
428
- }
429
- }
430
-
431
- async function consumeGeminiRestStreamResponse(response, { signal, onStreamDelta, label }) {
432
- if (!response?.body) throw new Error(`${label}: missing response body`);
433
- const reader = response.body.getReader();
434
- const decoder = new TextDecoder();
435
- let buffer = '';
436
- const allChunks = [];
437
- let sawStreamChunk = false;
438
- let idleTimedOut = false;
439
- let idleTimer = null;
440
- let idleReject = null;
441
-
442
- let firstByteTimer = setTimeout(() => {
443
- try { reader.cancel('first byte timeout'); } catch {}
444
- if (idleReject) {
445
- const e = geminiTimeoutError(`${label} first byte`, GEMINI_FIRST_BYTE_TIMEOUT_MS);
446
- const r = idleReject; idleReject = null; r(e);
447
- }
448
- }, GEMINI_FIRST_BYTE_TIMEOUT_MS);
449
- if (firstByteTimer.unref) firstByteTimer.unref();
450
-
451
- const clearFirstByteTimer = () => {
452
- if (firstByteTimer) {
453
- clearTimeout(firstByteTimer);
454
- firstByteTimer = null;
455
- }
456
- };
457
-
458
- const resetIdleTimer = () => {
459
- if (!PROVIDER_SSE_IDLE_WATCHDOG_ENABLED) return;
460
- if (idleTimer) clearTimeout(idleTimer);
461
- idleTimer = setTimeout(() => {
462
- idleTimedOut = true;
463
- try { reader.cancel('SSE idle timeout'); } catch {}
464
- if (idleReject) {
465
- const e = geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
466
- const r = idleReject; idleReject = null; r(e);
467
- }
468
- }, PROVIDER_SSE_IDLE_TIMEOUT_MS);
469
- if (idleTimer.unref) idleTimer.unref();
470
- };
471
-
472
- const onAbort = () => {
473
- try {
474
- const c = reader.cancel('aborted');
475
- if (c && typeof c.catch === 'function') c.catch(() => {});
476
- } catch {}
477
- };
478
-
479
- if (signal) {
480
- if (signal.aborted) {
481
- const reason = signal.reason;
482
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
483
- }
484
- signal.addEventListener('abort', onAbort, { once: true });
485
- }
486
-
487
- try {
488
- resetIdleTimer();
489
- while (true) {
490
- let chunk;
491
- try {
492
- chunk = await new Promise((resolve, reject) => {
493
- idleReject = reject;
494
- reader.read().then(resolve, reject);
495
- });
496
- } catch (err) {
497
- if (idleTimedOut) {
498
- throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
499
- }
500
- if (signal?.aborted) {
501
- const reason = signal.reason;
502
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
503
- }
504
- throw err;
505
- } finally {
506
- idleReject = null;
507
- }
508
- const { done, value } = chunk;
509
- if (done) break;
510
- resetIdleTimer();
511
- buffer += decoder.decode(value, { stream: true });
512
- let lineEnd;
513
- while ((lineEnd = buffer.indexOf('\n')) >= 0) {
514
- let line = buffer.slice(0, lineEnd);
515
- buffer = buffer.slice(lineEnd + 1);
516
- line = line.replace(/\r$/, '');
517
- if (!line.startsWith('data: ')) continue;
518
- const data = line.slice(6).trim();
519
- if (!data || data === '[DONE]') continue;
520
- let parsed;
521
- try { parsed = JSON.parse(data); } catch { continue; }
522
- if (!sawStreamChunk) {
523
- sawStreamChunk = true;
524
- clearFirstByteTimer();
525
- }
526
- allChunks.push(parsed);
527
- try { onStreamDelta?.(); } catch {}
528
- }
529
- }
530
- if (buffer.trim()) {
531
- const line = buffer.trim().replace(/\r$/, '');
532
- if (line.startsWith('data: ')) {
533
- const data = line.slice(6).trim();
534
- if (data && data !== '[DONE]') {
535
- try {
536
- const parsed = JSON.parse(data);
537
- if (!sawStreamChunk) {
538
- sawStreamChunk = true;
539
- clearFirstByteTimer();
540
- }
541
- allChunks.push(parsed);
542
- try { onStreamDelta?.(); } catch {}
543
- } catch { /* skip malformed tail */ }
544
- }
545
- }
546
- }
547
- } finally {
548
- clearFirstByteTimer();
549
- if (idleTimer) clearTimeout(idleTimer);
550
- if (signal) signal.removeEventListener('abort', onAbort);
551
- try { reader.releaseLock(); } catch {}
552
- }
553
-
554
- const aggregated = aggregateGeminiStreamChunks(allChunks);
555
- const finishReason = aggregated.candidates?.[0]?.finishReason || null;
556
- assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label });
557
- return aggregated;
558
- }
559
-
560
- async function consumeGeminiSdkStream(streamResult, { signal, onStreamDelta, label }) {
561
- let sawStreamChunk = false;
562
- let idleTimedOut = false;
563
- let idleTimer = null;
564
- let firstByteReject = null;
565
- let firstByteTimer = null;
566
- let inFlightReject = null;
567
-
568
- const armFirstByteTimer = () => {
569
- if (firstByteTimer) clearTimeout(firstByteTimer);
570
- firstByteTimer = setTimeout(() => {
571
- if (firstByteReject) {
572
- const e = geminiTimeoutError(`${label} first byte`, GEMINI_FIRST_BYTE_TIMEOUT_MS);
573
- const r = firstByteReject; firstByteReject = null; r(e);
574
- }
575
- }, GEMINI_FIRST_BYTE_TIMEOUT_MS);
576
- if (firstByteTimer.unref) firstByteTimer.unref();
577
- };
578
-
579
- const clearFirstByteTimer = () => {
580
- if (firstByteTimer) {
581
- clearTimeout(firstByteTimer);
582
- firstByteTimer = null;
583
- }
584
- firstByteReject = null;
585
- };
586
-
587
- const resetIdleTimer = () => {
588
- if (!PROVIDER_SSE_IDLE_WATCHDOG_ENABLED) return;
589
- if (idleTimer) clearTimeout(idleTimer);
590
- idleTimer = setTimeout(() => {
591
- idleTimedOut = true;
592
- if (inFlightReject) {
593
- const e = geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
594
- const r = inFlightReject; inFlightReject = null; r(e);
595
- }
596
- }, PROVIDER_SSE_IDLE_TIMEOUT_MS);
597
- if (idleTimer.unref) idleTimer.unref();
598
- };
599
-
600
- if (signal?.aborted) {
601
- const reason = signal.reason;
602
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
603
- }
604
-
605
- const iterator = streamResult.stream[Symbol.asyncIterator]();
606
-
607
- try {
608
- armFirstByteTimer();
609
- resetIdleTimer();
610
- while (true) {
611
- if (idleTimedOut) {
612
- throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
613
- }
614
- let step;
615
- try {
616
- step = await new Promise((resolve, reject) => {
617
- inFlightReject = reject;
618
- if (!sawStreamChunk) firstByteReject = reject;
619
- iterator.next().then(
620
- (value) => {
621
- inFlightReject = null;
622
- firstByteReject = null;
623
- resolve(value);
624
- },
625
- (err) => {
626
- inFlightReject = null;
627
- firstByteReject = null;
628
- reject(err);
629
- },
630
- );
631
- });
632
- } catch (err) {
633
- if (idleTimedOut) {
634
- throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
635
- }
636
- if (signal?.aborted) {
637
- const reason = signal.reason;
638
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
639
- }
640
- throw err;
641
- }
642
- if (step.done) break;
643
- if (!sawStreamChunk) {
644
- sawStreamChunk = true;
645
- clearFirstByteTimer();
646
- }
647
- resetIdleTimer();
648
- try { onStreamDelta?.(); } catch {}
649
- }
650
- if (idleTimedOut) {
651
- throw geminiTimeoutError(`${label} SSE idle`, PROVIDER_SSE_IDLE_TIMEOUT_MS);
652
- }
653
- } catch (err) {
654
- clearFirstByteTimer();
655
- if (signal?.aborted) {
656
- const reason = signal.reason;
657
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
658
- }
659
- throw err;
660
- } finally {
661
- clearFirstByteTimer();
662
- if (idleTimer) clearTimeout(idleTimer);
663
- }
664
-
665
- let response;
666
- try {
667
- response = await streamResult.response;
668
- } catch (err) {
669
- if (signal?.aborted) {
670
- const reason = signal.reason;
671
- throw reason instanceof Error ? reason : new Error(`${label} aborted`);
672
- }
673
- throw err;
674
- }
675
- const raw = response?.candidates ? response : (response?.response || response);
676
- const finishReason = raw?.candidates?.[0]?.finishReason || null;
677
- assertGeminiStreamCompleted({ sawStreamChunk, finishReason, label });
678
- return raw;
679
- }
680
-
681
- /**
682
- * Convert JSON Schema type string to Gemini SchemaType.
683
- * Gemini SDK uses its own enum instead of plain strings.
684
- */
685
- function toSchemaType(t) {
686
- const map = {
687
- string: SchemaType.STRING,
688
- number: SchemaType.NUMBER,
689
- integer: SchemaType.INTEGER,
690
- boolean: SchemaType.BOOLEAN,
691
- array: SchemaType.ARRAY,
692
- object: SchemaType.OBJECT,
693
- };
694
- return map[t] ?? SchemaType.STRING;
695
- }
696
-
697
- /**
698
- * Recursively convert a JSON Schema object to Gemini's FunctionDeclarationSchema.
699
- * Gemini requires `type` to be a SchemaType enum, not a plain string, and
700
- * rejects several JSON Schema fields the API does not understand
701
- * (additionalProperties, $schema, $ref, const, examples, definitions,
702
- * patternProperties). We strip those at every level.
703
- */
704
- const GEMINI_SCHEMA_STRIP = new Set([
705
- 'additionalProperties',
706
- '$schema',
707
- '$ref',
708
- 'const',
709
- 'examples',
710
- 'definitions',
711
- 'patternProperties',
712
- ]);
713
- function convertSchema(schema) {
714
- if (!schema || typeof schema !== 'object') return schema;
715
- const result = {};
716
- for (const [k, v] of Object.entries(schema)) {
717
- if (GEMINI_SCHEMA_STRIP.has(k)) continue;
718
- result[k] = v;
719
- }
720
- // Gemini's Schema validator requires every `enum` entry to be a string,
721
- // even when the parent `type` is integer/number/boolean. Drop the enum in
722
- // that case rather than emit an invalid typed enum — `type` plus the
723
- // description still guides the model, and the tool handler revalidates.
724
- const rawType = typeof result.type === 'string' ? result.type : undefined;
725
- if (Array.isArray(result.enum) && (rawType === 'integer' || rawType === 'number' || rawType === 'boolean')) {
726
- if (result.enum.some((item) => typeof item !== 'string')) {
727
- delete result.enum;
728
- }
729
- }
730
- // Gemini rejects array schemas that omit `items`; fill a permissive
731
- // default so the declaration validates.
732
- if (rawType === 'array' && (!result.items || typeof result.items !== 'object')) {
733
- result.items = { type: 'string' };
734
- }
735
- if (typeof result.type === 'string') {
736
- result.type = toSchemaType(result.type);
737
- }
738
- if (result.properties && typeof result.properties === 'object') {
739
- const props = {};
740
- for (const [key, val] of Object.entries(result.properties)) {
741
- props[key] = convertSchema(val);
742
- }
743
- result.properties = props;
744
- }
745
- if (result.items && typeof result.items === 'object') {
746
- result.items = convertSchema(result.items);
747
- }
748
- // Recurse into JSON Schema combinator keys so disallowed fields
749
- // (additionalProperties, $schema, etc.) get stripped at every nesting
750
- // level. Without this, schemas using anyOf/oneOf/allOf/not pass the
751
- // shallow strip but fail Gemini validation at depth.
752
- //
753
- // Two Gemini-specific normalizations are also applied per combinator
754
- // subschema:
755
- // 1. Inject `type: OBJECT` when a subschema uses object-only keys
756
- // (`required` / `properties`) without an explicit type — Gemini
757
- // rejects `required` outside of OBJECT type.
758
- // 2. Materialize a local `properties` map from the parent's properties
759
- // when the subschema only carries `required: [names]` — Gemini
760
- // validates that every name in `required` exists in *this*
761
- // subschema's `properties` (it does not inherit from the parent
762
- // the way JSON Schema's compositional model does).
763
- for (const combinator of ['anyOf', 'oneOf', 'allOf']) {
764
- if (Array.isArray(result[combinator])) {
765
- result[combinator] = result[combinator].map((s) => {
766
- const sub = convertSchema(s);
767
- if (sub && typeof sub === 'object') {
768
- const usesObjectKeys = sub.required !== undefined || sub.properties !== undefined;
769
- if (usesObjectKeys && sub.type === undefined) {
770
- sub.type = toSchemaType('object');
771
- }
772
- if (Array.isArray(sub.required) && !sub.properties && result.properties) {
773
- const projected = {};
774
- for (const k of sub.required) {
775
- if (result.properties[k]) projected[k] = result.properties[k];
776
- }
777
- if (Object.keys(projected).length > 0) sub.properties = projected;
778
- }
779
- }
780
- return sub;
781
- });
782
- }
783
- }
784
- if (result.not && typeof result.not === 'object') {
785
- result.not = convertSchema(result.not);
786
- }
787
- return result;
788
- }
789
-
790
- function toGeminiTools(tools) {
791
- return {
792
- functionDeclarations: tools.map((t) => ({
793
- name: t.name,
794
- description: t.description,
795
- parameters: convertSchema(t.inputSchema),
796
- })),
797
- };
798
- }
799
-
800
- // Map the orchestrator-level toolChoice to Gemini's functionCallingConfig.
801
- // auto -> AUTO
802
- // required -> ANY
803
- // none -> NONE
804
- // { name } -> ANY + allowedFunctionNames:[name] (specific tool)
805
- function toGeminiToolConfig(toolChoice) {
806
- if (toolChoice == null) return undefined;
807
- if (typeof toolChoice === 'string') {
808
- if (toolChoice === 'auto') return { functionCallingConfig: { mode: 'AUTO' } };
809
- if (toolChoice === 'required') return { functionCallingConfig: { mode: 'ANY' } };
810
- if (toolChoice === 'none') return { functionCallingConfig: { mode: 'NONE' } };
811
- return undefined;
812
- }
813
- if (typeof toolChoice === 'object') {
814
- const name = toolChoice.name || toolChoice.function?.name;
815
- if (typeof name === 'string' && name) {
816
- return { functionCallingConfig: { mode: 'ANY', allowedFunctionNames: [name] } };
817
- }
818
- }
819
- return undefined;
820
- }
821
-
822
- function toGeminiContent(message, toolNameByCallId) {
823
- if (!message || message.role === 'system') return null;
824
- if (message.role === 'assistant' && message.toolCalls?.length) {
825
- const parts = [];
826
- if (message.content) parts.push({ text: message.content });
827
- for (const tc of message.toolCalls) {
828
- // Gemini 3 thinking models require the original thoughtSignature
829
- // echoed back on every prior functionCall so the cached thinking
830
- // prefix stays valid. v1beta places the field at the Part level
831
- // (sibling of functionCall) — putting it inside functionCall returns
832
- // 400 "Unknown name". Older models / first turn have no signature.
833
- const part = { functionCall: { name: tc.name, args: tc.arguments } };
834
- if (tc.thoughtSignature) part.thoughtSignature = tc.thoughtSignature;
835
- parts.push(part);
836
- }
837
- return { role: 'model', parts };
838
- }
839
- if (message.role === 'tool') {
840
- // Tool result content stays byte-identical for cache prefix stability.
841
- // Gemini accepts functionResponse parts under role 'user' (per docs).
842
- // Using 'user' keeps tool_result entries byte-identical between
843
- // cachedContents.create (which rejects role:'function') and
844
- // generateContent, so the cached prefix actually matches at runtime.
845
- // functionResponse.name must be the FUNCTION name, not the synthetic
846
- // toolCallId. Resolve it from the toolCallId->functionName map built
847
- // from prior assistant tool_calls; fall back to the raw id only when
848
- // no mapping exists.
849
- const functionName = (toolNameByCallId && toolNameByCallId.get(message.toolCallId))
850
- || message.toolCallId
851
- || '';
852
- return {
853
- role: 'user',
854
- parts: [{ functionResponse: { name: functionName, response: { result: message.content } } }],
855
- };
856
- }
857
- return {
858
- role: message.role === 'assistant' ? 'model' : 'user',
859
- parts: [{ text: message.content }],
860
- };
861
- }
862
-
863
- function toGeminiContents(messages) {
864
- const contents = [];
865
- // Map synthetic toolCallId -> function name from prior assistant
866
- // tool_calls so each functionResponse part carries the real function name.
867
- const toolNameByCallId = new Map();
868
- for (const m of messages) {
869
- if (m?.role === 'assistant' && Array.isArray(m.toolCalls)) {
870
- for (const tc of m.toolCalls) {
871
- if (tc?.id && tc?.name) toolNameByCallId.set(tc.id, tc.name);
872
- }
873
- }
874
- }
875
- for (const message of messages) {
876
- const content = toGeminiContent(message, toolNameByCallId);
877
- if (content) contents.push(content);
878
- }
879
- return contents;
880
- }
881
-
882
- function parseToolCalls(parts) {
883
- const calls = parts.filter((p) => 'functionCall' in p && !!p.functionCall);
884
- if (!calls.length)
885
- return undefined;
886
- // The @google/generative-ai 0.24.1 SDK predates Gemini 3 thinking — its
887
- // FunctionCall type only declares { name, args }. The runtime object,
888
- // however, retains whatever the wire response carried, which means the
889
- // signature may sit under any of:
890
- // • part.functionCall.thoughtSignature (camelCase, expected)
891
- // • part.functionCall.thought_signature (snake_case, raw protobuf)
892
- // • part.thoughtSignature / part.thought_signature (sibling on Part)
893
- // Read all four and use the first non-empty hit. Set MIXDOG_DEBUG_GEMINI=1
894
- // to dump the raw parts so we can confirm the actual key location on the
895
- // next session and harden the parser.
896
- if (process.env.MIXDOG_DEBUG_GEMINI === '1') {
897
- try { process.stderr.write(`[gemini fc raw] ${JSON.stringify(parts)}\n`); } catch {}
898
- }
899
- return calls.map((p, i) => {
900
- const fc = p.functionCall;
901
- const sig = fc.thoughtSignature
902
- || fc.thought_signature
903
- || p.thoughtSignature
904
- || p.thought_signature
905
- || null;
906
- const call = {
907
- id: `gemini_${Date.now()}_${i}`,
908
- name: fc.name,
909
- arguments: (fc.args ?? {}),
910
- };
911
- if (sig) call.thoughtSignature = sig;
912
- return call;
913
- });
914
- }
915
-
916
- export class GeminiProvider {
917
- // promptTokenCount is the total (cachedContentTokenCount is a subset), so
918
- // input already includes cache. See registry.mjs.
919
- static inputExcludesCache = false;
920
- name = 'gemini';
921
- genAI;
922
- config;
923
-
924
- constructor(config) {
925
- this.config = config;
926
- const apiKey = config.apiKey || process.env.GEMINI_API_KEY || '';
927
- this.genAI = new GoogleGenerativeAI(apiKey);
928
- // Warm a kept-alive socket to the Gemini REST API so the first cache/
929
- // generateContent request skips the cold TLS handshake. Best-effort.
930
- preconnect('https://generativelanguage.googleapis.com');
931
- }
932
-
933
- reloadApiKey() {
934
- try {
935
- const freshConfig = loadConfig();
936
- const cfg = freshConfig.providers?.gemini;
937
- const newKey = cfg?.apiKey || process.env.GEMINI_API_KEY;
938
- if (newKey) {
939
- this.genAI = new GoogleGenerativeAI(newKey);
940
- }
941
- } catch { /* best effort */ }
942
- }
943
-
944
- _getApiKey() {
945
- return this.config?.apiKey || process.env.GEMINI_API_KEY || '';
946
- }
947
-
948
- // Explicit cachedContents API. The implicit cache layer on Gemini 3.x
949
- // does not surface cachedContentTokenCount in usageMetadata, so the only
950
- // way to obtain measurable + billable cache savings is to register the
951
- // stable prefix (system + tools) as a CachedContent and pass its name on
952
- // every generateContent call. TTL is 1h so a single worker session keeps
953
- // one cache slot warm without re-creation overhead; storage cost (~$0.5/M
954
- // tokens/hour) is dwarfed by the 75% input-price discount on hits beyond
955
- // a few iterations.
956
- async _ensureGeminiCache({ apiKey, model, systemInstruction, geminiTools, contents, opts }) {
957
- const state = opts.providerState?.gemini || null;
958
- const now = Date.now();
959
- const currentIter = Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : 1;
960
- const refreshEveryN = Number(process.env.MIXDOG_GEMINI_CACHE_REFRESH_EVERY) > 0
961
- ? Number(process.env.MIXDOG_GEMINI_CACHE_REFRESH_EVERY)
962
- : 4;
963
- const cacheLiveMs = state?.cacheExpiresAt ? state.cacheExpiresAt - now : 0;
964
- const itersSinceCreate = state?.cacheCreatedAtIter != null
965
- ? currentIter - state.cacheCreatedAtIter
966
- : Infinity;
967
- const statePrefixContentCount = Number.isFinite(Number(state?.cachePrefixContentCount))
968
- ? Math.max(0, Math.trunc(Number(state.cachePrefixContentCount)))
969
- : null;
970
- const currentStatePrefixHash = statePrefixContentCount != null
971
- ? _geminiCachePrefixHash({
972
- model,
973
- systemInstruction,
974
- geminiTools,
975
- contents,
976
- prefixCount: statePrefixContentCount,
977
- })
978
- : null;
979
- const modelMatches = !!state?.cacheName && state?.cacheModel === model;
980
- const prefixMatches = !!state?.cacheName
981
- && statePrefixContentCount != null
982
- && statePrefixContentCount <= (Array.isArray(contents) ? contents.length : 0)
983
- && !!state?.cachePrefixHash
984
- && state.cachePrefixHash === currentStatePrefixHash;
985
- const canAttachState = !!state?.cacheName && cacheLiveMs > 0 && modelMatches && prefixMatches;
986
- const canReuseState = canAttachState && cacheLiveMs > 6 * 60 * 1000 && itersSinceCreate < refreshEveryN;
987
- try {
988
- appendBridgeTrace({
989
- sessionId: opts.sessionId || opts.session?.id || null,
990
- iteration: currentIter,
991
- kind: 'gemini_cache_decision',
992
- payload: {
993
- hasState: !!state?.cacheName,
994
- stateCacheName: state?.cacheName || null,
995
- stateCreatedAtIter: state?.cacheCreatedAtIter ?? null,
996
- stateCacheModel: state?.cacheModel || null,
997
- statePrefixContentCount,
998
- statePrefixHash: state?.cachePrefixHash || null,
999
- currentStatePrefixHash,
1000
- modelMatches,
1001
- prefixMatches,
1002
- canAttachState,
1003
- cacheLiveMs,
1004
- itersSinceCreate,
1005
- refreshEveryN,
1006
- decision: canReuseState ? 'reuse' : 'rebuild',
1007
- contentsLen: Array.isArray(contents) ? contents.length : 0,
1008
- },
1009
- });
1010
- } catch {}
1011
- if (canReuseState) {
1012
- return state.cacheName;
1013
- }
1014
- if (!apiKey) return null;
1015
- // Pre-flight invariant: cachedContents.create rejects prefixes below
1016
- // the model-specific minimum. Skip the POST entirely when the estimate
1017
- // is under threshold so we don't spam 400 responses turn-after-turn.
1018
- const minTokens = _geminiCacheMinTokens(model);
1019
- const estimatedTokens = _estimateGeminiCacheTokens(systemInstruction, geminiTools, contents);
1020
- if (estimatedTokens < minTokens) {
1021
- try {
1022
- appendBridgeTrace({
1023
- sessionId: opts.sessionId || opts.session?.id || null,
1024
- iteration: currentIter,
1025
- kind: 'gemini_cache_skip',
1026
- payload: {
1027
- reason: 'prefix_below_min',
1028
- estimatedTokens,
1029
- minTokens,
1030
- model,
1031
- },
1032
- });
1033
- } catch {}
1034
- return canAttachState ? state.cacheName : null;
1035
- }
1036
- try {
1037
- const ttlSeconds = 3600;
1038
- const cachePrefixContentCount = _geminiCachePrefixCount(contents);
1039
- const cachePrefixHash = _geminiCachePrefixHash({
1040
- model,
1041
- systemInstruction,
1042
- geminiTools,
1043
- contents,
1044
- prefixCount: cachePrefixContentCount,
1045
- });
1046
- const cachePrefixContents = _geminiCachePrefixContents(contents, cachePrefixContentCount);
1047
- const body = {
1048
- model: `models/${model}`,
1049
- ttl: `${ttlSeconds}s`,
1050
- };
1051
- if (systemInstruction) {
1052
- body.systemInstruction = { parts: [{ text: systemInstruction }] };
1053
- }
1054
- if (Array.isArray(geminiTools) && geminiTools.length) {
1055
- body.tools = geminiTools;
1056
- }
1057
- // Capture conversation prefix (everything except the latest user/
1058
- // tool input that the generateContent call will carry) inside the
1059
- // cache. cachedContents only accepts role='user' or 'model';
1060
- // generateContent uses role='function' for tool_result turns, so
1061
- // collapse that to 'user' (functionResponse parts remain inside).
1062
- if (cachePrefixContents.length) {
1063
- body.contents = cachePrefixContents;
1064
- }
1065
- const url = `https://generativelanguage.googleapis.com/v1beta/cachedContents?key=${encodeURIComponent(apiKey)}`;
1066
- // Honor the external session abort signal during cache creation, not
1067
- // only the 20s ceiling. Without merging opts.signal a session that is
1068
- // aborted (stall-watchdog / closeSession) mid-cache-create leaves this
1069
- // preflight request running until its own timeout fires.
1070
- const res = await fetch(url, {
1071
- method: 'POST',
1072
- headers: { 'Content-Type': 'application/json' },
1073
- body: JSON.stringify(body),
1074
- signal: opts.signal
1075
- ? AbortSignal.any([opts.signal, AbortSignal.timeout(20_000)])
1076
- : AbortSignal.timeout(20_000),
1077
- dispatcher: getLlmDispatcher(),
1078
- });
1079
- if (!res.ok) {
1080
- const text = await res.text().catch(() => '');
1081
- try {
1082
- appendBridgeTrace({
1083
- sessionId: opts.sessionId || opts.session?.id || null,
1084
- iteration: currentIter,
1085
- kind: 'gemini_cache_create_fail',
1086
- payload: {
1087
- status: res.status,
1088
- body: text.slice(0, 500),
1089
- contentsLen: Array.isArray(contents) ? contents.length : 0,
1090
- cachePrefixContentCount,
1091
- canAttachState,
1092
- },
1093
- });
1094
- } catch {}
1095
- return canAttachState ? state.cacheName : null;
1096
- }
1097
- const data = await res.json();
1098
- const cacheName = data?.name || null;
1099
- if (!cacheName) return canAttachState ? state.cacheName : null;
1100
- const cacheTokenSize = Number(data?.usageMetadata?.totalTokenCount || 0) || 0;
1101
- try {
1102
- appendBridgeTrace({
1103
- sessionId: opts.sessionId || opts.session?.id || null,
1104
- iteration: currentIter,
1105
- kind: 'gemini_cache_create_ok',
1106
- payload: {
1107
- cacheName,
1108
- cacheTokenSize,
1109
- contentsLen: Array.isArray(contents) ? contents.length : 0,
1110
- cachePrefixContentCount,
1111
- cachePrefixHash,
1112
- },
1113
- });
1114
- } catch {}
1115
- // Best-effort cleanup of the previous cache so storage cost only
1116
- // accrues on the live revision. Fire-and-forget; TTL expiry covers
1117
- // any delete failures.
1118
- const priorCacheName = state?.cacheName || null;
1119
- if (priorCacheName && priorCacheName !== cacheName) {
1120
- const delUrl = `https://generativelanguage.googleapis.com/v1beta/${priorCacheName}?key=${encodeURIComponent(apiKey)}`;
1121
- fetch(delUrl, { method: 'DELETE', signal: AbortSignal.timeout(10_000), dispatcher: getLlmDispatcher() })
1122
- .catch(() => { /* TTL expiry will reclaim it */ });
1123
- }
1124
- opts.providerState = {
1125
- ...(opts.providerState || {}),
1126
- gemini: {
1127
- cacheName,
1128
- cacheCreatedAt: now,
1129
- cacheCreatedAtIter: currentIter,
1130
- cacheExpiresAt: now + ttlSeconds * 1000,
1131
- cacheModel: model,
1132
- cacheTokenSize,
1133
- cachePrefixContentCount,
1134
- cachePrefixHash,
1135
- },
1136
- };
1137
- return cacheName;
1138
- } catch (err) {
1139
- process.stderr.write(`[gemini] cachedContents.create error: ${err?.message || err}\n`);
1140
- return canAttachState ? state.cacheName : null;
1141
- }
1142
- }
1143
-
1144
- async send(messages, model, tools, sendOpts) {
1145
- try {
1146
- return await this._doSend(messages, model, tools, sendOpts);
1147
- } catch (err) {
1148
- if (err.message && (err.message.includes('401') || err.message.includes('403'))) {
1149
- process.stderr.write(`[provider] Auth error, re-reading config...\n`);
1150
- this.reloadApiKey();
1151
- return await this._doSend(messages, model, tools, sendOpts);
1152
- }
1153
- throw err;
1154
- }
1155
- }
1156
-
1157
- async _doSend(messages, model, tools, sendOpts) {
1158
- const opts = sendOpts || {};
1159
- const signal = opts.signal || null;
1160
- const onStreamDelta = typeof opts.onStreamDelta === 'function' ? opts.onStreamDelta : null;
1161
- if (signal?.aborted) {
1162
- const reason = signal.reason;
1163
- throw reason instanceof Error ? reason : new Error('Gemini request aborted by session close');
1164
- }
1165
-
1166
- const useModel = model || await ensureLatestGeminiModel(this);
1167
- const systemInstruction = messages
1168
- .filter(m => m.role === 'system')
1169
- .map(m => m.content)
1170
- .join('\n\n') || undefined;
1171
- const chatMsgs = messages.filter(m => m.role !== 'system');
1172
- const contents = toGeminiContents(chatMsgs);
1173
- if (!contents.length)
1174
- throw new Error('No messages to send');
1175
-
1176
- const geminiTools = tools?.length ? [toGeminiTools(tools)] : undefined;
1177
- const toolConfig = geminiTools ? toGeminiToolConfig(opts.toolChoice) : undefined;
1178
- try { opts.onStageChange?.('requesting'); } catch {}
1179
-
1180
- // Explicit cachedContents (system + tools + prior-turn transcript).
1181
- // Per Google docs, `tools` must be supplied on BOTH the cache create
1182
- // call AND every subsequent generate_content call — the cache stores
1183
- // the schema for prompt-token credit but the runtime model still
1184
- // needs the tool schema to actually emit function calls. Sending
1185
- // cachedContent without tools yields an empty completion (function
1186
- // calling silently disabled). The contents payload captures the
1187
- // accumulated prefix; we refresh the cache every N iterations so
1188
- // recent turns also enter the cached prefix instead of being billed
1189
- // at full input rates.
1190
- const cachedContent = await this._ensureGeminiCache({
1191
- apiKey: this._getApiKey(),
1192
- model: useModel,
1193
- systemInstruction,
1194
- geminiTools,
1195
- contents,
1196
- opts,
1197
- });
1198
- try { opts.onStageChange?.('requesting'); } catch {}
1199
-
1200
- // When cachedContent is attached we bypass @google/generative-ai
1201
- // (deprecated; v1beta v1.x docs explicitly forbid re-sending tools or
1202
- // systemInstruction once a cache carries them, but the bundled SDK
1203
- // can't actually issue a tool-less generateContent call). REST direct
1204
- // sends the v1beta payload Google's new genai client uses, so the
1205
- // cache owns system/tools and the runtime gets a clean cache hit.
1206
- let response;
1207
- if (cachedContent) {
1208
- const apiKey = this._getApiKey();
1209
- const genUrl = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(useModel)}:streamGenerateContent?alt=sse&key=${encodeURIComponent(apiKey)}`;
1210
- const cachedPrefixContentCount = Number.isFinite(Number(opts.providerState?.gemini?.cachePrefixContentCount))
1211
- ? Math.max(0, Math.min(contents.length, Math.trunc(Number(opts.providerState.gemini.cachePrefixContentCount))))
1212
- : 0;
1213
- const deltaContents = contents.slice(cachedPrefixContentCount);
1214
- // Cache carries the recorded prefix. Send every uncached tail turn,
1215
- // not just the last message, so reused cachedContents preserve
1216
- // full conversation context between periodic refreshes.
1217
- const body = {
1218
- contents: deltaContents.length ? deltaContents : contents.slice(-1),
1219
- cachedContent,
1220
- };
1221
- if (toolConfig) body.toolConfig = toolConfig;
1222
- response = await runGeminiOperationWithTimeout({
1223
- label: 'Gemini REST streamGenerateContent total',
1224
- timeoutMs: PROVIDER_NONSTREAM_TOTAL_TIMEOUT_MS,
1225
- signal,
1226
- run: (totalSignal) => withRetry(
1227
- async ({ signal: attemptSignal }) => {
1228
- try { opts.onStageChange?.('requesting'); } catch {}
1229
- const openFirstByte = createTimeoutSignal(
1230
- attemptSignal,
1231
- GEMINI_FIRST_BYTE_TIMEOUT_MS,
1232
- 'Gemini REST first byte',
1233
- );
1234
- let res;
1235
- try {
1236
- res = await fetch(genUrl, {
1237
- method: 'POST',
1238
- headers: { 'Content-Type': 'application/json' },
1239
- body: JSON.stringify(body),
1240
- signal: openFirstByte.signal,
1241
- dispatcher: getLlmDispatcher(),
1242
- });
1243
- } finally {
1244
- openFirstByte.cleanup();
1245
- }
1246
- if (!res.ok) {
1247
- const text = await res.text().catch(() => '');
1248
- const err = new Error(`Gemini REST streamGenerateContent ${res.status}: ${text.slice(0, 300)}`);
1249
- err.status = res.status;
1250
- throw err;
1251
- }
1252
- return await consumeGeminiRestStreamResponse(res, {
1253
- signal: attemptSignal,
1254
- onStreamDelta,
1255
- label: 'Gemini REST streamGenerateContent',
1256
- });
1257
- },
1258
- {
1259
- signal: totalSignal,
1260
- onRetry: ({ attempt, lastErr }) => {
1261
- try { opts.onStageChange?.('requesting'); } catch {}
1262
- process.stderr.write(`[gemini-rest] retry attempt ${attempt + 1} after ${lastErr?.message || lastErr?.code || 'transient error'}\n`);
1263
- },
1264
- },
1265
- ),
1266
- });
1267
- } else {
1268
- const genModel = this.genAI.getGenerativeModel({
1269
- model: useModel,
1270
- systemInstruction,
1271
- tools: geminiTools,
1272
- ...(toolConfig ? { toolConfig } : {}),
1273
- });
1274
- response = await runGeminiOperationWithTimeout({
1275
- label: 'Gemini streamGenerateContent total',
1276
- timeoutMs: PROVIDER_NONSTREAM_TOTAL_TIMEOUT_MS,
1277
- signal,
1278
- run: (totalSignal) => withRetry(
1279
- async ({ signal: attemptSignal }) => {
1280
- try { opts.onStageChange?.('requesting'); } catch {}
1281
- const openFirstByte = createTimeoutSignal(
1282
- attemptSignal,
1283
- GEMINI_FIRST_BYTE_TIMEOUT_MS,
1284
- 'Gemini SDK first byte',
1285
- );
1286
- let streamResult;
1287
- try {
1288
- try {
1289
- streamResult = await genModel.generateContentStream(
1290
- { contents },
1291
- { signal: openFirstByte.signal },
1292
- );
1293
- } catch (err) {
1294
- if (openFirstByte.signal.aborted) {
1295
- throw openFirstByte.signal.reason instanceof Error
1296
- ? openFirstByte.signal.reason
1297
- : err;
1298
- }
1299
- throw err;
1300
- }
1301
- } finally {
1302
- openFirstByte.cleanup();
1303
- }
1304
- return await consumeGeminiSdkStream(streamResult, {
1305
- signal: attemptSignal,
1306
- onStreamDelta,
1307
- label: 'Gemini SDK streamGenerateContent',
1308
- });
1309
- },
1310
- {
1311
- signal: totalSignal,
1312
- onRetry: ({ attempt, lastErr }) => {
1313
- try { opts.onStageChange?.('requesting'); } catch {}
1314
- process.stderr.write(`[gemini] retry attempt ${attempt + 1} after ${lastErr?.message || lastErr?.code || 'transient error'}\n`);
1315
- },
1316
- },
1317
- ),
1318
- });
1319
- }
1320
- writeGeminiCacheTrace({
1321
- opts,
1322
- model: useModel,
1323
- systemInstruction,
1324
- tools,
1325
- contents,
1326
- usageMetadata: response.usageMetadata,
1327
- cachedContent,
1328
- });
1329
- const candidate = response.candidates?.[0] || null;
1330
- const textParts = candidate?.content?.parts?.filter(p => 'text' in p) ?? [];
1331
- const content = textParts.map(p => 'text' in p ? p.text : '').join('');
1332
- const toolCalls = parseToolCalls(candidate?.content?.parts ?? []);
1333
- // Inspect candidate.finishReason — Gemini reports terminal status here.
1334
- // Only STOP (and the legacy "FINISH_REASON_STOP") plus tool/function-
1335
- // call paths represent a fully delivered turn. MAX_TOKENS / SAFETY /
1336
- // RECITATION / OTHER all mean the candidate was cut off before the
1337
- // model finished, and surfacing the partial text as final would
1338
- // silently accept a truncated answer. Convert those into a typed
1339
- // provider-incomplete error so the loop can decide whether to retry,
1340
- // nudge, or surface to the user. Missing finishReason (still
1341
- // streaming / unknown) is left alone — existing success paths for
1342
- // genuinely complete responses keep working.
1343
- const finishReason = candidate?.finishReason || null;
1344
- const incompleteFinishReasons = new Set([
1345
- 'MAX_TOKENS',
1346
- 'SAFETY',
1347
- 'RECITATION',
1348
- 'OTHER',
1349
- 'BLOCKLIST',
1350
- 'PROHIBITED_CONTENT',
1351
- 'SPII',
1352
- 'MALFORMED_FUNCTION_CALL',
1353
- ]);
1354
- if (finishReason && incompleteFinishReasons.has(finishReason)) {
1355
- const err = Object.assign(
1356
- new Error(`Gemini response incomplete: finishReason=${finishReason}`),
1357
- {
1358
- name: 'ProviderIncompleteError',
1359
- code: 'PROVIDER_INCOMPLETE',
1360
- providerIncomplete: true,
1361
- finishReason,
1362
- partialContent: content,
1363
- partialToolCalls: toolCalls,
1364
- model: useModel,
1365
- rawUsage: response.usageMetadata || null,
1366
- },
1367
- );
1368
- throw err;
1369
- }
1370
- const um = response.usageMetadata || null;
1371
- // Hoist cachedTokens so the returned usage block can reuse the
1372
- // exact value the trace already recorded (including the
1373
- // cachedFallback when cachedContentTokenCount under-reports).
1374
- let cachedTokens = 0;
1375
- if (um) {
1376
- const {
1377
- inputTokens,
1378
- reportedCachedTokens,
1379
- cachedFallbackTokens,
1380
- cachedTokens: resolvedCachedTokens,
1381
- cacheTokenSource,
1382
- } = _resolveGeminiCacheUsage({
1383
- usageMetadata: um,
1384
- cachedContent,
1385
- providerState: opts.providerState,
1386
- });
1387
- cachedTokens = resolvedCachedTokens;
1388
- const outputTokens = (um.candidatesTokenCount || 0) + (um.thoughtsTokenCount || 0);
1389
- if (cachedContent && inputTokens > 0 && cachedTokens <= 0) {
1390
- try {
1391
- appendBridgeTrace({
1392
- sessionId: opts.sessionId || opts.session?.id || null,
1393
- iteration: Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null,
1394
- kind: 'gemini_cache_anomaly',
1395
- payload: {
1396
- reason: 'cached_content_attached_but_zero_cached_tokens',
1397
- inputTokens,
1398
- reportedCachedTokens,
1399
- cachedFallbackTokens,
1400
- cacheTokenSource,
1401
- cacheName: opts.providerState?.gemini?.cacheName || null,
1402
- cachePrefixContentCount: opts.providerState?.gemini?.cachePrefixContentCount ?? null,
1403
- },
1404
- });
1405
- } catch {}
1406
- }
1407
- traceBridgeUsage({
1408
- sessionId: opts.sessionId || opts.session?.id || null,
1409
- iteration: Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null,
1410
- inputTokens,
1411
- outputTokens,
1412
- cachedTokens,
1413
- cacheWriteTokens: 0,
1414
- promptTokens: inputTokens,
1415
- model: useModel,
1416
- modelDisplay: useModel,
1417
- rawUsage: um,
1418
- provider: 'gemini',
1419
- });
1420
- }
1421
- return {
1422
- content,
1423
- model: useModel,
1424
- toolCalls,
1425
- providerState: opts.providerState,
1426
- usage: um ? (() => {
1427
- const input = um.promptTokenCount || um.totalTokenCount || 0;
1428
- return {
1429
- inputTokens: input,
1430
- outputTokens: (um.candidatesTokenCount || 0) + (um.thoughtsTokenCount || 0),
1431
- // Use the already-computed cachedTokens (with
1432
- // cache-create fallback applied) rather than the raw
1433
- // metadata field, so the returned usage matches what
1434
- // traceBridgeUsage recorded for this same call.
1435
- cachedTokens,
1436
- // Gemini promptTokenCount is total (cachedContentTokenCount
1437
- // is a subset). Alias directly into promptTokens.
1438
- promptTokens: input,
1439
- };
1440
- })() : undefined,
1441
- };
1442
- }
1443
-
1444
- async listModels() {
1445
- const cached = _loadModelCache();
1446
- if (cached) return cached;
1447
- // Dynamic lookup via Gemini v1beta /models. Requires API key.
1448
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
1449
- if (!apiKey) return MODELS; // no key — return minimal static list
1450
- try {
1451
- return await this._fetchAndCacheModels(apiKey);
1452
- } catch (err) {
1453
- process.stderr.write(`[gemini] listModels fetch failed (${err.message})\n`);
1454
- return MODELS;
1455
- }
1456
- }
1457
-
1458
- // Shared fetch+normalize+enrich+write used by both listModels() (after the
1459
- // TTL check) and _refreshModelCache() (bypassing it). Throws on failure so
1460
- // each caller applies its own fallback/logging.
1461
- async _fetchAndCacheModels(apiKey) {
1462
- const url = `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}`;
1463
- const listSignal = AbortSignal.timeout(60_000);
1464
- const res = await fetch(url, { signal: listSignal, dispatcher: getLlmDispatcher() });
1465
- if (!res.ok) throw new Error(`gemini list_models ${res.status}`);
1466
- const data = await res.json();
1467
- const items = Array.isArray(data?.models) ? data.models : [];
1468
- // Filter to Gemini family; skip embedding/imagen endpoints.
1469
- const normalized = items
1470
- .filter(m => (m?.name || '').includes('gemini'))
1471
- .filter(m => !/embedding|aqa|imagen/.test(m?.name || ''))
1472
- .map(m => {
1473
- const id = (m.name || '').replace(/^models\//, '');
1474
- const family = /flash-lite/.test(id) ? 'gemini-flash-lite'
1475
- : /flash/.test(id) ? 'gemini-flash'
1476
- : /pro/.test(id) ? 'gemini-pro'
1477
- : 'gemini';
1478
- return {
1479
- id,
1480
- display: m.displayName || id,
1481
- family,
1482
- provider: 'gemini',
1483
- contextWindow: m.inputTokenLimit || 1000000,
1484
- outputTokens: m.outputTokenLimit || 8192,
1485
- tier: 'version',
1486
- latest: false,
1487
- description: m.description || '',
1488
- };
1489
- });
1490
- _markLatestGemini(normalized);
1491
- // LiteLLM catalog overlays pricing and updated metadata.
1492
- const { enrichModels } = await import('./model-catalog.mjs');
1493
- const enriched = await enrichModels(normalized);
1494
- _saveModelCache(enriched);
1495
- return enriched;
1496
- }
1497
-
1498
- // Force a catalog refresh (ignores the 24h disk TTL). De-duped via
1499
- // _modelRefreshInFlight so concurrent callers share one HTTP round-trip.
1500
- // Fire-and-forget context: failures are caught/logged, returning null.
1501
- async _refreshModelCache() {
1502
- if (_modelRefreshInFlight) return _modelRefreshInFlight;
1503
- _modelRefreshInFlight = (async () => {
1504
- try {
1505
- const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY;
1506
- if (!apiKey) return null; // no key — nothing to refresh
1507
- const enriched = await this._fetchAndCacheModels(apiKey);
1508
- process.stderr.write(`[gemini] catalog refreshed (${enriched.length} models)\n`);
1509
- return enriched;
1510
- } catch (err) {
1511
- process.stderr.write(`[gemini] catalog refresh failed (${err.message})\n`);
1512
- return null;
1513
- } finally {
1514
- _modelRefreshInFlight = null;
1515
- }
1516
- })();
1517
- return _modelRefreshInFlight;
1518
- }
1519
-
1520
- async isAvailable() {
1521
- try {
1522
- const model = this.genAI.getGenerativeModel({ model: DEFAULT_MODEL });
1523
- await model.generateContent('hi');
1524
- return true;
1525
- }
1526
- catch {
1527
- return false;
1528
- }
1529
- }
1530
- }