mixdog 0.9.3 → 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 (329) hide show
  1. package/package.json +7 -3
  2. package/scripts/bench/lead-review-tasks-r3.json +20 -0
  3. package/scripts/bench/lead-review-tasks.json +20 -0
  4. package/scripts/bench/r4-mixed-tasks.json +20 -0
  5. package/scripts/bench/review-tasks.json +20 -0
  6. package/scripts/bench/round-codex.json +114 -0
  7. package/scripts/bench/round-mixdog-lead-r3.json +269 -0
  8. package/scripts/bench/round-mixdog-lead.json +269 -0
  9. package/scripts/bench/round-mixdog.json +126 -0
  10. package/scripts/bench/round-r10-bigsample.json +679 -0
  11. package/scripts/bench/round-r11-codexalign.json +257 -0
  12. package/scripts/bench/round-r4-codex.json +114 -0
  13. package/scripts/bench/round-r4-mixed.json +225 -0
  14. package/scripts/bench/round-r5-gpt-lead.json +259 -0
  15. package/scripts/bench/round-r6-codex.json +114 -0
  16. package/scripts/bench/round-r6-solo.json +257 -0
  17. package/scripts/bench/round-r7-full.json +254 -0
  18. package/scripts/bench/round-r8-fulldefault.json +255 -0
  19. package/scripts/bench-run.mjs +215 -29
  20. package/scripts/freevar-smoke.mjs +95 -0
  21. package/scripts/internal-comms-bench.mjs +1 -0
  22. package/scripts/internal-comms-smoke.mjs +10 -9
  23. package/scripts/mouse-probe.mjs +45 -0
  24. package/scripts/output-style-bench.mjs +13 -6
  25. package/scripts/output-style-smoke.mjs +4 -4
  26. package/scripts/provider-toolcall-test.mjs +7 -3
  27. package/scripts/recall-usecase-cases.json +18 -0
  28. package/scripts/recall-usecase-probe.json +6 -0
  29. package/scripts/session-bench.mjs +152 -6
  30. package/scripts/tool-smoke.mjs +23 -63
  31. package/scripts/tui-render-smoke.mjs +90 -0
  32. package/scripts/webhook-smoke.mjs +208 -0
  33. package/src/agents/debugger/AGENT.md +4 -1
  34. package/src/agents/heavy-worker/AGENT.md +6 -5
  35. package/src/agents/maintainer/AGENT.md +4 -0
  36. package/src/agents/reviewer/AGENT.md +2 -1
  37. package/src/agents/worker/AGENT.md +8 -4
  38. package/src/lib/rules-builder.cjs +4 -0
  39. package/src/mixdog-session-runtime.mjs +632 -2042
  40. package/src/output-styles/default.md +34 -9
  41. package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
  42. package/src/output-styles/minimal.md +4 -1
  43. package/src/output-styles/simple.md +22 -7
  44. package/src/rules/agent/00-common.md +2 -0
  45. package/src/rules/lead/lead-brief.md +12 -0
  46. package/src/rules/lead/lead-tool.md +0 -11
  47. package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
  49. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
  50. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
  51. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
  52. package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
  53. package/src/runtime/agent/orchestrator/config.mjs +69 -2
  54. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
  55. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
  60. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
  62. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
  63. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
  65. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
  66. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
  67. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
  68. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
  76. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
  77. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
  80. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
  83. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
  84. package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
  85. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
  87. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
  88. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
  89. package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
  90. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
  91. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
  92. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
  93. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
  94. package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
  95. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
  97. package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
  98. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
  99. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
  100. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
  101. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
  102. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
  103. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
  104. package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
  105. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
  106. package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
  108. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
  109. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
  110. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
  118. package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
  120. package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
  124. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
  125. package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
  126. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
  128. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
  130. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
  131. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
  132. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
  133. package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
  134. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
  136. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
  137. package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
  138. package/src/runtime/channels/backends/discord-access.mjs +32 -0
  139. package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
  140. package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
  141. package/src/runtime/channels/backends/discord.mjs +12 -292
  142. package/src/runtime/channels/index.mjs +229 -663
  143. package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
  144. package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
  145. package/src/runtime/channels/lib/event-queue.mjs +63 -4
  146. package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
  147. package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
  148. package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
  149. package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
  150. package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
  151. package/src/runtime/channels/lib/session-discovery.mjs +56 -4
  152. package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
  153. package/src/runtime/channels/lib/tool-format.mjs +1 -1
  154. package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
  155. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
  156. package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
  157. package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
  158. package/src/runtime/channels/lib/webhook/log.mjs +42 -0
  159. package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
  160. package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
  161. package/src/runtime/channels/lib/webhook.mjs +36 -570
  162. package/src/runtime/channels/tool-defs.mjs +11 -130
  163. package/src/runtime/memory/index.mjs +201 -1948
  164. package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
  165. package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
  166. package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
  167. package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
  168. package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
  169. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
  170. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
  171. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
  172. package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
  173. package/src/runtime/memory/lib/memory-embed.mjs +149 -0
  174. package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
  175. package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
  177. package/src/runtime/memory/lib/query-handlers.mjs +780 -0
  178. package/src/runtime/memory/lib/recall-format.mjs +55 -0
  179. package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
  180. package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
  181. package/src/runtime/memory/tool-defs.mjs +5 -13
  182. package/src/runtime/search/lib/http-fetch.mjs +274 -0
  183. package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
  184. package/src/runtime/search/lib/web-tools.mjs +24 -602
  185. package/src/runtime/shared/atomic-file.mjs +26 -1
  186. package/src/runtime/shared/launcher-control.mjs +2 -2
  187. package/src/runtime/shared/tool-primitives.mjs +308 -0
  188. package/src/runtime/shared/tool-result-summary.mjs +515 -0
  189. package/src/runtime/shared/tool-surface.mjs +80 -898
  190. package/src/runtime/shared/transcript-writer.mjs +23 -0
  191. package/src/runtime/shared/update-checker.mjs +7 -4
  192. package/src/session-runtime/config-helpers.mjs +84 -2
  193. package/src/session-runtime/config-lifecycle.mjs +232 -0
  194. package/src/session-runtime/cwd-plugins.mjs +226 -0
  195. package/src/session-runtime/mcp-glue.mjs +177 -0
  196. package/src/session-runtime/model-recency.mjs +111 -0
  197. package/src/session-runtime/native-search.mjs +247 -0
  198. package/src/session-runtime/output-styles.mjs +11 -9
  199. package/src/session-runtime/prewarm.mjs +142 -0
  200. package/src/session-runtime/provider-models.mjs +278 -0
  201. package/src/session-runtime/provider-usage.mjs +120 -0
  202. package/src/session-runtime/quick-model-rows.mjs +170 -0
  203. package/src/session-runtime/quick-search-models.mjs +46 -0
  204. package/src/session-runtime/session-hooks.mjs +93 -0
  205. package/src/session-runtime/settings-api.mjs +319 -0
  206. package/src/session-runtime/tool-catalog.mjs +29 -29
  207. package/src/session-runtime/tool-defs.mjs +84 -0
  208. package/src/session-runtime/warmup-schedulers.mjs +201 -0
  209. package/src/standalone/agent-tool/helpers.mjs +237 -0
  210. package/src/standalone/agent-tool/notify.mjs +107 -0
  211. package/src/standalone/agent-tool/provider-init.mjs +143 -0
  212. package/src/standalone/agent-tool/render.mjs +152 -0
  213. package/src/standalone/agent-tool/tool-def.mjs +55 -0
  214. package/src/standalone/agent-tool.mjs +110 -671
  215. package/src/standalone/channel-worker.mjs +4 -7
  216. package/src/standalone/explore-tool.mjs +30 -9
  217. package/src/standalone/hook-bus/config.mjs +207 -0
  218. package/src/standalone/hook-bus/constants.mjs +90 -0
  219. package/src/standalone/hook-bus/handlers.mjs +481 -0
  220. package/src/standalone/hook-bus/payload.mjs +31 -0
  221. package/src/standalone/hook-bus/rules.mjs +77 -0
  222. package/src/standalone/hook-bus.mjs +77 -870
  223. package/src/standalone/memory-runtime-proxy.mjs +7 -0
  224. package/src/standalone/opencode-go-login.mjs +5 -1
  225. package/src/standalone/provider-admin.mjs +1 -16
  226. package/src/standalone/usage-dashboard.mjs +3 -1
  227. package/src/tui/App.jsx +945 -8094
  228. package/src/tui/app/app-format.mjs +206 -0
  229. package/src/tui/app/channel-pickers.mjs +510 -0
  230. package/src/tui/app/clipboard.mjs +67 -0
  231. package/src/tui/app/core-memory-picker.mjs +210 -0
  232. package/src/tui/app/extension-pickers.mjs +506 -0
  233. package/src/tui/app/input-parsers.mjs +193 -0
  234. package/src/tui/app/maintenance-pickers.mjs +324 -0
  235. package/src/tui/app/model-options.mjs +330 -0
  236. package/src/tui/app/model-picker.mjs +365 -0
  237. package/src/tui/app/onboarding-steps.mjs +400 -0
  238. package/src/tui/app/project-picker.mjs +247 -0
  239. package/src/tui/app/provider-setup-picker.mjs +580 -0
  240. package/src/tui/app/resume-picker.mjs +55 -0
  241. package/src/tui/app/route-pickers.mjs +419 -0
  242. package/src/tui/app/settings-picker.mjs +490 -0
  243. package/src/tui/app/slash-commands.mjs +101 -0
  244. package/src/tui/app/slash-dispatch.mjs +427 -0
  245. package/src/tui/app/text-layout.mjs +46 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +154 -0
  247. package/src/tui/app/transcript-window.mjs +671 -0
  248. package/src/tui/app/use-mouse-input.mjs +460 -0
  249. package/src/tui/app/use-prompt-handlers.mjs +310 -0
  250. package/src/tui/app/use-transcript-scroll.mjs +510 -0
  251. package/src/tui/app/use-transcript-window.mjs +589 -0
  252. package/src/tui/components/ConfirmBar.jsx +1 -1
  253. package/src/tui/components/Picker.jsx +32 -4
  254. package/src/tui/components/PromptInput.jsx +23 -101
  255. package/src/tui/components/SlashCommandPalette.jsx +8 -1
  256. package/src/tui/components/StatusLine.jsx +63 -12
  257. package/src/tui/components/TextEntryPanel.jsx +11 -0
  258. package/src/tui/components/ToolExecution.jsx +52 -594
  259. package/src/tui/components/TranscriptItem.jsx +105 -0
  260. package/src/tui/components/UsagePanel.jsx +18 -4
  261. package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
  262. package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
  263. package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
  264. package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
  265. package/src/tui/components/tool-execution/text-format.mjs +161 -0
  266. package/src/tui/display-width.mjs +20 -3
  267. package/src/tui/dist/index.mjs +19652 -18630
  268. package/src/tui/engine/agent-job-feed.mjs +133 -0
  269. package/src/tui/engine/notification-plan.mjs +76 -0
  270. package/src/tui/engine/render-timing.mjs +17 -0
  271. package/src/tui/engine/tool-approval.mjs +94 -0
  272. package/src/tui/engine/tool-card-results.mjs +234 -0
  273. package/src/tui/engine/tool-result-status.mjs +135 -0
  274. package/src/tui/engine.mjs +122 -562
  275. package/src/tui/figures.mjs +5 -0
  276. package/src/tui/index.jsx +105 -0
  277. package/src/tui/input-editing.mjs +2 -2
  278. package/src/tui/markdown/format-token.mjs +4 -1
  279. package/src/tui/statusline-ansi-bridge.mjs +11 -3
  280. package/src/tui/theme.mjs +6 -0
  281. package/src/ui/statusline-agents.mjs +213 -0
  282. package/src/ui/statusline-format.mjs +146 -0
  283. package/src/ui/statusline-segments.mjs +148 -0
  284. package/src/ui/statusline.mjs +67 -501
  285. package/src/ui/tool-card.mjs +0 -1
  286. package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
  287. package/src/workflows/default/WORKFLOW.md +1 -1
  288. package/src/workflows/sequential/WORKFLOW.md +1 -1
  289. package/vendor/ink/build/display-width.js +19 -3
  290. package/vendor/ink/build/ink.js +103 -6
  291. package/vendor/ink/build/log-update.js +17 -3
  292. package/vendor/ink/build/wrap-text.js +125 -0
  293. package/scripts/_test-folder-dialog.mjs +0 -30
  294. package/scripts/fix-brief-fn.mjs +0 -35
  295. package/scripts/fix-format-tool-surface.mjs +0 -24
  296. package/scripts/fix-tool-exec-visible.mjs +0 -42
  297. package/scripts/patch-agent-brief.mjs +0 -48
  298. package/scripts/patch-app.mjs +0 -21
  299. package/scripts/patch-app2.mjs +0 -18
  300. package/scripts/patch-dist-brief.mjs +0 -96
  301. package/scripts/patch-tool-exec.mjs +0 -70
  302. package/src/examples/schedules/SCHEDULE.example.md +0 -32
  303. package/src/examples/webhooks/WEBHOOK.example.md +0 -40
  304. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
  305. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
  306. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
  307. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
  308. package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
  309. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
  310. package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
  311. package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
  312. package/src/standalone/agent-task-status.test.mjs +0 -76
  313. package/src/tui/components/tool-output-format.test.mjs +0 -399
  314. package/src/tui/display-width.test.mjs +0 -35
  315. package/src/tui/engine-runtime-notification.test.mjs +0 -115
  316. package/src/tui/engine-tool-result-text.test.mjs +0 -75
  317. package/src/tui/input-editing.selection.test.mjs +0 -75
  318. package/src/tui/markdown/format-token.test.mjs +0 -354
  319. package/src/tui/markdown/render-ansi.test.mjs +0 -108
  320. package/src/tui/markdown/stream-fence.test.mjs +0 -26
  321. package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
  322. package/src/tui/paste-fix.test.mjs +0 -119
  323. package/src/tui/prompt-history-store.test.mjs +0 -52
  324. package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
  325. package/src/tui/transcript-tool-failures.test.mjs +0 -111
  326. package/src/ui/markdown.test.mjs +0 -70
  327. package/src/ui/statusline-context-label.test.mjs +0 -15
  328. package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
  329. package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
