mixdog 0.9.2 → 0.9.4

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 (417) hide show
  1. package/package.json +8 -3
  2. package/scripts/anthropic-maxtokens-test.mjs +119 -0
  3. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  4. package/scripts/bench/lead-review-tasks.json +20 -0
  5. package/scripts/bench/r4-mixed-tasks.json +20 -0
  6. package/scripts/bench/review-tasks.json +20 -0
  7. package/scripts/bench/round-codex.json +114 -0
  8. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  9. package/scripts/bench/round-mixdog-lead.json +269 -0
  10. package/scripts/bench/round-mixdog.json +126 -0
  11. package/scripts/bench/round-r10-bigsample.json +679 -0
  12. package/scripts/bench/round-r11-codexalign.json +257 -0
  13. package/scripts/bench/round-r4-codex.json +114 -0
  14. package/scripts/bench/round-r4-mixed.json +225 -0
  15. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  16. package/scripts/bench/round-r6-codex.json +114 -0
  17. package/scripts/bench/round-r6-solo.json +257 -0
  18. package/scripts/bench/round-r7-full.json +254 -0
  19. package/scripts/bench/round-r8-fulldefault.json +255 -0
  20. package/scripts/bench-run.mjs +215 -29
  21. package/scripts/build-tui.mjs +13 -1
  22. package/scripts/explore-bench.mjs +124 -0
  23. package/scripts/freevar-smoke.mjs +95 -0
  24. package/scripts/hook-bus-test.mjs +191 -0
  25. package/scripts/internal-comms-bench.mjs +1 -0
  26. package/scripts/internal-comms-smoke.mjs +10 -9
  27. package/scripts/mouse-probe.mjs +45 -0
  28. package/scripts/output-style-bench.mjs +13 -6
  29. package/scripts/output-style-smoke.mjs +4 -4
  30. package/scripts/path-suffix-test.mjs +57 -0
  31. package/scripts/provider-toolcall-test.mjs +7 -3
  32. package/scripts/recall-bench.mjs +207 -0
  33. package/scripts/recall-usecase-cases.json +18 -0
  34. package/scripts/recall-usecase-probe.json +6 -0
  35. package/scripts/session-bench.mjs +152 -6
  36. package/scripts/tool-smoke.mjs +30 -67
  37. package/scripts/tui-render-smoke.mjs +90 -0
  38. package/scripts/webhook-smoke.mjs +208 -0
  39. package/src/agents/debugger/AGENT.md +5 -2
  40. package/src/agents/heavy-worker/AGENT.md +21 -11
  41. package/src/agents/maintainer/AGENT.md +4 -0
  42. package/src/agents/reviewer/AGENT.md +3 -2
  43. package/src/agents/worker/AGENT.md +21 -11
  44. package/src/lib/rules-builder.cjs +4 -0
  45. package/src/mixdog-session-runtime.mjs +933 -3731
  46. package/src/output-styles/default.md +34 -9
  47. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  48. package/src/output-styles/minimal.md +4 -1
  49. package/src/output-styles/simple.md +22 -7
  50. package/src/repl.mjs +5 -5
  51. package/src/rules/agent/00-common.md +2 -0
  52. package/src/rules/agent/30-explorer.md +8 -11
  53. package/src/rules/lead/lead-brief.md +12 -0
  54. package/src/rules/lead/lead-tool.md +2 -9
  55. package/src/rules/shared/01-tool.md +11 -5
  56. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  57. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  58. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  59. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  60. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  61. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  62. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  63. package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
  64. package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
  66. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
  70. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  71. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
  72. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
  73. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  74. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  75. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
  78. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
  79. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
  80. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  81. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  82. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
  83. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
  92. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  96. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  98. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
  99. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
  100. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  101. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  102. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  103. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  107. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  108. package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
  109. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
  110. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  111. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
  112. package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
  113. package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
  114. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
  115. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  116. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  117. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
  118. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  120. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
  121. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  122. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
  123. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
  124. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
  125. package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
  126. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  127. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
  128. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
  129. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
  130. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
  131. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  132. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  133. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  137. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  138. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  141. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
  142. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
  143. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  145. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
  146. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
  148. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  149. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  151. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
  152. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
  155. package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
  156. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
  157. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
  158. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
  159. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
  160. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
  161. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
  162. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  163. package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
  164. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
  165. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
  166. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
  167. package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
  168. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
  169. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
  170. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  171. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
  172. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  173. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
  174. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  175. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  176. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  177. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  178. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  179. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  180. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  181. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  182. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  183. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  184. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
  185. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
  186. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  187. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
  188. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  189. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  190. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  191. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  192. package/src/runtime/channels/backends/discord.mjs +12 -292
  193. package/src/runtime/channels/index.mjs +241 -894
  194. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  195. package/src/runtime/channels/lib/boot-profile.mjs +23 -0
  196. package/src/runtime/channels/lib/crash-log.mjs +106 -0
  197. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  198. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  199. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  200. package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
  201. package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
  202. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  203. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  204. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  205. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  206. package/src/runtime/channels/lib/telegram-format.mjs +19 -22
  207. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  208. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  209. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  210. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  211. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  212. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  213. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  214. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  215. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  216. package/src/runtime/channels/lib/webhook.mjs +36 -570
  217. package/src/runtime/channels/lib/whisper-language.mjs +42 -0
  218. package/src/runtime/channels/tool-defs.mjs +11 -130
  219. package/src/runtime/memory/index.mjs +258 -2050
  220. package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
  221. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  222. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  223. package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
  224. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  225. package/src/runtime/memory/lib/http-wire.mjs +57 -0
  226. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  227. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  228. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  229. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  230. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  231. package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
  232. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  233. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  234. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
  235. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  236. package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
  237. package/src/runtime/memory/lib/memory.mjs +20 -0
  238. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  239. package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
  240. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  241. package/src/runtime/memory/lib/recall-format.mjs +238 -0
  242. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  243. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  244. package/src/runtime/memory/tool-defs.mjs +6 -14
  245. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  246. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  247. package/src/runtime/search/lib/web-tools.mjs +24 -602
  248. package/src/runtime/shared/abort-controller.mjs +1 -1
  249. package/src/runtime/shared/atomic-file.mjs +26 -1
  250. package/src/runtime/shared/background-tasks.mjs +2 -3
  251. package/src/runtime/shared/buffered-appender.mjs +149 -0
  252. package/src/runtime/shared/launcher-control.mjs +2 -2
  253. package/src/runtime/shared/task-notification-envelope.mjs +98 -0
  254. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  255. package/src/runtime/shared/tool-primitives.mjs +308 -0
  256. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  257. package/src/runtime/shared/tool-surface.mjs +80 -898
  258. package/src/runtime/shared/transcript-writer.mjs +52 -2
  259. package/src/runtime/shared/update-checker.mjs +7 -4
  260. package/src/session-runtime/config-helpers.mjs +291 -0
  261. package/src/session-runtime/config-lifecycle.mjs +232 -0
  262. package/src/session-runtime/cwd-plugins.mjs +226 -0
  263. package/src/session-runtime/effort.mjs +128 -0
  264. package/src/session-runtime/fs-utils.mjs +10 -0
  265. package/src/session-runtime/mcp-glue.mjs +177 -0
  266. package/src/session-runtime/model-capabilities.mjs +130 -0
  267. package/src/session-runtime/model-recency.mjs +111 -0
  268. package/src/session-runtime/native-search.mjs +247 -0
  269. package/src/session-runtime/output-styles.mjs +126 -0
  270. package/src/session-runtime/plugin-mcp.mjs +114 -0
  271. package/src/session-runtime/prewarm.mjs +142 -0
  272. package/src/session-runtime/provider-models.mjs +278 -0
  273. package/src/session-runtime/provider-usage.mjs +120 -0
  274. package/src/session-runtime/quick-model-rows.mjs +170 -0
  275. package/src/session-runtime/quick-search-models.mjs +46 -0
  276. package/src/session-runtime/session-hooks.mjs +93 -0
  277. package/src/session-runtime/session-text.mjs +100 -0
  278. package/src/session-runtime/settings-api.mjs +319 -0
  279. package/src/session-runtime/statusline-route.mjs +35 -0
  280. package/src/session-runtime/tool-catalog.mjs +720 -0
  281. package/src/session-runtime/tool-defs.mjs +84 -0
  282. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  283. package/src/session-runtime/workflow.mjs +358 -0
  284. package/src/standalone/agent-tool/helpers.mjs +237 -0
  285. package/src/standalone/agent-tool/notify.mjs +107 -0
  286. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  287. package/src/standalone/agent-tool/render.mjs +152 -0
  288. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  289. package/src/standalone/agent-tool.mjs +155 -677
  290. package/src/standalone/channel-worker.mjs +7 -9
  291. package/src/standalone/explore-tool.mjs +40 -12
  292. package/src/standalone/hook-bus/config.mjs +207 -0
  293. package/src/standalone/hook-bus/constants.mjs +90 -0
  294. package/src/standalone/hook-bus/handlers.mjs +481 -0
  295. package/src/standalone/hook-bus/payload.mjs +31 -0
  296. package/src/standalone/hook-bus/rules.mjs +77 -0
  297. package/src/standalone/hook-bus.mjs +110 -746
  298. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  299. package/src/standalone/opencode-go-login.mjs +125 -0
  300. package/src/standalone/provider-admin.mjs +15 -19
  301. package/src/standalone/usage-dashboard.mjs +3 -1
  302. package/src/tui/App.jsx +1163 -7571
  303. package/src/tui/app/app-format.mjs +206 -0
  304. package/src/tui/app/channel-pickers.mjs +510 -0
  305. package/src/tui/app/clipboard.mjs +67 -0
  306. package/src/tui/app/core-memory-picker.mjs +210 -0
  307. package/src/tui/app/extension-pickers.mjs +506 -0
  308. package/src/tui/app/input-parsers.mjs +193 -0
  309. package/src/tui/app/maintenance-pickers.mjs +324 -0
  310. package/src/tui/app/model-options.mjs +330 -0
  311. package/src/tui/app/model-picker.mjs +365 -0
  312. package/src/tui/app/onboarding-steps.mjs +400 -0
  313. package/src/tui/app/project-picker.mjs +247 -0
  314. package/src/tui/app/provider-setup-picker.mjs +580 -0
  315. package/src/tui/app/resume-picker.mjs +55 -0
  316. package/src/tui/app/route-pickers.mjs +419 -0
  317. package/src/tui/app/settings-picker.mjs +490 -0
  318. package/src/tui/app/slash-commands.mjs +101 -0
  319. package/src/tui/app/slash-dispatch.mjs +427 -0
  320. package/src/tui/app/text-layout.mjs +46 -0
  321. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  322. package/src/tui/app/transcript-window.mjs +671 -0
  323. package/src/tui/app/use-mouse-input.mjs +460 -0
  324. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  325. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  326. package/src/tui/app/use-transcript-window.mjs +589 -0
  327. package/src/tui/components/ConfirmBar.jsx +1 -1
  328. package/src/tui/components/Picker.jsx +32 -4
  329. package/src/tui/components/PromptInput.jsx +259 -80
  330. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  331. package/src/tui/components/StatusLine.jsx +63 -12
  332. package/src/tui/components/TextEntryPanel.jsx +11 -0
  333. package/src/tui/components/ToolExecution.jsx +56 -588
  334. package/src/tui/components/TranscriptItem.jsx +105 -0
  335. package/src/tui/components/UsagePanel.jsx +18 -4
  336. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  337. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  338. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  339. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  340. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  341. package/src/tui/components/tool-output-format.mjs +2 -2
  342. package/src/tui/display-width.mjs +20 -3
  343. package/src/tui/dist/index.mjs +18034 -17188
  344. package/src/tui/engine/agent-envelope.mjs +296 -0
  345. package/src/tui/engine/agent-job-feed.mjs +133 -0
  346. package/src/tui/engine/boot-profile.mjs +21 -0
  347. package/src/tui/engine/labels.mjs +67 -0
  348. package/src/tui/engine/notice-text.mjs +112 -0
  349. package/src/tui/engine/notification-plan.mjs +76 -0
  350. package/src/tui/engine/queue-helpers.mjs +161 -0
  351. package/src/tui/engine/render-timing.mjs +17 -0
  352. package/src/tui/engine/session-stats.mjs +46 -0
  353. package/src/tui/engine/tool-approval.mjs +94 -0
  354. package/src/tui/engine/tool-call-fields.mjs +23 -0
  355. package/src/tui/engine/tool-card-results.mjs +234 -0
  356. package/src/tui/engine/tool-result-status.mjs +135 -0
  357. package/src/tui/engine/tool-result-text.mjs +126 -0
  358. package/src/tui/engine.mjs +405 -1385
  359. package/src/tui/figures.mjs +5 -0
  360. package/src/tui/index.jsx +105 -0
  361. package/src/tui/input-editing.mjs +60 -10
  362. package/src/tui/keyboard-protocol.mjs +2 -2
  363. package/src/tui/lib/voice-recorder.mjs +35 -19
  364. package/src/tui/markdown/format-token.mjs +11 -9
  365. package/src/tui/paste-attachments.mjs +38 -0
  366. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  367. package/src/tui/theme.mjs +6 -0
  368. package/src/tui/themes/base.mjs +2 -2
  369. package/src/tui/themes/kanagawa.mjs +4 -4
  370. package/src/tui/themes/teal.mjs +4 -5
  371. package/src/tui/themes/utils.mjs +1 -1
  372. package/src/ui/statusline-agents.mjs +213 -0
  373. package/src/ui/statusline-format.mjs +146 -0
  374. package/src/ui/statusline-segments.mjs +148 -0
  375. package/src/ui/statusline.mjs +77 -462
  376. package/src/ui/tool-card.mjs +0 -1
  377. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  378. package/src/workflows/default/WORKFLOW.md +16 -9
  379. package/src/workflows/sequential/WORKFLOW.md +16 -11
  380. package/src/workflows/solo/WORKFLOW.md +5 -1
  381. package/vendor/ink/build/display-width.js +19 -3
  382. package/vendor/ink/build/ink.js +103 -6
  383. package/vendor/ink/build/log-update.js +17 -3
  384. package/vendor/ink/build/wrap-text.js +125 -0
  385. package/scripts/_test-folder-dialog.mjs +0 -30
  386. package/scripts/fix-brief-fn.mjs +0 -35
  387. package/scripts/fix-format-tool-surface.mjs +0 -24
  388. package/scripts/fix-tool-exec-visible.mjs +0 -42
  389. package/scripts/patch-agent-brief.mjs +0 -48
  390. package/scripts/patch-app.mjs +0 -21
  391. package/scripts/patch-app2.mjs +0 -18
  392. package/scripts/patch-dist-brief.mjs +0 -96
  393. package/scripts/patch-tool-exec.mjs +0 -70
  394. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  395. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  396. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  397. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  398. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  399. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  400. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  401. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  402. package/src/standalone/agent-task-status.test.mjs +0 -76
  403. package/src/tui/components/tool-output-format.test.mjs +0 -399
  404. package/src/tui/display-width.test.mjs +0 -35
  405. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  406. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  407. package/src/tui/markdown/format-token.test.mjs +0 -354
  408. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  409. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  410. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  411. package/src/tui/prompt-history-store.test.mjs +0 -52
  412. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  413. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  414. package/src/ui/markdown.test.mjs +0 -70
  415. package/src/ui/statusline-context-label.test.mjs +0 -15
  416. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  417. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -0,0 +1,303 @@
