mixdog 0.7.1

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 (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. package/tools.json +1653 -0
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Smart Bridge — Internal LLM Helper (session-based).
3
+ *
4
+ * Every one-shot LLM dispatch from internal callers (memory-cycle,
5
+ * scheduler, webhook) now flows through the SAME session pipeline as the
6
+ * MCP `bridge` tool. No more parallel `provider.send()` helper — one code
7
+ * path = one message shape = one usage log = "bridge single path".
8
+ *
9
+ * The returned function uses the existing caller signature, so call sites
10
+ * do not need changes:
11
+ *
12
+ * const llm = makeBridgeLlm({ role: 'maintenance', preset: 'haiku' });
13
+ * const text = await llm({ prompt });
14
+ *
15
+ * Internally it:
16
+ * 1. Resolves the preset (explicit arg > opts.preset > user-workflow.json[role])
17
+ * 2. Creates or reuses a session via the session manager
18
+ * 3. Applies stateless-reset for stateless profiles so the prefix handle
19
+ * stays warm while per-dispatch transcripts never leak
20
+ * 4. Calls `askSession` → provider.send() → usage logged via
21
+ * `session/manager.mjs` (mode='active')
22
+ */
23
+
24
+ import { readFileSync } from 'fs';
25
+ import { join } from 'path';
26
+ import { loadConfig } from '../config.mjs';
27
+ import { resolveRuntimeSpec } from '../config.mjs';
28
+ import { getHiddenRole, resolveBridgeSessionPermission } from '../internal-roles.mjs';
29
+ import { DEFAULT_PLUGIN, DEFAULT_MARKETPLACE } from '../../../shared/plugin-paths.mjs';
30
+ import { prepareBridgeSession } from './session-builder.mjs';
31
+ import {
32
+ askSession,
33
+ updateSessionStatus,
34
+ closeSession,
35
+ } from '../session/manager.mjs';
36
+
37
+ // Cap bridge role synthesis to ~3000 tokens (~12 KB at the 4 B/tok
38
+ // working average). Pool B explore/recall/search answers occasionally land
39
+ // 8-10k-token walls that then ride in the Lead context for the rest of the
40
+ // turn; the cap keeps those outliers bounded without touching the 95%+ of
41
+ // answers already under the threshold.
42
+ const BRIEF_CAP_BYTES = 12 * 1024;
43
+ function applyBriefCap(text) {
44
+ if (typeof text !== 'string') return text;
45
+ if (text.length <= BRIEF_CAP_BYTES) return text;
46
+ const head = text.slice(0, BRIEF_CAP_BYTES);
47
+ const approxTokens = Math.round(text.length / 4);
48
+ return `${head}\n\n... [TRUNCATED — full answer was ~${approxTokens} tokens / ${Math.round(text.length / 1024)} KB. Re-run with brief:false for the complete synthesis]`;
49
+ }
50
+
51
+ function pluginRoot() {
52
+ return process.env.CLAUDE_PLUGIN_ROOT
53
+ || join(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'plugins', 'marketplaces', DEFAULT_MARKETPLACE, 'external_plugins', DEFAULT_PLUGIN);
54
+ }
55
+
56
+ // Unified-shard policy — most bridge sessions (Pool B + Pool C) share the
57
+ // same tool schema so BP_1 is bit-identical across roles and one provider-side
58
+ // cache shard serves every caller. Per-role behaviour is steered by:
59
+ // 1. rules/bridge/*.md concatenated into BP2 roleCatalog (via
60
+ // loadScopedRoleCatalog — every bridge session carries the full
61
+ // hidden-role catalog so the shard stays bit-identical across roles)
62
+ // 2. call-time guards (loop.mjs write-block + ai-wrapped-dispatch
63
+ // recursion break)
64
+ // Hidden-role exceptions are declarative: defaults/hidden-roles.json may set
65
+ // toolSchemaProfile when first-turn routing quality is worth a separate tool
66
+ // prefix. Keep "unified" as the default; new profiles must justify the cache
67
+ // shard split.
68
+ // See manager.mjs resolveSessionTools for the single source of truth;
69
+ // bridge visibility is declared via annotations.bridgeHidden on each tool def.
70
+ const HIDDEN_ROLE_TOOL_SCHEMA_PROFILES = Object.freeze({
71
+ unified: null,
72
+ 'llm-only': Object.freeze([]),
73
+ 'filesystem-read': Object.freeze([
74
+ 'code_graph',
75
+ 'glob',
76
+ 'list',
77
+ 'grep',
78
+ 'read',
79
+ ]),
80
+ });
81
+
82
+ export function resolveHiddenRoleSchemaAllowedTools(hidden) {
83
+ if (!hidden) return null;
84
+ if (Array.isArray(hidden.schemaAllowedTools)) {
85
+ return hidden.schemaAllowedTools.map((name) => String(name || '').trim()).filter(Boolean);
86
+ }
87
+ const profile = String(hidden.toolSchemaProfile || 'unified').trim() || 'unified';
88
+ if (Object.prototype.hasOwnProperty.call(HIDDEN_ROLE_TOOL_SCHEMA_PROFILES, profile)) {
89
+ return HIDDEN_ROLE_TOOL_SCHEMA_PROFILES[profile];
90
+ }
91
+ process.stderr.write(`[bridge-llm] unknown hidden-role toolSchemaProfile="${profile}" role="${hidden.name || 'unknown'}"; using unified schema\n`);
92
+ return null;
93
+ }
94
+
95
+ function buildUnifiedHeader({ permission, role }) {
96
+ const lines = [];
97
+ if (permission) lines.push(`Permission: ${permission}`);
98
+ if (role) lines.push(`Role: ${role}`);
99
+ if (lines.length === 0) return '';
100
+ lines.push('');
101
+ lines.push('---');
102
+ lines.push('');
103
+ return lines.join('\n');
104
+ }
105
+
106
+ /**
107
+ * Resolve a preset name from (preset arg | opts.preset | hidden-role | user-workflow).
108
+ *
109
+ * Hidden roles (explorer, cycle1/cycle2, scheduler-task, etc.) are
110
+ * plugin-managed and take precedence over user-workflow.json — users cannot
111
+ * override them by redefining the same name.
112
+ */
113
+ export function resolvePresetName({ preset, optsPreset, role, config: cfgIn = null }) {
114
+ if (preset) return preset;
115
+ if (optsPreset) return optsPreset;
116
+ if (!role) return null;
117
+ // Hidden roles resolve their maintenance preset by SLOT. Every slot carries
118
+ // a concrete default in DEFAULT_MAINTENANCE, so `maint[slot]` resolves
119
+ // directly; the Setup panel can still tune each slot independently.
120
+ // (explorer.slot = 'explore', cycle1-agent.slot = 'cycle1', …)
121
+ const hidden = getHiddenRole(role);
122
+ if (hidden) {
123
+ try {
124
+ const config = cfgIn || loadConfig();
125
+ const maint = config?.maintenance || {};
126
+ return maint[hidden.slot] || null;
127
+ } catch { return null; }
128
+ }
129
+ try {
130
+ const pluginData = process.env.CLAUDE_PLUGIN_DATA;
131
+ if (!pluginData) return null;
132
+ const wf = JSON.parse(readFileSync(join(pluginData, 'user-workflow.json'), 'utf8'));
133
+ if (!Array.isArray(wf.roles)) return null;
134
+ const entry = wf.roles.find((r) => r.name === role);
135
+ return entry ? entry.preset : null;
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Build a bridge-backed LLM callback.
143
+ *
144
+ * @param {object} opts
145
+ * @param {string} opts.role — REQUIRED; canonical role name (worker, cycle1-agent, scheduler-task, ...)
146
+ * @param {string} [opts.taskType] — optional internal classification stamped on the session
147
+ * @param {string} [opts.preset] — explicit preset override (bypasses role → preset lookup)
148
+ * @param {string} [opts.parentSessionId] — parent bridge session for trace aggregation
149
+ * @param {AbortSignal} [opts.parentSignal] — optional AbortSignal from the fan-out coordinator;
150
+ * when aborted the bridge role session's own controller is also aborted so the
151
+ * provider call tears down promptly (parent→child cascade).
152
+ * @returns {(args: { prompt, preset?, sourceName? }) => Promise<string>}
153
+ */
154
+ export function makeBridgeLlm(opts = {}) {
155
+ if (!opts.role || typeof opts.role !== 'string') {
156
+ throw new Error('[bridge-llm] opts.role is required');
157
+ }
158
+ const role = opts.role;
159
+
160
+ return async function bridgeLlm({ prompt, preset: presetArg, sourceName: sourceNameArg, parentSignal: callParentSignal, idleTimeoutMs: callIdleTimeoutMs }) {
161
+ if (typeof prompt !== 'string' || !prompt) {
162
+ throw new Error(`[bridge-llm] prompt required for role "${role}"`);
163
+ }
164
+
165
+ const config = opts.config || loadConfig();
166
+ const presetName = resolvePresetName({
167
+ preset: presetArg,
168
+ optsPreset: opts.preset,
169
+ role,
170
+ config,
171
+ });
172
+ if (!presetName) {
173
+ throw new Error(
174
+ `[bridge-llm] preset unresolved for role "${role}" `
175
+ + `(preset="${presetArg || opts.preset || ''}")`,
176
+ );
177
+ }
178
+
179
+ const preset = config.presets?.find((p) => p.id === presetName || p.name === presetName);
180
+ if (!preset) {
181
+ throw new Error(`[bridge-llm] preset "${presetName}" not found in mixdog-config.json`);
182
+ }
183
+
184
+ const runtimeSpec = resolveRuntimeSpec(preset, {
185
+ lane: 'bridge',
186
+ agentId: role,
187
+ });
188
+
189
+ // Callers (e.g. aiWrapped explore dispatch) may pass an explicit
190
+ // `cwd` to scope the agent's filesystem view. Absolute path expected
191
+ // (aiWrapped already expands `~` and resolves relatives). When unset
192
+ // we pass `null` through instead of falling back to `process.cwd()`
193
+ // — the MCP server's launch dir is not deterministic across callers,
194
+ // and the downstream skill-discovery path tolerates null. Combined
195
+ // with the frozen bridge skill meta-tools (collect.mjs) this keeps
196
+ // every caller on the same provider cache shard.
197
+ const cwd = (typeof opts.cwd === 'string' && opts.cwd) ? opts.cwd : null;
198
+
199
+ // Unified dispatch: Pool B/C share bit-identical tools + system prompt
200
+ // unless a hidden role declares a narrow toolSchemaProfile. Per-role
201
+ // differentiation rides in the user-message header (permission + role
202
+ // line) plus an optional short Pool C snippet. The read-only contract
203
+ // is still enforced at call time via loop.mjs's READ_BLOCKED_TOOLS
204
+ // guard; schema profiles are a routing-efficiency layer, not safety.
205
+ const hidden = getHiddenRole(role);
206
+ const isPoolC = Boolean(hidden);
207
+ // Permission: read-declared hidden roles are locked in
208
+ // resolveBridgeSessionPermission (prepareBridgeSession applies the same).
209
+ const permission = resolveBridgeSessionPermission(
210
+ role,
211
+ opts.permission ?? (isPoolC ? (hidden?.permission || 'read') : null),
212
+ );
213
+ // Pool C hidden-role instructions live in BP2 roleCatalog (loaded
214
+ // by loadScopedRoleCatalog from rules/bridge/*.md) — the Tier 3
215
+ // reminder is suppressed so the shard stays bit-identical across
216
+ // every bridge role.
217
+ //
218
+ // User message = pure query. Permission / role ride in BP3
219
+ // sessionMarker (composeSystemPrompt) — only the query varies per
220
+ // call, so provider cache reuses the shared prefix.
221
+ //
222
+ // Stateless ephemeral session — created fresh per call, never
223
+ // pooled or resumed. Cache prefix matching happens at the provider
224
+ // layer (account-level), not the session level.
225
+ const finalPrompt = prompt;
226
+ const { session } = prepareBridgeSession({
227
+ role,
228
+ presetName,
229
+ preset,
230
+ runtimeSpec,
231
+ permission,
232
+ cwd,
233
+ sourceType: opts.sourceType,
234
+ sourceName: sourceNameArg || opts.sourceName,
235
+ parentSessionId: opts.parentSessionId || null,
236
+ clientHostPid: opts.clientHostPid,
237
+ skipRoleReminder: isPoolC,
238
+ schemaAllowedTools: resolveHiddenRoleSchemaAllowedTools(hidden),
239
+ taskType: opts.taskType,
240
+ maxLoopIterations: opts.maxLoopIterations,
241
+ });
242
+ // Diagnostic — dump the actual tool names exposed to this LLM call,
243
+ // visible from the worker log instead of being hidden behind a
244
+ // count-only "tools=N" line.
245
+ try {
246
+ const _toolNames = (session.tools || []).map((t) => t?.name).filter(Boolean);
247
+ process.stderr.write(`[bridge-llm] role=${role} tool-list (${_toolNames.length}): ${_toolNames.join(',')}\n`);
248
+ } catch { /* best-effort diagnostic */ }
249
+
250
+ await updateSessionStatus(session.id, 'running');
251
+ // Parent→child abort cascade: when opts.parentSignal (factory) or
252
+ // callParentSignal (per-call) fires, abort the sub-session's own
253
+ // controller so the provider call tears down promptly. Best-effort:
254
+ // if the session/manager import is unavailable we fall back silently.
255
+ const _managerMod = await import('../session/manager.mjs').catch(() => null);
256
+ const _linkSignal = _managerMod?.linkParentSignalToSession;
257
+ const _getLastProgressAt = _managerMod?.getSessionLastProgressAt;
258
+ if (_linkSignal) {
259
+ if (opts.parentSignal instanceof AbortSignal) {
260
+ try { _linkSignal(session.id, opts.parentSignal); } catch { /* ignore */ }
261
+ }
262
+ if (callParentSignal instanceof AbortSignal) {
263
+ try { _linkSignal(session.id, callParentSignal); } catch { /* ignore */ }
264
+ }
265
+ }
266
+ // Idle-progress watchdog. A "session that's actually doing work"
267
+ // refreshes one of three runtime fields every chunk/tool-call.
268
+ // If none of those advance for `idleMs`, we treat the provider call
269
+ // as stuck (network black-hole, nested-spawn deadlock, etc.) and
270
+ // abort the sub-session controller. Routine long runs — multi-tool
271
+ // chains, slow streams — stay untouched because at least one of
272
+ // the three fields keeps ticking. `0` or negative disables the
273
+ // watchdog entirely; default is 120s.
274
+ const _idleMs = Number.isFinite(callIdleTimeoutMs)
275
+ ? callIdleTimeoutMs
276
+ : (Number.isFinite(opts.idleTimeoutMs) ? opts.idleTimeoutMs : 120_000);
277
+ const _idleController = (_idleMs > 0 && _linkSignal) ? new AbortController() : null;
278
+ if (_idleController) {
279
+ try { _linkSignal(session.id, _idleController.signal); } catch { /* ignore */ }
280
+ }
281
+ const _idleTimer = (_idleController && typeof _getLastProgressAt === 'function')
282
+ ? setInterval(() => {
283
+ const last = _getLastProgressAt(session.id);
284
+ if (!last) return; // ask hasn't started writing progress yet
285
+ if (Date.now() - last > _idleMs) {
286
+ try {
287
+ _idleController.abort(new Error(`idle timeout exceeded (${_idleMs}ms)`));
288
+ } catch { /* ignore */ }
289
+ }
290
+ }, 1000)
291
+ : null;
292
+ if (_idleTimer && typeof _idleTimer.unref === 'function') _idleTimer.unref();
293
+ let terminalStatus = 'idle';
294
+ process.stderr.write(`[bridge-llm] role=${role} preset=${presetName} model=${preset.model} provider=${preset.provider} session=${session.id}\n`);
295
+ const _bridgeT0 = Date.now();
296
+ try {
297
+ const result = await askSession(session.id, finalPrompt, null, null, cwd);
298
+ process.stderr.write(`[bridge-llm] role=${role} session=${session.id} elapsed=${Date.now() - _bridgeT0}ms\n`);
299
+ const raw = result?.content || '';
300
+ // Brief cap. Bridge role answers (explore/recall/search)
301
+ // occasionally balloon to 8-10k token walls that then ride in the
302
+ // parent Lead's context for the rest of the turn. A 3000-token
303
+ // (~12 KB) ceiling trims the long tail while leaving the vast
304
+ // majority of answers untouched. Opt-out via `brief:false` when
305
+ // the caller explicitly wants the full synthesis.
306
+ if (opts.brief === false) {
307
+ try { closeSession(session.id, 'ephemeral-done'); } catch { /* ignore */ }
308
+ return raw;
309
+ }
310
+ const out = applyBriefCap(raw);
311
+ try { closeSession(session.id, 'ephemeral-done'); } catch { /* ignore */ }
312
+ return out;
313
+ } catch (err) {
314
+ terminalStatus = 'error';
315
+ try { closeSession(session.id, 'ephemeral-error'); } catch { /* ignore */ }
316
+ throw err;
317
+ } finally {
318
+ if (_idleTimer) {
319
+ try { clearInterval(_idleTimer); } catch { /* ignore */ }
320
+ }
321
+ // Always flip out of 'running' before returning so the sweep never
322
+ // leaves a stateless Pool C session stuck in 'running' when the
323
+ // try/catch falls through in unexpected ways.
324
+ try { await updateSessionStatus(session.id, terminalStatus); } catch { /* ignore */ }
325
+ }
326
+ };
327
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Smart Bridge — Cache Observability
3
+ *
4
+ * Normalizes raw provider usage objects into a unified CacheObservation
5
+ * for cross-provider cache performance comparison.
6
+ *
7
+ * @typedef {Object} CacheObservation
8
+ * @property {string} provider — provider name
9
+ * @property {number} cache_hit_tokens — tokens read from cache
10
+ * @property {number} cache_write_tokens — tokens written to cache
11
+ * @property {string|null} cache_ttl_tier — '5m' | '1h' | null
12
+ * @property {number|null} cache_hit_ratio — 0.0–1.0, null if not computable
13
+ * @property {boolean} cache_observable — whether the provider reports cache metrics
14
+ */
15
+
16
+ /**
17
+ * Normalize raw provider usage into a CacheObservation.
18
+ *
19
+ * @param {string} provider — one of the 10 supported providers
20
+ * @param {object} rawUsage — raw usage object from provider response
21
+ * @returns {CacheObservation}
22
+ */
23
+ export function normalizeUsage(provider, rawUsage) {
24
+ if (!rawUsage) {
25
+ return _empty(provider, false);
26
+ }
27
+
28
+ switch (provider) {
29
+ case 'anthropic':
30
+ case 'anthropic-oauth':
31
+ return _normalizeAnthropic(provider, rawUsage);
32
+
33
+ case 'openai':
34
+ case 'openai-oauth':
35
+ case 'xai':
36
+ case 'grok-oauth':
37
+ // xAI Responses usage mirrors OpenAI's shape:
38
+ // input_tokens_details.cached_tokens for prefix cache hits.
39
+ return _normalizeOpenai(provider, rawUsage);
40
+
41
+ case 'gemini':
42
+ return _normalizeGemini(provider, rawUsage);
43
+
44
+ case 'deepseek':
45
+ return _normalizeOpenai(provider, rawUsage);
46
+
47
+ case 'ollama':
48
+ case 'lmstudio':
49
+ return _empty(provider, false);
50
+
51
+ default:
52
+ return _empty(provider, false);
53
+ }
54
+ }
55
+
56
+ // --- Per-provider extractors ---
57
+
58
+ function _normalizeAnthropic(provider, u) {
59
+ const hit = _num(u.cache_read_input_tokens);
60
+ const write = _num(u.cache_creation_input_tokens);
61
+ const input = _num(u.input_tokens);
62
+
63
+ // TTL tier: pick from cache_creation breakdown
64
+ let ttlTier = null;
65
+ if (u.cache_creation) {
66
+ if (_num(u.cache_creation.ephemeral_1h_input_tokens) > 0) ttlTier = '1h';
67
+ else if (_num(u.cache_creation.ephemeral_5m_input_tokens) > 0) ttlTier = '5m';
68
+ }
69
+
70
+ const total = hit + input + write;
71
+ const ratio = total > 0 ? hit / total : null;
72
+
73
+ return {
74
+ provider,
75
+ cache_hit_tokens: hit,
76
+ cache_write_tokens: write,
77
+ cache_ttl_tier: ttlTier,
78
+ cache_hit_ratio: ratio,
79
+ cache_observable: true,
80
+ };
81
+ }
82
+
83
+ function _normalizeOpenai(provider, u) {
84
+ // OpenAI: cached_tokens in prompt_tokens_details or input_tokens_details
85
+ const hit = _num(u.prompt_tokens_details?.cached_tokens)
86
+ || _num(u.input_tokens_details?.cached_tokens)
87
+ || 0;
88
+ const input = _num(u.prompt_tokens) || _num(u.input_tokens) || 0;
89
+ const ratio = input > 0 ? hit / input : null;
90
+
91
+ return {
92
+ provider,
93
+ cache_hit_tokens: hit,
94
+ cache_write_tokens: 0,
95
+ cache_ttl_tier: null,
96
+ cache_hit_ratio: ratio,
97
+ cache_observable: true,
98
+ };
99
+ }
100
+
101
+ function _normalizeGemini(provider, u) {
102
+ // Gemini: provider-normalized usage may already include fallback-adjusted
103
+ // cachedTokens. Raw SDK/REST usage reports cachedContentTokenCount.
104
+ const meta = u.usageMetadata || u;
105
+ const hit = _num(meta.cachedTokens)
106
+ || _num(meta.cached_tokens)
107
+ || _num(meta.effectiveCachedTokens)
108
+ || _num(meta.cachedContentTokenCount)
109
+ || _num(meta.cachedFallbackTokens)
110
+ || 0;
111
+ const observable = 'cachedTokens' in (meta || {})
112
+ || 'cached_tokens' in (meta || {})
113
+ || 'effectiveCachedTokens' in (meta || {})
114
+ || 'cachedContentTokenCount' in (meta || {})
115
+ || 'cachedFallbackTokens' in (meta || {});
116
+ const input = _num(meta.promptTokens)
117
+ || _num(meta.inputTokens)
118
+ || _num(meta.input_tokens)
119
+ || _num(meta.promptTokenCount)
120
+ || _num(meta.totalTokenCount)
121
+ || 0;
122
+ const ratio = input > 0 ? hit / input : null;
123
+
124
+ return {
125
+ provider,
126
+ cache_hit_tokens: hit,
127
+ cache_write_tokens: 0,
128
+ cache_ttl_tier: null,
129
+ cache_hit_ratio: ratio,
130
+ cache_observable: observable,
131
+ };
132
+ }
133
+
134
+ // --- Helpers ---
135
+
136
+ function _empty(provider, observable) {
137
+ return {
138
+ provider,
139
+ cache_hit_tokens: 0,
140
+ cache_write_tokens: 0,
141
+ cache_ttl_tier: null,
142
+ cache_hit_ratio: null,
143
+ cache_observable: observable,
144
+ };
145
+ }
146
+
147
+ function _num(v) {
148
+ const n = Number(v);
149
+ return Number.isFinite(n) ? n : 0;
150
+ }