@@ -213,6 +213,155 @@ export async function flushEmbeddingDirty(db, options = {}) {
213
213
  }
214
214
  }
215
215
 
216
+ // Embeds raw transcript rows (is_root = 0, non-chunk-member) so the dense
217
+ // recall leg can rank them alongside root/summary entries. Mirrors
218
+ // flushEmbeddingDirty's SKIP LOCKED batch-claim structure but sources the
219
+ // embed text from `content` (raw rows have no element/summary) and honors
220
+ // `limit` as a total cap across the whole call rather than an unbounded loop.
221
+ //
222
+ // Stale-embedding safety when recap toggles back ON: cycle1's promotion
223
+ // UPDATE (memory-cycle1.mjs chunk commit) SETs summary/element, which fires
224
+ // trg_entries_embedding_invalidate (memory.mjs — BEFORE UPDATE OF content,
225
+ // summary, element; NEW.is_root=1 AND summary IS DISTINCT FROM OLD.summary)
226
+ // and NULLs the raw-content embedding written here; cycle1's end-of-cycle
227
+ // flushEmbeddingDirty (is_root=1 AND embedding IS NULL) then re-embeds the
228
+ // root from its fresh element/summary. Member leaves keep their content
229
+ // embedding — the dense recall leg resolves members to their chunk root
230
+ // (memory-recall-store.mjs member→root resolution), so that is leaf-level
231
+ // dense matching, not staleness.
232
+ export async function flushRawEmbeddings(db, options = {}) {
233
+ const { limit = 200, signal } = options ?? {}
234
+ throwIfAborted(signal)
235
+ await ensureEmbCacheTable(db)
236
+ throwIfAborted(signal)
237
+
238
+ let attempted = 0
239
+ let embedded = 0
240
+ let cursor = 0
241
+
242
+ while (attempted < limit) {
243
+ throwIfAborted(signal)
244
+ const batchCap = Math.min(BATCH_SIZE, limit - attempted)
245
+ const client = await db._pool.connect()
246
+ let ids
247
+ try {
248
+ throwIfAborted(signal)
249
+ await client.query('BEGIN')
250
+ const res = await client.query(
251
+ `SELECT id FROM memory.entries
252
+ WHERE is_root = 0 AND chunk_root IS NULL AND embedding IS NULL
253
+ AND NULLIF(btrim(session_id), '') IS NOT NULL
254
+ AND content IS NOT NULL
255
+ AND id > $2
256
+ ORDER BY id
257
+ LIMIT $1
258
+ FOR UPDATE SKIP LOCKED`,
259
+ [batchCap, cursor],
260
+ )
261
+ ids = res.rows.map(r => Number(r.id))
262
+ throwIfAborted(signal)
263
+ } catch (err) {
264
+ try { await client.query('ROLLBACK') } catch {}
265
+ client.release()
266
+ if (signal?.aborted) throw signal.reason ?? err
267
+ __mixdogMemoryLog(`[embed] raw flush SKIP LOCKED claim failed: ${err.message}\n`)
268
+ break
269
+ }
270
+
271
+ if (ids.length === 0) {
272
+ try { await client.query('COMMIT') } catch {}
273
+ client.release()
274
+ break
275
+ }
276
+
277
+ cursor = ids[ids.length - 1]
278
+ attempted += ids.length
279
+ let batchDone = false
280
+ try {
281
+ throwIfAborted(signal)
282
+ const writtenIds = await syncRawBatchEmbeddings(client, ids, { signal })
283
+ embedded += writtenIds.length
284
+ batchDone = true
285
+ } catch (err) {
286
+ if (signal?.aborted) throw signal.reason ?? err
287
+ __mixdogMemoryLog(`[embed] raw batch failed (ids=${ids[0]}..${ids[ids.length-1]}): ${err.message}\n`)
288
+ } finally {
289
+ try {
290
+ if (batchDone) await client.query('COMMIT')
291
+ else await client.query('ROLLBACK')
292
+ } catch {}
293
+ client.release()
294
+ }
295
+
296
+ if (ids.length < batchCap) break
297
+ }
298
+
299
+ return { attempted, embedded }
300
+ }
301
+
302
+ // Batch-embed raw transcript rows: content is the embed source text (raw
303
+ // rows have no element/summary). Mirrors syncBatchEmbeddings' shape.
304
+ async function syncRawBatchEmbeddings(db, ids, options = {}) {
305
+ const signal = options?.signal
306
+ throwIfAborted(signal)
307
+ const rows = (await db.query(
308
+ `SELECT id, content FROM memory.entries
309
+ WHERE id = ANY($1::bigint[]) AND is_root = 0 AND chunk_root IS NULL`,
310
+ [ids],
311
+ )).rows
312
+ throwIfAborted(signal)
313
+ if (rows.length === 0) return []
314
+
315
+ const dimsRow = (await db.query(`SELECT value FROM memory.meta WHERE key = 'embedding.current_dims'`)).rows[0]
316
+ throwIfAborted(signal)
317
+ const expected = Number(dimsRow?.value ?? 0)
318
+
319
+ const texts = rows.map(row => (typeof row.content === 'string' ? row.content.trim() : ''))
320
+ const vectors = await cachedEmbedTextBatch(db, texts.filter(Boolean), { signal })
321
+ throwIfAborted(signal)
322
+ let vecIdx = 0
323
+ const updates = [] // { id, content, vector }
324
+ for (let i = 0; i < rows.length; i++) {
325
+ throwIfAborted(signal)
326
+ const row = rows[i]
327
+ if (!texts[i]) continue
328
+ const vector = vectors[vecIdx++]
329
+ if (!Array.isArray(vector) || vector.length === 0) continue
330
+ if (Number.isFinite(expected) && expected > 0 && vector.length !== expected) {
331
+ __mixdogMemoryLog(`[embed] raw dim mismatch (id=${row.id} got=${vector.length} expected=${expected})\n`)
332
+ continue
333
+ }
334
+ updates.push({ id: Number(row.id), content: row.content, vector })
335
+ }
336
+
337
+ if (updates.length === 0) return []
338
+
339
+ const valClauses = updates.map((u, i) => {
340
+ const base = i * 3
341
+ return `($${base+1}::bigint, $${base+2}::halfvec, $${base+3}::text)`
342
+ })
343
+ const params = []
344
+ for (const u of updates) {
345
+ params.push(u.id, embeddingToSql(u.vector), u.content)
346
+ }
347
+ const res = await db.query(
348
+ `UPDATE memory.entries AS e
349
+ SET embedding = t.vector
350
+ FROM (VALUES ${valClauses.join(',')}) AS t(id, vector, content)
351
+ WHERE e.id = t.id AND e.is_root = 0 AND e.chunk_root IS NULL
352
+ AND e.embedding IS NULL
353
+ AND e.content IS NOT DISTINCT FROM t.content
354
+ RETURNING e.id`,
355
+ params,
356
+ )
357
+ throwIfAborted(signal)
358
+ const writtenIds = (res.rows || []).map(r => Number(r.id))
359
+ if (writtenIds.length < updates.length) {
360
+ __mixdogMemoryLog(`[embed-sync] ${updates.length - writtenIds.length} raw stale-write(s) skipped — content changed since read\n`)
361
+ }
362
+ return writtenIds
363
+ }
364
+
216
365
  // Batch-embed all ids in one DB round-trip for fetching, one API call per unique text,