1
+ // Session tool schema resolution + permission narrowing, extracted verbatim
2
+ // from manager.mjs. Behavior-preserving: same toolset ids, same ordering,
3
+ // same dedup, same fail-closed permission intersection.
4
+ import { getMcpTools } from '../../mcp/client.mjs';
5
+ import { getInternalTools } from '../../internal-tools.mjs';
6
+ import { BUILTIN_TOOLS } from '../../tools/builtin/builtin-tools.mjs';
7
+ import { PATCH_TOOL_DEFS } from '../../tools/patch-tool-defs.mjs';
8
+ import { CODE_GRAPH_TOOL_DEFS } from '../../tools/code-graph-tool-defs.mjs';
9
+ import { buildSkillToolDefs } from '../../context/collect.mjs';
10
+ import { getHiddenAgent, listHiddenAgentNames } from '../../internal-agents.mjs';
11
+
12
+ // Merge externally-connected MCP tools with the plugin's in-process tools
13
+ // (registered by agent's toolExecutor adapter). Internal tools are exposed
14
+ // under their bare names — no mcp__ prefix, since the dispatcher in
15
+ // server.mjs handles them directly without a transport.
16
+ // Sorted deterministically by name — protects BP_1 hash stability from
17
+ // listTools() ordering churn. Anthropic / OpenAI / Gemini all hash the
18
+ // tools array verbatim, so any reorder rewrites the prefix.
19
+ // No cache: getMcpTools() and getInternalTools() are O(n) in-memory reads;
20
+ // the sort overhead on ~30 tools is negligible.
21
+ function _getMcpTools() {
22
+ const mcp = getMcpTools() || [];
23
+ const internalRaw = getInternalTools() || [];
24
+ const internal = internalRaw.map(t => ({
25
+ name: t.name,
26
+ description: typeof t.description === 'string' ? t.description : '',
27
+ inputSchema: t.inputSchema || { type: 'object', properties: {} },
28
+ // Keep annotations so the permission filter / role invariants can
29
+ // tell read-only from write-capable internal tools, and so
30
+ // agentHidden can be read during deny filtering.
31
+ annotations: t.annotations || {},
32
+ }));
33
+ return [...mcp, ...internal].sort((a, b) => {
34
+ const an = a?.name || '';
35
+ const bn = b?.name || '';
36
+ return an < bn ? -1 : an > bn ? 1 : 0;
37
+ });
38
+ }
39
+
40
+ const SESSION_ROUTE_TOOL_ORDER = [
41
+ 'code_graph',
42
+ 'find',
43
+ 'glob',
44
+ 'list',
45
+ 'grep',
46
+ 'read',
47
+ 'apply_patch',
48
+ 'shell',
49
+ 'task',
50
+ ];
51
+ const SESSION_ROUTE_TOOL_RANK = new Map(SESSION_ROUTE_TOOL_ORDER.map((name, index) => [name, index]));
52
+ const FILESYSTEM_TOOL_NAMES = new Set([
53
+ 'code_graph',
54
+ 'find',
55
+ 'glob',
56
+ 'list',
57
+ 'grep',
58
+ 'read',
59
+ 'apply_patch',
60
+ ]);
61
+ const READONLY_TOOL_NAMES = new Set([
62
+ 'code_graph',
63
+ 'find',
64
+ 'glob',
65
+ 'list',
66
+ 'grep',
67
+ 'read',
68
+ ]);
69
+
70
+ const AGENT_STRING_PERMISSION_READ_ALLOW = Object.freeze([
71
+ 'code_graph',
72
+ 'find',
73
+ 'glob',
74
+ 'list',
75
+ 'grep',
76
+ 'read',
77
+ 'explore',
78
+ 'search',
79
+ 'web_fetch',
80
+ 'Skill',
81
+ ]);
82
+
83
+ function stringToolPermissionAllowList(toolPermission) {
84
+ if (toolPermission === 'read') return AGENT_STRING_PERMISSION_READ_ALLOW;
85
+ if (toolPermission === 'read-write') return AGENT_STRING_PERMISSION_READ_WRITE_ALLOW;
86
+ if (toolPermission === 'none') return [];
87
+ return null;
88
+ }
89
+
90
+ const AGENT_STRING_PERMISSION_READ_WRITE_ALLOW = Object.freeze([
91
+ 'code_graph',
92
+ 'find',
93
+ 'glob',
94
+ 'list',
95
+ 'grep',
96
+ 'read',
97
+ 'apply_patch',
98
+ 'explore',
99
+ 'search',
100
+ 'web_fetch',
101
+ 'Skill',
102
+ ]);
103
+
104
+ export function applyToolPermissionNarrowing(tools, toolPermission, warnRole = null) {
105
+ if (toolPermission === 'none') return [];
106
+ const allowList = stringToolPermissionAllowList(toolPermission);
107
+ if (allowList) {
108
+ const allowSet = new Set(allowList.map((n) => String(n).toLowerCase()));
109
+ return tools.filter((t) => allowSet.has(String(t?.name || '').toLowerCase()));
110
+ }
111
+ if (toolPermission && typeof toolPermission === 'object') {
112
+ const allowSet = Array.isArray(toolPermission.allow) && toolPermission.allow.length > 0
113
+ ? new Set(toolPermission.allow.map(n => String(n).toLowerCase()))
114
+ : null;
115
+ const denySet = Array.isArray(toolPermission.deny) && toolPermission.deny.length > 0
116
+ ? new Set(toolPermission.deny.map(n => String(n).toLowerCase()))
117
+ : null;
118
+ if (allowSet || denySet) {
119
+ const filtered = tools.filter(t => {
120
+ const name = String(t?.name || '').toLowerCase();
121
+ if (denySet && denySet.has(name)) return false;
122
+ if (allowSet && !allowSet.has(name)) return false;
123
+ return true;
124
+ });
125
+ if (filtered.length === 0) {
126
+ process.stderr.write(`[session] WARN: role permission intersection produced 0 tools — failing closed (role=${warnRole || 'unknown'})\n`);
127
+ }
128
+ return filtered;
129
+ }
130
+ }
131
+ return tools;
132
+ }
133
+
134
+ function recursiveWrapperToolNameForPublicAgent(agent) {
135
+ if (!agent) return null;
136
+ const key = String(agent).trim();
137
+ if (key === 'explore') return 'explore';
138
+ for (const hiddenName of listHiddenAgentNames()) {
139
+ const def = getHiddenAgent(hiddenName);
140
+ const invokedBy = typeof def?.invokedBy === 'string' ? def.invokedBy.trim() : '';
141
+ if (hiddenName === key && invokedBy) return invokedBy;
142
+ if (invokedBy && invokedBy === key) return invokedBy;
143
+ }
144
+ return null;
145
+ }
146
+
147
+ export function finalizeSessionToolList(tools, {
148
+ schemaAllowedTools = null,
149
+ disallowedTools = null,
150
+ ownerIsAgent = false,
151
+ resolvedAgent = null,
152
+ } = {}) {
153
+ let out = Array.isArray(tools) ? tools : [];
154
+ const hasCallerAllow = Array.isArray(schemaAllowedTools);
155
+ if (hasCallerAllow) {
156
+ const allowSet = new Set(schemaAllowedTools.map(n => String(n).toLowerCase()));
157
+ out = out.filter(t => allowSet.has(String(t?.name || '').toLowerCase()));
158
+ }
159
+ const callerDeny = Array.isArray(disallowedTools) ? disallowedTools.map(n => String(n)) : [];
160
+ if (callerDeny.length) {
161
+ const denySet = new Set(callerDeny.map(n => n.toLowerCase()));
162
+ out = out.filter(t => !denySet.has(String(t?.name || '').toLowerCase()));
163
+ }
164
+ const recursiveDeny = ownerIsAgent ? recursiveWrapperToolNameForPublicAgent(resolvedAgent) : null;
165
+ if (recursiveDeny) {
166
+ const deny = recursiveDeny.toLowerCase();
167
+ out = out.filter(t => String(t?.name || '').toLowerCase() !== deny);
168
+ }
169
+ if (ownerIsAgent) {
170
+ out = out.filter(t => !t?.annotations?.agentHidden);
171
+ out = orderSessionTools(out);
172
+ }
173
+ return out;
174
+ }
175
+
176
+ function orderSessionTools(tools) {
177
+ return tools.map((tool, index) => ({ tool, index }))
178
+ .sort((a, b) => {
179
+ const ar = SESSION_ROUTE_TOOL_RANK.get(a.tool?.name) ?? 10_000;
180
+ const br = SESSION_ROUTE_TOOL_RANK.get(b.tool?.name) ?? 10_000;
181
+ if (ar !== br) return ar - br;
182
+ return a.index - b.index;
183
+ })
184
+ .map((entry) => entry.tool);
185
+ }
186
+
187
+ const ALL_BUILTIN_SESSION_TOOLS = orderSessionTools(_dedupByName([
188
+ ...BUILTIN_TOOLS,
189
+ ...PATCH_TOOL_DEFS,
190
+ ...CODE_GRAPH_TOOL_DEFS,
191
+ ]));
192
+
193
+ export function resolveSessionTools(toolSpec, skills, { ownerIsAgentSession = false } = {}) {
194
+ const mcp = _getMcpTools();
195
+ // Agent sessions freeze the skill meta-tool into the schema
196
+ // unconditionally — concrete skill resolution is cwd-scoped at tool-call
197
+ // time (loop.mjs), so the schema bytes stay bit-identical across roles /
198
+ // cwds and the provider cache shard does not fragment.
199
+ const skillTools = buildSkillToolDefs(skills, { ownerIsAgentSession });
200
+ return _computeBaseTools(toolSpec, mcp, skillTools);
201
+ }
202
+
203
+ export function previewSessionTools(toolSpec, skills = [], options = {}) {
204
+ return resolveSessionTools(toolSpec, skills, options);
205
+ }
206
+
207
+ // Dedup by name, first occurrence wins. BUILTIN_TOOLS is passed in ahead
208
+ // of the MCP-registered internal tools so plugin-side definitions take
209
+ // precedence when both surfaces declare the same name (e.g. read / grep / glob).
210
+ // Without this merge, Anthropic rejected the request with
211
+ // "tools: Tool names must be unique" and the orchestrator burned up to
212
+ // 20 iterations retrying before the final answer landed.
213
+ function _dedupByName(tools) {
214
+ const seen = new Map();
215
+ for (const t of tools) {
216
+ const n = t?.name;
217
+ if (!n || seen.has(n)) continue;
218
+ seen.set(n, t);
219
+ }
220
+ return [...seen.values()];
221
+ }
222
+
223
+ // Agent visibility is declared per-tool via annotations.agentHidden.
224
+ // Tools with agentHidden:true are stripped from agent sessions at schema
225
+ // build time (see deny filtering below). No code-level name list needed.
226
+
227
+ function _computeBaseTools(toolSpec, mcp, skillTools) {
228
+ if (Array.isArray(toolSpec)) {
229
+ if (toolSpec.length === 0) {
230
+ // Explicit "no tools" — skill meta tools still travel so the model
231
+ // can at least discover and invoke skills if that is the one
232
+ // dynamic surface the profile retains.
233
+ return _dedupByName([...skillTools]);
234
+ }
235
+ if (toolSpec.includes('full')) {
236
+ return _dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]);
237
+ }
238
+ const byName = new Map();
239
+ const add = (tool) => { if (tool?.name && !byName.has(tool.name)) byName.set(tool.name, tool); };
240
+ const addMany = (arr) => { for (const t of arr) add(t); };
241
+ for (const tagRaw of toolSpec) {
242
+ const tag = String(tagRaw || '').trim();
243
+ switch (tag) {
244
+ case 'tools:filesystem':
245
+ addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => FILESYSTEM_TOOL_NAMES.has(t.name)));
246
+ break;
247
+ case 'tools:readonly':
248
+ addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => READONLY_TOOL_NAMES.has(t.name)));
249
+ break;
250
+ case 'tools:shell':
251
+ case 'tools:git':
252
+ case 'tools:analysis':
253
+ // Shell-class toolset. `tools:git` / `tools:analysis` exist so
254
+ // profile authors can name the intent (git workflows / data
255
+ // analysis) without inventing new toolset ids.
256
+ addMany(ALL_BUILTIN_SESSION_TOOLS.filter(t => t.name === 'shell' || t.name === 'task'));
257
+ break;
258
+ case 'tools:mcp':
259
+ addMany(mcp);
260
+ break;
261
+ case 'tools:search':
262
+ // Name-pattern match: picks up `search` and any future tool
263
+ // whose name contains `search`. `recall` and `explore` deliberately do NOT match
264
+ // — they need `tools:mcp` (full mcp surface) or their own
265
+ // toolset id if a role wants targeted retrieval. Public agent
266
+ // roles never reach the wrapper bodies regardless: see the
267
+ // isBlockedPublicWrapperCall guard in session/loop.mjs.
268
+ addMany(mcp.filter(t => /search/i.test(t?.name || '')));
269
+ break;
270
+ default:
271
+ process.stderr.write(`[session] unknown toolset id "${tag}" (profile.tools); skipping\n`);
272
+ }
273
+ }
274
+ return _dedupByName([...byName.values(), ...skillTools]);
275
+ }
276
+
277
+ switch (toolSpec) {
278
+ case 'mcp':
279
+ return _dedupByName([...mcp, ...skillTools]);
280
+ case 'readonly': {
281
+ const readTools = ALL_BUILTIN_SESSION_TOOLS.filter(t => READONLY_TOOL_NAMES.has(t.name));
282
+ return _dedupByName([...readTools, ...mcp, ...skillTools]);
283
+ }
284
+ case 'full':
285
+ default:
286
+ return _dedupByName([...ALL_BUILTIN_SESSION_TOOLS, ...mcp, ...skillTools]);
287
+ }
288
+ }
289
+
290
+ export function permissionFromToolSpec(toolSpec) {
291
+ if (toolSpec === 'readonly') return 'read';
292
+ if (toolSpec === 'mcp') return 'mcp';
293
+ if (Array.isArray(toolSpec)) {
294
+ const tags = new Set(toolSpec.map(t => String(t || '').trim()));
295
+ const hasWriteOrShell = tags.has('full')
296
+ || tags.has('tools:filesystem')
297
+ || tags.has('tools:shell')
298
+ || tags.has('tools:git')
299
+ || tags.has('tools:analysis');
300
+ if (tags.has('tools:readonly') && !hasWriteOrShell) return 'read';
301
+ }
302
+ return null;
303
+ }
@@ -0,0 +1,210 @@
1
+ // Incremental + terminal usage-metrics accounting for sessions.
2
+ // Extracted verbatim from manager.mjs (behavior-preserving). Pure helpers plus
3
+ // per-session idempotency tracking for incremental usage persistence.
4
+ //
5
+ // Runtime coupling is injected: persistIterationMetrics needs the live
6
+ // _runtimeState map (owned by manager.mjs) to read the in-memory session and
7
+ // flag usageMetricsTurnIncremental. manager.mjs wires it via configureUsageMetricsRuntime().
8
+ import { providerInputExcludesCache } from '../../providers/registry.mjs';
9
+ import { loadSession, saveSessionAsync } from '../store.mjs';
10
+
11
+ // Per-session idempotency tracking: sessionId → Set of seen
12
+ // turn:epoch:iteration:source keys.
13
+ const _metricSeenIter = new Map();
14
+
15
+ // Injected accessor for manager's _runtimeState. Defaults to a no-op lookup so
16
+ // the pure helpers remain usable (and unit-testable) before wiring.
17
+ let _getRuntimeEntry = () => null;
18
+
19
+ /** Wire the live runtime-state accessor from manager.mjs. */
20
+ export function configureUsageMetricsRuntime({ getRuntimeEntry } = {}) {
21
+ if (typeof getRuntimeEntry === 'function') _getRuntimeEntry = getRuntimeEntry;
22
+ }
23
+
24
+ /** Drop the per-session metric-idempotency Set (called on session close). */
25
+ export function dropMetricSeenState(sessionId) {
26
+ if (sessionId) _metricSeenIter.delete(sessionId);
27
+ }
28
+
29
+ /** Monotonic per-session ask/turn id for incremental usage idempotency. */
30
+ export function bumpUsageMetricsTurnId(session) {
31
+ if (!session || typeof session !== 'object') return 0;
32
+ const next = (Number(session.usageMetricsTurnId) || 0) + 1;
33
+ session.usageMetricsTurnId = next;
34
+ return next;
35
+ }
36
+
37
+ export function resolveUsageMetricsTurnId(session, delta = {}) {
38
+ if (delta.usageMetricsTurnId != null && Number.isFinite(Number(delta.usageMetricsTurnId))) {
39
+ return Number(delta.usageMetricsTurnId);
40
+ }
41
+ return Number(session?.usageMetricsTurnId) || 0;
42
+ }
43
+
44
+ /** Advance loop metrics epoch when agentLoop resets its iteration counter (post-compact). */
45
+ export function bumpUsageMetricsEpoch(session) {
46
+ if (!session || typeof session !== 'object') return 0;
47
+ const next = (Number(session.usageMetricsEpoch) || 0) + 1;
48
+ session.usageMetricsEpoch = next;
49
+ return next;
50
+ }
51
+
52
+ /**
53
+ * Resolve usage-metrics epoch for idempotency (exported for regression smoke).
54
+ * Prefers session.usageMetricsEpoch (bumped in loop on compact reset) and optional
55
+ * delta.usageMetricsEpoch; falls back to iteration regression when loop did not bump.
56
+ */
57
+ export function resolveUsageMetricsEpoch(session, delta = {}) {
58
+ if (!session) return 0;
59
+ let epoch = Number(session.usageMetricsEpoch) || 0;
60
+ if (delta.usageMetricsEpoch != null && Number.isFinite(Number(delta.usageMetricsEpoch))) {
61
+ epoch = Math.max(epoch, Number(delta.usageMetricsEpoch));
62
+ }
63
+ const idx = Number(delta.iterationIndex);
64
+ const prevLastIdx = typeof session.lastIterationIndex === 'number'
65
+ ? session.lastIterationIndex
66
+ : null;
67
+ if (
68
+ (delta.usageMetricsEpoch == null || !Number.isFinite(Number(delta.usageMetricsEpoch)))
69
+ && prevLastIdx !== null
70
+ && Number.isFinite(idx)
71
+ && idx < prevLastIdx
72
+ ) {
73
+ epoch += 1;
74
+ }
75
+ return epoch;
76
+ }
77
+
78
+ export function usageMetricsSourceKey(delta = {}) {
79
+ const raw = delta.source ?? delta.usageSource;
80
+ if (raw == null || raw === '') return 'provider_send';
81
+ return String(raw);
82
+ }
83
+
84
+ /** Idempotency key for incremental usage persistence (exported for regression smoke). */
85
+ export function usageMetricsIdempotencyKey(sessionId, session, delta = {}) {
86
+ const turnId = resolveUsageMetricsTurnId(session, delta);
87
+ const epoch = resolveUsageMetricsEpoch(session, delta);
88
+ const source = usageMetricsSourceKey(delta);
89
+ return `${sessionId}:${turnId}:${epoch}:${delta.iterationIndex}:${source}`;
90
+ }
91
+
92
+ export function uncachedInputTokensForProvider(provider, inputTokens, cachedReadTokens = 0, cacheWriteTokens = 0) {
93
+ const input = Number(inputTokens) || 0;
94
+ if (input <= 0) return 0;
95
+ // Anthropic-style providers report input_tokens excluding cache reads; OpenAI
96
+ // Responses/Gemini-style providers report input_tokens inclusive of cached
97
+ // prefix tokens. Keep both views so UI can show the real context footprint
98
+ // and the fresh/new token portion without mistaking cache hits for a cache
99
+ // break.
100
+ if (providerInputExcludesCache(provider)) return input + (Number(cacheWriteTokens) || 0);
101
+ return Math.max(input - (Number(cachedReadTokens) || 0) - (Number(cacheWriteTokens) || 0), 0);
102
+ }
103
+
104
+ /**
105
+ * Apply terminal ask usage to session totals. Skips lifetime totals when incremental
106
+ * per-iteration persistence already counted this turn (askSession path).
107
+ */
108
+ export function applyAskTerminalUsageTotals(session, result, options = {}) {
109
+ if (!session || !result?.usage) return;
110
+ const skipTotals = options.skipTotalsIfIncremental === true;
111
+ if (!skipTotals) {
112
+ const inputTokens = result.usage.inputTokens || 0;
113
+ const outputTokens = result.usage.outputTokens || 0;
114
+ const cachedTokens = result.usage.cachedTokens || 0;
115
+ const cacheWriteTokens = result.usage.cacheWriteTokens || 0;
116
+ const uncachedInputTokens = uncachedInputTokensForProvider(session.provider, inputTokens, cachedTokens, cacheWriteTokens);
117
+ session.totalInputTokens = (session.totalInputTokens || 0) + inputTokens;
118
+ session.totalOutputTokens = (session.totalOutputTokens || 0) + outputTokens;
119
+ session.tokensCumulative = (session.tokensCumulative || 0)
120
+ + inputTokens
121
+ + outputTokens;
122
+ session.totalCachedReadTokens = (session.totalCachedReadTokens || 0) + cachedTokens;
123
+ session.totalCacheWriteTokens = (session.totalCacheWriteTokens || 0) + cacheWriteTokens;
124
+ session.totalUncachedInputTokens = (session.totalUncachedInputTokens || 0) + uncachedInputTokens;
125
+ }
126
+ const _lastTurn = result.lastTurnUsage || result.usage || {};
127
+ const _lastInputTokens = _lastTurn.inputTokens || 0;
128
+ const _lastCachedReadTokens = _lastTurn.cachedTokens || 0;
129
+ const _lastCacheWriteTokens = _lastTurn.cacheWriteTokens || 0;
130
+ session.lastInputTokens = _lastInputTokens;
131
+ session.lastOutputTokens = _lastTurn.outputTokens || 0;
132
+ session.lastCachedReadTokens = _lastCachedReadTokens;
133
+ session.lastCacheWriteTokens = _lastCacheWriteTokens;
134
+ session.lastUncachedInputTokens = uncachedInputTokensForProvider(
135
+ session.provider,
136
+ _lastInputTokens,
137
+ _lastCachedReadTokens,
138
+ _lastCacheWriteTokens,
139
+ );
140
+ const _inputExcludesCache = providerInputExcludesCache(session.provider);
141
+ session.lastContextTokens = _inputExcludesCache
142
+ ? _lastInputTokens + _lastCachedReadTokens + _lastCacheWriteTokens
143
+ : _lastInputTokens;
144
+ session.lastContextTokensUpdatedAt = Date.now();
145
+ session.lastContextTokensStaleAfterCompact = false;
146
+ }
147
+
148
+ /**
149
+ * Persist incremental usage delta immediately after each provider.send iteration.
150
+ * Idempotency key `sessionId:turnId:epoch:iterationIndex:source` scopes retries
151
+ * per ask, compaction epoch, iteration, and usage source.
152
+ */
153
+ export async function persistIterationMetrics(delta) {
154
+ if (!delta || !delta.sessionId) return;
155
+ const { sessionId, iterationIndex, deltaInput, deltaOutput, deltaCachedRead, deltaCacheWrite, ts } = delta;
156
+ const runtimeEntry = _getRuntimeEntry(sessionId);
157
+ const session = runtimeEntry?.session ?? loadSession(sessionId);
158
+ if (!session || session.closed) return;
159
+ const epoch = resolveUsageMetricsEpoch(session, delta);
160
+ if (epoch !== (Number(session.usageMetricsEpoch) || 0)) {
161
+ session.usageMetricsEpoch = epoch;
162
+ }
163
+ let seen = _metricSeenIter.get(sessionId);
164
+ if (!seen) {
165
+ seen = new Set();
166
+ _metricSeenIter.set(sessionId, seen);
167
+ }
168
+ const ikey = usageMetricsIdempotencyKey(sessionId, session, delta);
169
+ const isReplay = seen.has(ikey);
170
+ seen.add(ikey);
171
+ if (!isReplay) {
172
+ if (runtimeEntry) runtimeEntry.usageMetricsTurnIncremental = true;
173
+ const deltaUncachedInput = delta.deltaUncachedInput != null
174
+ ? Number(delta.deltaUncachedInput) || 0
175
+ : uncachedInputTokensForProvider(session.provider, deltaInput, deltaCachedRead, deltaCacheWrite);
176
+ session.totalInputTokens = (session.totalInputTokens || 0) + (deltaInput || 0);
177
+ session.totalOutputTokens = (session.totalOutputTokens || 0) + (deltaOutput || 0);
178
+ session.tokensCumulative = (session.tokensCumulative || 0) + (deltaInput || 0) + (deltaOutput || 0);
179
+ // Cache totals — additive fields, default 0 on legacy sessions; both
180
+ // are undefined-safe so the schema migrates lazily as new iterations
181
+ // land. Keeps live + terminal aggregates in lock-step (loop.mjs already
182
+ // includes cached_read / cache_write in its terminal usage rollup).
183
+ session.totalCachedReadTokens = (session.totalCachedReadTokens || 0) + (deltaCachedRead || 0);
184
+ session.totalCacheWriteTokens = (session.totalCacheWriteTokens || 0) + (deltaCacheWrite || 0);
185
+ session.totalUncachedInputTokens = (session.totalUncachedInputTokens || 0) + deltaUncachedInput;
186
+ // Window snapshot updated per iteration so agent type=list reflects the
187
+ // most-recent provider-reported input size even for short dispatches
188
+ // that finish before askSession's terminal save lands.
189
+ session.lastInputTokens = deltaInput || 0;
190
+ session.lastOutputTokens = deltaOutput || 0;
191
+ session.lastCachedReadTokens = deltaCachedRead || 0;
192
+ session.lastCacheWriteTokens = deltaCacheWrite || 0;
193
+ session.lastUncachedInputTokens = deltaUncachedInput;
194
+ // Normalized last-call context footprint: how many prompt tokens the
195
+ // model actually saw on the most-recent send, comparable ACROSS
196
+ // providers. Anthropic reports input_tokens EXCLUDING cache (cache_read
197
+ // is a separate field), so the cached portion must be added back to
198
+ // reflect real context size; openai/grok/gemini already fold cached
199
+ // tokens INTO the input count, so input alone is the footprint.
200
+ const _inputExcludesCache = providerInputExcludesCache(session.provider);
201
+ session.lastContextTokens = _inputExcludesCache
202
+ ? (deltaInput || 0) + (deltaCachedRead || 0) + (deltaCacheWrite || 0)
203
+ : (deltaInput || 0);
204
+ session.lastContextTokensUpdatedAt = ts || Date.now();
205
+ session.lastContextTokensStaleAfterCompact = false;
206
+ }
207
+ session.lastIterationIndex = iterationIndex;
208
+ session.updatedAt = ts || Date.now();
209
+ await saveSessionAsync(session, { expectedGeneration: session.generation });
210
+ }