217
366
  // then one VALUES UPDATE for all results.
218
367
  // Returns the list of ids that were actually UPDATEd with a fresh embedding.
@@ -0,0 +1,162 @@
1
+ // Extracted from index.mjs — single/multi-instance process-lock + owner-election
2
+ // helpers. Pure functions parameterized on lock-file paths so index.mjs retains
3
+ // ownership of DATA_DIR-derived constants and the daemon logger.
4
+ import fs from 'node:fs'
5
+ import { execFileSync } from 'node:child_process'
6
+
7
+ export function parsePositivePid(value) {
8
+ const pid = Number(value)
9
+ return Number.isFinite(pid) && pid > 0 ? pid : null
10
+ }
11
+
12
+ export function isPidAliveLocal(pid) {
13
+ if (!Number.isFinite(pid) || pid <= 0) return false
14
+ try { process.kill(pid, 0); return true }
15
+ catch (e) { return e.code !== 'ESRCH' }
16
+ }
17
+
18
+ export function tryAcquireMemoryOwnerLock(ownerLockFile, log = () => {}) {
19
+ // Returns true on success (this process now owns memory worker for the data
20
+ // dir), false when a live peer holds the lock. Stale locks (dead PID) are
21
+ // unlinked and retried atomically. Throws on unexpected fs errors so callers
22
+ // surface lock-system corruption rather than silently downgrading.
23
+ //
24
+ // EPERM/EBUSY/EACCES at openSync are transient — AV scanners (SignKorea /
25
+ // SKCert / ezPDFWS etc) briefly lock newly-created files during inspection.
26
+ // The 0.1.x baseline threw immediately and the worker promoted to
27
+ // permanentlyDegraded, killing memory tools for the rest of the session.
28
+ // Treat the AV error codes as retryable with bounded backoff (~750ms total)
29
+ // before giving up and rethrowing.
30
+ for (let attempt = 0; attempt < 5; attempt++) {
31
+ try {
32
+ const fd = fs.openSync(ownerLockFile, 'wx')
33
+ fs.writeSync(fd, String(process.pid))
34
+ fs.closeSync(fd)
35
+ return true
36
+ } catch (e) {
37
+ if (e.code === 'EEXIST') {
38
+ let ownerPid = NaN
39
+ try { ownerPid = Number(fs.readFileSync(ownerLockFile, 'utf8').trim()) } catch {}
40
+ if (isPidAliveLocal(ownerPid)) return false
41
+ // Stale lock: dead owner — unlink and retry exclusive create.
42
+ try { fs.unlinkSync(ownerLockFile) } catch {}
43
+ continue
44
+ }
45
+ const transient = e.code === 'EPERM' || e.code === 'EBUSY' || e.code === 'EACCES'
46
+ if (transient && attempt < 4) {
47
+ // Sync busy-wait acceptable here: this runs on memory worker boot
48
+ // path, once per process; the parent handler is not blocked.
49
+ const end = Date.now() + 50 * (attempt + 1)
50
+ while (Date.now() < end) {}
51
+ continue
52
+ }
53
+ throw e
54
+ }
55
+ }
56
+ return false
57
+ }
58
+
59
+ export function releaseMemoryOwnerLock(ownerLockFile) {
60
+ try {
61
+ const ownerPid = Number(fs.readFileSync(ownerLockFile, 'utf8').trim())
62
+ if (ownerPid === process.pid) fs.unlinkSync(ownerLockFile)
63
+ } catch {}
64
+ }
65
+
66
+ export function killPreviousServer(pid, log = () => {}) {
67
+ if (pid <= 0 || pid === process.pid) return false
68
+ if (process.platform === 'win32') {
69
+ try {
70
+ execFileSync('taskkill', ['/F', '/T', '/PID', String(pid)], { encoding: 'utf8', timeout: 5000, windowsHide: true })
71
+ log(`[memory-service] Killed previous server PID ${pid}\n`)
72
+ return true
73
+ } catch (e) {
74
+ // Exit code 128 = process not found; treat stale lock as already-dead = success.
75
+ // Status 128 reliably means "process not found" regardless of locale; no text match needed.
76
+ // Status 1 with English text match handles edge cases on some Windows versions.
77
+ const notFoundText = /not found|no running instance/i.test(e.stdout || '')
78
+ || /not found|no running instance/i.test(e.stderr || '')
79
+ || /not found|no running instance/i.test(e.message || '')
80
+ const alreadyDead = e.status === 128 || (e.status === 1 && notFoundText)
81
+ if (alreadyDead) {
82
+ log(`[memory-service] PID ${pid} already dead (stale lock), proceeding\n`)
83
+ return true
84
+ }
85
+ log(`[memory-service] taskkill failed for PID ${pid}: ${e.message}\n`)
86
+ return false
87
+ }
88
+ } else {
89
+ // Pre-flight: if the process is already gone, treat stale lock as success.
90
+ try {
91
+ process.kill(pid, 0)
92
+ } catch (e) {
93
+ if (e.code === 'ESRCH') {
94
+ log(`[memory-service] PID ${pid} already dead (stale lock), proceeding\n`)
95
+ return true
96
+ }
97
+ }
98
+ try { process.kill(pid, 'SIGTERM') } catch {}
99
+ try { process.kill(pid, 'SIGKILL') } catch {}
100
+ // Poll for death up to 2s
101
+ const deadline = Date.now() + 2000
102
+ while (Date.now() < deadline) {
103
+ try {
104
+ process.kill(pid, 0)
105
+ } catch (e) {
106
+ if (e.code === 'ESRCH') {
107
+ log(`[memory-service] Killed previous server PID ${pid}\n`)
108
+ return true
109
+ }
110
+ }
111
+ // Synchronous 50ms sleep via shared buffer spin
112
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50)
113
+ }
114
+ log(`[memory-service] PID ${pid} still alive after SIGKILL\n`)
115
+ return false
116
+ }
117
+ }
118
+
119
+ export function acquireLock(lockFile, log = () => {}) {
120
+ // Multi-instance guard. In multi-terminal mode the lock owner is a *peer*
121
+ // memory worker serving recall for another CC session. killPreviousServer
122
+ // would taskkill /F that healthy peer mid-flight, then this fork-proxy
123
+ // mode wouldn't even need a lock anyway. Skip the entire kill-the-previous
124
+ // protocol; fork-proxy detection in init() takes priority. If neither
125
+ // proxy nor lock-owner path applies (race window during simultaneous
126
+ // boot), the worker simply continues without the lock — server-main /
127
+ // PG / port-listen handle the actual conflict cases.
128
+ if (process.env.MIXDOG_MULTI_INSTANCE === '1') return
129
+ try {
130
+ if (fs.existsSync(lockFile)) {
131
+ const lockedPid = Number(fs.readFileSync(lockFile, 'utf8').trim())
132
+ if (lockedPid > 0 && lockedPid !== process.pid) {
133
+ const killed = killPreviousServer(lockedPid, log)
134
+ if (!killed) {
135
+ log(`[memory-service] Could not kill previous server PID ${lockedPid}, aborting\n`)
136
+ process.exit(1)
137
+ }
138
+ try { fs.unlinkSync(lockFile) } catch {}
139
+ }
140
+ }
141
+ const fd = fs.openSync(lockFile, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600)
142
+ try {
143
+ fs.writeSync(fd, String(process.pid))
144
+ } finally {
145
+ fs.closeSync(fd)
146
+ }
147
+ } catch (e) {
148
+ if (e.code === 'EEXIST') {
149
+ log(`[memory-service] Lock file exists (EEXIST) — another instance is already running, exiting\n`)
150
+ process.exit(0)
151
+ }
152
+ log(`[memory-service] Lock acquisition failed: ${e.message}\n`)
153
+ process.exit(1)
154
+ }
155
+ }
156
+
157
+ export function releaseLock(lockFile) {
158
+ try {
159
+ const content = fs.readFileSync(lockFile, 'utf8').trim()
160
+ if (Number(content) === process.pid) fs.unlinkSync(lockFile)
161
+ } catch {}
162
+ }
@@ -15,6 +15,18 @@ import { recallReadQuery } from './memory-recall-read-query.mjs'
15
15
  const _MV_HOT_ACTIVE_TTL_MS = 60_000
16
16
  const _mvHotActiveCache = new WeakMap() // db → { populated: boolean, ts: number }
17
17
  const SEMANTIC_ONLY_MIN_SIM = 0.72
18
+ // Cross-language semantic rescue: a query in one language against rows in
19
+ // another rarely clears SEMANTIC_ONLY_MIN_SIM on a small (384-dim) embedding
20
+ // model, yet the TOP dense ranks are still overwhelmingly relevant. Accept
21
+ // semantic-only rows that are both top-ranked in the dense leg AND above a
22
+ // softer floor, instead of dropping everything below 0.72.
23
+ const SEMANTIC_TOP_RANK_MAX = 3
24
+ // Floor for the top-rank rescue. 384-dim MiniLM-class embeddings put UNRELATED
25
+ // text at ~0.65-0.75 sim (measured: gibberish query "zzqqxx ... 999" scored
26
+ // 0.749 against real rows), so anything below ~0.70 is noise regardless of
27
+ // rank; cross-language relevant pairs measured 0.67-0.74. 0.70 keeps most of
28
+ // the cross-language rescue while rejecting pure-noise top ranks.
29
+ const SEMANTIC_TOP_RANK_MIN_SIM = 0.70
18
30
  const SHORT_QUERY_TOKEN_MAX = 2
19
31
 
20
32
  function buildExactTerms(query) {
@@ -62,7 +74,12 @@ function hasFullQueryTextMatch(query, row) {
62
74
  function hasQueryTokenCoverage(row, queryTokenCount) {
63
75
  if (queryTokenCount <= SHORT_QUERY_TOKEN_MAX) return true
64
76
  const hits = Number(row?.exact_hits)
65
- return Number.isFinite(hits) && hits >= queryTokenCount
77
+ // Majority coverage, not full coverage. Requiring EVERY query token to hit
78
+ // made mixed-language queries ("usage spike 원인") return nothing when one
79
+ // token has no lexical counterpart in the row — ~60% of tokens matching is
80
+ // strong enough lexical evidence alongside the semantic leg.
81
+ const required = Math.max(2, Math.ceil(queryTokenCount * 0.6))
82
+ return Number.isFinite(hits) && hits >= required
66
83
  }
67
84
 
68
85
  function exactTextBoost(query, row, exactHits) {
@@ -479,7 +496,10 @@ LEFT JOIN exact x ON x.id = c.id`
479
496
  const scored = scoredAll.filter(({ row }) => {
480
497
  const hasTextSupport = row.sparse_rank != null || row.trgm_rank != null || row.exact_rank != null
481
498
  const sim = Number(row.dense_sim)
482
- const hasSemanticSupport = Number.isFinite(sim) && sim >= SEMANTIC_ONLY_MIN_SIM
499
+ const denseRankNum = row.dense_rank != null ? Number(row.dense_rank) : null
500
+ const hasSemanticSupport = (Number.isFinite(sim) && sim >= SEMANTIC_ONLY_MIN_SIM)
501
+ || (Number.isFinite(sim) && sim >= SEMANTIC_TOP_RANK_MIN_SIM
502
+ && denseRankNum != null && denseRankNum <= SEMANTIC_TOP_RANK_MAX)
483
503
  if (!hasTextSupport) {
484
504
  if (hasSemanticSupport) return true
485
505
  semanticOnlyDropped += 1
@@ -194,7 +194,7 @@ function patchActiveInstance(fields) {
194
194
  else merged[k] = v;
195
195
  }
196
196
  return merged;
197
- }, { compact: true, fsyncDir: true });
197
+ }, { compact: true, fsyncDir: true, renameFallback: 'truncate' });
198
198
  } catch (e) {
199
199
  __mixdogMemoryLog(`[supervisor-pg] patchActiveInstance failed: ${e?.message}\n`);
200
200
  }