mixdog 0.9.151 → 0.9.153

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 (307) hide show
  1. package/LICENSES/browser-import-NOTICE.txt +11 -0
  2. package/NOTICE.md +11 -0
  3. package/README.md +108 -68
  4. package/package.json +17 -4
  5. package/scripts/computer-task-cost-report.mjs +144 -0
  6. package/scripts/run-office-live-tests.mjs +32 -0
  7. package/scripts/tool-stress.mjs +0 -2
  8. package/src/agents/front-worker/AGENT.md +13 -0
  9. package/src/agents/front-worker/agent.json +6 -0
  10. package/src/agents/heavy-worker/AGENT.md +10 -11
  11. package/src/agents/heavy-worker/agent.json +1 -1
  12. package/src/agents/maintainer/AGENT.md +0 -2
  13. package/src/agents/reviewer/AGENT.md +9 -13
  14. package/src/agents/reviewer/agent.json +1 -1
  15. package/src/agents/security/AGENT.md +14 -0
  16. package/src/agents/security/agent.json +6 -0
  17. package/src/agents/worker/AGENT.md +8 -8
  18. package/src/agents/worker/agent.json +1 -1
  19. package/src/agents/writer/AGENT.md +14 -0
  20. package/src/agents/writer/agent.json +6 -0
  21. package/src/defaults/skills/setup/SKILL.md +18 -27
  22. package/src/headless-exec.mjs +3 -10
  23. package/src/headless-exec.test.mjs +2 -7
  24. package/src/rules/agent/00-common.md +4 -7
  25. package/src/rules/lead/01-general.md +0 -3
  26. package/src/rules/shared/10-tool-workflow.md +10 -2
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.test.mjs +378 -0
  29. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
  31. package/src/runtime/agent/orchestrator/cache-break-trace.test.mjs +115 -0
  32. package/src/runtime/agent/orchestrator/config.mjs +13 -68
  33. package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
  34. package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
  35. package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
  36. package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
  37. package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
  38. package/src/runtime/agent/orchestrator/providers/admission-scheduler-metrics.test.mjs +47 -0
  39. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
  40. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
  41. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  43. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
  44. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
  45. package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
  46. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -3
  47. package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
  48. package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
  49. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
  50. package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +59 -0
  51. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
  52. package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +71 -0
  53. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
  56. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
  57. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
  58. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +47 -2
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -12
  60. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +122 -6
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
  64. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +144 -93
  65. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +18 -13
  66. package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +288 -0
  67. package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
  68. package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
  69. package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
  70. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +86 -7
  71. package/src/runtime/agent/orchestrator/session/agent-loop.test.mjs +222 -0
  72. package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
  73. package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.test.mjs +83 -0
  74. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
  75. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +26 -2
  76. package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +112 -0
  77. package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +192 -0
  78. package/src/runtime/agent/orchestrator/session/context-usage-restart.test.mjs +72 -0
  79. package/src/runtime/agent/orchestrator/session/context-utils.mjs +100 -15
  80. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
  81. package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +74 -0
  82. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
  83. package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +20 -1
  84. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
  85. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
  86. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.test.mjs +28 -0
  87. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
  88. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
  89. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  90. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
  91. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +111 -39
  92. package/src/runtime/agent/orchestrator/session/manager/ask-session.test.mjs +46 -0
  93. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -6
  94. package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
  95. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +27 -3
  96. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +26 -54
  97. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +63 -1
  98. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
  100. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +580 -0
  101. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +593 -0
  102. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +119 -190
  103. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
  104. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +43 -8
  105. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
  106. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +9 -2
  107. package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
  108. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
  109. package/src/runtime/agent/orchestrator/session/store-live-publication.test.mjs +83 -0
  110. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
  111. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
  112. package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +64 -0
  113. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
  114. package/src/runtime/agent/orchestrator/session/store-summary-visibility.test.mjs +219 -0
  115. package/src/runtime/agent/orchestrator/session/store.mjs +26 -2
  116. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
  118. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
  119. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
  120. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
  121. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
  122. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
  123. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
  124. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
  125. package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
  126. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
  128. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
  129. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
  130. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
  131. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
  132. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
  133. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
  134. package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
  135. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
  136. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
  137. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
  138. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  139. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +12 -0
  140. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
  141. package/src/runtime/attachments/pdf-extract.mjs +3 -0
  142. package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
  143. package/src/runtime/bridge-clients.test.mjs +827 -0
  144. package/src/runtime/browser-bridge/action-schema.mjs +251 -0
  145. package/src/runtime/browser-bridge/client.mjs +152 -0
  146. package/src/runtime/browser-bridge/tool-defs.mjs +159 -0
  147. package/src/runtime/computer-bridge/action-schema.mjs +653 -0
  148. package/src/runtime/computer-bridge/client.mjs +231 -0
  149. package/src/runtime/computer-bridge/tool-defs.mjs +33 -0
  150. package/src/runtime/media/store.mjs +18 -8
  151. package/src/runtime/media/store.test.mjs +14 -0
  152. package/src/runtime/memory/index.mjs +1 -1
  153. package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
  154. package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
  155. package/src/runtime/office/assurance-benchmark.mjs +358 -0
  156. package/src/runtime/office/assurance.mjs +833 -0
  157. package/src/runtime/office/benchmark.mjs +496 -0
  158. package/src/runtime/office/capabilities.mjs +551 -0
  159. package/src/runtime/office/com-adapter.mjs +283 -0
  160. package/src/runtime/office/content-model.mjs +183 -0
  161. package/src/runtime/office/contract-benchmark.mjs +105 -0
  162. package/src/runtime/office/design-library.mjs +1562 -0
  163. package/src/runtime/office/design-system.mjs +1966 -0
  164. package/src/runtime/office/index.mjs +2413 -0
  165. package/src/runtime/office/journal.mjs +82 -0
  166. package/src/runtime/office/office-assurance.test.mjs +371 -0
  167. package/src/runtime/office/office-com-host.ps1 +3781 -0
  168. package/src/runtime/office/office-com-session-host.ps1 +855 -0
  169. package/src/runtime/office/office-live-runtime.test.mjs +819 -0
  170. package/src/runtime/office/office-runtime.test.mjs +2070 -0
  171. package/src/runtime/office/ooxml-validator.mjs +271 -0
  172. package/src/runtime/office/pagination.mjs +132 -0
  173. package/src/runtime/office/pdf-adapter.mjs +591 -0
  174. package/src/runtime/office/pdf-analysis.mjs +372 -0
  175. package/src/runtime/office/pdf-render.mjs +167 -0
  176. package/src/runtime/office/pdf-security.mjs +63 -0
  177. package/src/runtime/office/polish-benchmark.mjs +190 -0
  178. package/src/runtime/office/portable-ooxml.mjs +1867 -0
  179. package/src/runtime/office/quality-live-benchmark.mjs +486 -0
  180. package/src/runtime/office/quality-pipeline.mjs +136 -0
  181. package/src/runtime/office/tabular.mjs +302 -0
  182. package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
  183. package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
  184. package/src/runtime/office/tool-defs.mjs +113 -0
  185. package/src/runtime/office/visual-diff.mjs +89 -0
  186. package/src/runtime/office/xlsx-assertions.mjs +172 -0
  187. package/src/runtime/office/xlsx-contract.mjs +147 -0
  188. package/src/runtime/shared/agent-route-config.mjs +9 -0
  189. package/src/runtime/shared/child-spawn-gate.mjs +17 -0
  190. package/src/runtime/shared/child-spawn-remote.mjs +69 -7
  191. package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
  192. package/src/runtime/shared/pristine-execution-contract.json +2 -1
  193. package/src/runtime/shared/provider-api-key.mjs +1 -0
  194. package/src/runtime/shared/session-runtime-health.mjs +91 -0
  195. package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
  196. package/src/runtime/shared/skill-document.mjs +86 -0
  197. package/src/runtime/shared/skill-document.test.mjs +81 -0
  198. package/src/runtime/shared/tool-execution-contract.mjs +16 -0
  199. package/src/runtime/shared/tool-surface.mjs +27 -0
  200. package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
  201. package/src/runtime/shared/turn-snapshot.mjs +32 -9
  202. package/src/runtime/shared/user-cwd.mjs +33 -14
  203. package/src/runtime/shared/user-cwd.test.mjs +43 -0
  204. package/src/session-runtime/agent-disable.test.mjs +20 -1
  205. package/src/session-runtime/context-status.mjs +9 -3
  206. package/src/session-runtime/cwd-plugins.mjs +26 -26
  207. package/src/session-runtime/cwd-plugins.test.mjs +75 -0
  208. package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
  209. package/src/session-runtime/deferred-tool-delta.mjs +104 -0
  210. package/src/session-runtime/deferred-tool-delta.test.mjs +40 -0
  211. package/src/session-runtime/global-extensions.mjs +26 -0
  212. package/src/session-runtime/global-extensions.test.mjs +28 -0
  213. package/src/session-runtime/global-settings-session-identity.test.mjs +76 -0
  214. package/src/session-runtime/goal-runtime.mjs +1141 -0
  215. package/src/session-runtime/goal-runtime.test.mjs +645 -0
  216. package/src/session-runtime/lifecycle-api.mjs +58 -11
  217. package/src/session-runtime/lifecycle-api.test.mjs +183 -0
  218. package/src/session-runtime/mcp-glue.mjs +49 -47
  219. package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
  220. package/src/session-runtime/plugin-mcp.mjs +96 -22
  221. package/src/session-runtime/prewarm.mjs +1 -10
  222. package/src/session-runtime/provider-request-snapshot.mjs +33 -9
  223. package/src/session-runtime/provider-request-snapshot.test.mjs +27 -5
  224. package/src/session-runtime/resource-api-global.test.mjs +109 -0
  225. package/src/session-runtime/resource-api.mjs +168 -116
  226. package/src/session-runtime/runtime-core.mjs +208 -29
  227. package/src/session-runtime/runtime-tunables.mjs +6 -8
  228. package/src/session-runtime/session-lifecycle.mjs +34 -12
  229. package/src/session-runtime/session-title.mjs +2 -2
  230. package/src/session-runtime/session-turn-api.mjs +37 -25
  231. package/src/session-runtime/skills-api.mjs +79 -25
  232. package/src/session-runtime/skills-api.test.mjs +99 -0
  233. package/src/session-runtime/tool-catalog-data.mjs +1 -1
  234. package/src/session-runtime/tool-catalog-schema.mjs +0 -5
  235. package/src/session-runtime/tool-catalog.mjs +42 -112
  236. package/src/session-runtime/tool-defs.mjs +6 -1
  237. package/src/session-runtime/tool-policy-surface.test.mjs +93 -17
  238. package/src/session-runtime/tool-surface.mjs +30 -8
  239. package/src/session-runtime/workflow-agents-api.mjs +8 -25
  240. package/src/session-runtime/workflow.mjs +8 -6
  241. package/src/standalone/agent-dispatch-broker.mjs +5 -9
  242. package/src/standalone/agent-tool/helpers.mjs +2 -2
  243. package/src/standalone/agent-tool/job-views.mjs +3 -1
  244. package/src/standalone/agent-tool/notify.mjs +11 -9
  245. package/src/standalone/agent-tool/notify.test.mjs +37 -0
  246. package/src/standalone/agent-tool/render.mjs +3 -0
  247. package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
  248. package/src/standalone/agent-tool/spawn-flow.test.mjs +41 -0
  249. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  250. package/src/standalone/agent-tool/worker-index.mjs +123 -0
  251. package/src/standalone/agent-tool/worker-index.test.mjs +132 -0
  252. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  253. package/src/standalone/agent-tool.mjs +57 -10
  254. package/src/standalone/daemon.mjs +113 -15
  255. package/src/standalone/hook-bus/config.mjs +1 -0
  256. package/src/standalone/plugin-admin.mjs +17 -0
  257. package/src/standalone/plugin-admin.test.mjs +7 -0
  258. package/src/standalone/provider-admin.mjs +1 -0
  259. package/src/standalone/session-protocol.mjs +5 -0
  260. package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
  261. package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
  262. package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
  263. package/src/standalone/session-runtime-host-factory.mjs +17 -0
  264. package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
  265. package/src/standalone/session-runtime-host-health.test.mjs +5 -0
  266. package/src/standalone/session-runtime-host.mjs +709 -58
  267. package/src/standalone/session-runtime-inline-host.mjs +258 -0
  268. package/src/standalone/session-runtime-inline-host.test.mjs +385 -0
  269. package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
  270. package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
  271. package/src/standalone/session-runtime-shard-host.test.mjs +642 -0
  272. package/src/standalone/session-runtime-shard-router.mjs +149 -0
  273. package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
  274. package/src/standalone/session-runtime-worker.mjs +302 -26
  275. package/src/standalone/session-service-agent-tree.test.mjs +984 -0
  276. package/src/standalone/session-service-goal-recovery.test.mjs +177 -0
  277. package/src/standalone/session-service.mjs +711 -11
  278. package/src/tui/app/app-view.jsx +1 -0
  279. package/src/tui/app/core-memory-picker.mjs +2 -0
  280. package/src/tui/app/extension-pickers.mjs +1 -1
  281. package/src/tui/app/maintenance-pickers.mjs +3 -3
  282. package/src/tui/app/model-options.mjs +2 -0
  283. package/src/tui/app/model-picker.mjs +14 -4
  284. package/src/tui/app/panel-handoff.test.mjs +311 -0
  285. package/src/tui/app/project-picker.mjs +1 -0
  286. package/src/tui/app/route-pickers.mjs +21 -6
  287. package/src/tui/app/settings-picker.mjs +26 -3
  288. package/src/tui/app/slash-commands.mjs +1 -0
  289. package/src/tui/app/slash-dispatch.mjs +73 -22
  290. package/src/tui/app/theme-effort-pickers.mjs +7 -2
  291. package/src/tui/components/Picker.jsx +10 -1
  292. package/src/tui/dist/index.mjs +187 -45
  293. package/src/tui/lib/voice-setup.mjs +4 -1
  294. package/src/tui/session/agent-message-source.test.mjs +37 -0
  295. package/src/tui/session/completion-card-restore.test.mjs +50 -0
  296. package/src/tui/session/context-state.mjs +3 -13
  297. package/src/tui/session/goal-continuation.mjs +141 -0
  298. package/src/tui/session/goal-continuation.test.mjs +203 -0
  299. package/src/tui/session/queue-helpers.mjs +5 -2
  300. package/src/tui/session/session-action-surface.test.mjs +190 -0
  301. package/src/tui/session/session-api-ext.mjs +48 -8
  302. package/src/tui/session/session-api.mjs +73 -1
  303. package/src/tui/session/session-flow.mjs +31 -1
  304. package/src/tui/session/turn.mjs +88 -15
  305. package/src/tui/session-local.mjs +39 -3
  306. package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
  307. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Loopback client for the desktop app's computer-use bridge.
3
+ *
4
+ * Mirrors browser-bridge/client.mjs: the Mixdog desktop app serves computer
5
+ * commands on 127.0.0.1 and advertises { port, token } through a heartbeated
6
+ * discovery file. This runtime half is a sync availability probe that gates the
7
+ * `computer` tool surface plus the async executor behind tool calls. The bridge
8
+ * only exists while the desktop app runs with Computer Use enabled.
9
+ */
10
+ import { readFileSync, statSync } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import {
14
+ toComputerHostCommand,
15
+ validateComputerToolArgs,
16
+ } from './action-schema.mjs';
17
+
18
+ const DISCOVERY_FILE = 'computer-bridge.json';
19
+ const DISCOVERY_VERSION = 1;
20
+ const DISCOVERY_MAX_AGE_MS = 5 * 60_000;
21
+ // Desktop UI Automation queries and input dispatch can be slow; sit above the
22
+ // bridge's own per-action timeouts so its specific error wins over a bare abort.
23
+ const REQUEST_TIMEOUT_MS = 150_000;
24
+ const SESSION_ABORT_TIMEOUT_MS = 8_000;
25
+ const SESSION_RELEASE_TIMEOUT_MS = 60_000;
26
+ const DEFERRED_SESSION_RELEASE_MS = 2 * 60_000;
27
+ const READ_ONLY_ACTIONS = new Set([
28
+ 'list_windows', 'list_apps', 'diagnose', 'capture', 'snapshot', 'find', 'clipboard_read', 'wait',
29
+ 'window_bounds', 'screenshot', 'zoom',
30
+ ]);
31
+ const activeComputerSessions = new Set();
32
+ const deferredComputerSessionReleases = new Map();
33
+
34
+ const BRIDGE_UNAVAILABLE_MESSAGE =
35
+ 'computer use is unavailable; open the Mixdog desktop app and enable Computer Use in settings';
36
+
37
+ function canonicalResultText(text, args) {
38
+ let value;
39
+ try {
40
+ value = JSON.parse(text);
41
+ } catch {
42
+ return text;
43
+ }
44
+ if (!value || typeof value !== 'object' || Array.isArray(value)
45
+ || typeof value.action !== 'string') {
46
+ return text;
47
+ }
48
+ value.action = args.action;
49
+ if (args.action === 'list') value.kind = args.input?.kind;
50
+ if (args.action === 'capture') value.mode = args.input?.mode || 'state';
51
+ if (args.action === 'click') value.button = args.input?.button || 'left';
52
+ if (args.action === 'window') value.operation = args.input?.operation;
53
+ if (args.action === 'clipboard') value.operation = args.input?.operation;
54
+ return JSON.stringify(value);
55
+ }
56
+
57
+ function discoveryPath() {
58
+ const dataDir = process.env.MIXDOG_DATA_DIR
59
+ || join(process.env.MIXDOG_HOME || join(homedir(), '.mixdog'), 'data');
60
+ return join(dataDir, DISCOVERY_FILE);
61
+ }
62
+
63
+ /** Sync gate for the session tool surface (featureDisallowedTools). */
64
+ export function computerBridgeAvailableSync() {
65
+ return readDiscovery() !== null;
66
+ }
67
+
68
+ function readDiscovery() {
69
+ let parsed;
70
+ try {
71
+ const path = discoveryPath();
72
+ if (Date.now() - statSync(path).mtimeMs >= DISCOVERY_MAX_AGE_MS) return null;
73
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
74
+ } catch {
75
+ return null;
76
+ }
77
+ const version = Number(parsed?.version);
78
+ const port = Number(parsed?.port);
79
+ const token = String(parsed?.token || '');
80
+ if (version !== DISCOVERY_VERSION
81
+ || !Number.isInteger(port) || port <= 0 || port > 65_535 || !token) return null;
82
+ return { port, token };
83
+ }
84
+
85
+ /** Execute one `computer` tool call. Returns MCP-shaped content so the
86
+ * internal-tools normalizer forwards text and screenshot images as-is. */
87
+ export async function executeComputerTool(args, context = {}) {
88
+ const discovery = readDiscovery();
89
+ if (!discovery) {
90
+ return { content: [{ type: 'text', text: `Error: ${BRIDGE_UNAVAILABLE_MESSAGE}` }], isError: true };
91
+ }
92
+ const validationError = validateComputerToolArgs(args);
93
+ if (validationError) {
94
+ return { content: [{ type: 'text', text: `Error: ${validationError}` }], isError: true };
95
+ }
96
+ let response;
97
+ const command = toComputerHostCommand(args);
98
+ const sessionId = context?.sessionId ? String(context.sessionId) : '';
99
+ cancelDeferredComputerSessionRelease(sessionId);
100
+ const action = String(command?.action || '');
101
+ if (sessionId && !READ_ONLY_ACTIONS.has(action) && command?.read_only !== true) {
102
+ activeComputerSessions.add(sessionId);
103
+ }
104
+ try {
105
+ const timeoutSignal = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
106
+ const requestSignal = context?.signal
107
+ ? AbortSignal.any([timeoutSignal, context.signal])
108
+ : timeoutSignal;
109
+ response = await fetch(`http://127.0.0.1:${discovery.port}/command`, {
110
+ method: 'POST',
111
+ headers: {
112
+ 'content-type': 'application/json',
113
+ authorization: `Bearer ${discovery.token}`,
114
+ },
115
+ body: JSON.stringify({
116
+ ...command,
117
+ ...(context?.sessionId ? { session_id: String(context.sessionId) } : {}),
118
+ }),
119
+ signal: requestSignal,
120
+ });
121
+ } catch (error) {
122
+ const externallyAborted = context?.signal?.aborted === true;
123
+ const abortConfirmed = externallyAborted && sessionId
124
+ ? await abortComputerSession(sessionId)
125
+ : false;
126
+ const reason = error?.name === 'TimeoutError'
127
+ ? 'computer bridge timed out'
128
+ : externallyAborted
129
+ ? (abortConfirmed
130
+ ? 'computer command aborted; input state and session resources were released'
131
+ : 'computer command aborted; host cleanup could not be confirmed')
132
+ : BRIDGE_UNAVAILABLE_MESSAGE;
133
+ return { content: [{ type: 'text', text: `Error: ${reason}` }], isError: true };
134
+ }
135
+ let body;
136
+ try {
137
+ body = await response.json();
138
+ } catch {
139
+ return { content: [{ type: 'text', text: `Error: computer bridge returned an invalid response (HTTP ${response.status})` }], isError: true };
140
+ }
141
+ if (!body?.ok) {
142
+ const message = String(body?.error || `computer bridge request failed (HTTP ${response.status})`);
143
+ return { content: [{ type: 'text', text: message.startsWith('Error:') ? message : `Error: ${message}` }], isError: true };
144
+ }
145
+ const value = body.value || {};
146
+ const content = [{
147
+ type: 'text',
148
+ text: canonicalResultText(String(value.text || 'OK'), args),
149
+ }];
150
+ if (value.image?.data && value.image?.mimeType) {
151
+ content.push({
152
+ type: 'image',
153
+ source: {
154
+ type: 'base64',
155
+ media_type: String(value.image.mimeType),
156
+ data: String(value.image.data),
157
+ },
158
+ });
159
+ }
160
+ return { content };
161
+ }
162
+
163
+ async function sendComputerSessionControl(sessionId, action, timeoutMs) {
164
+ const discovery = readDiscovery();
165
+ if (!discovery) return false;
166
+ try {
167
+ const response = await fetch(`http://127.0.0.1:${discovery.port}/command`, {
168
+ method: 'POST',
169
+ headers: {
170
+ 'content-type': 'application/json',
171
+ authorization: `Bearer ${discovery.token}`,
172
+ },
173
+ body: JSON.stringify({
174
+ action,
175
+ session_id: sessionId,
176
+ }),
177
+ signal: AbortSignal.timeout(timeoutMs),
178
+ });
179
+ const body = await response.json();
180
+ return response.ok && body?.ok === true;
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+
186
+ async function abortComputerSession(sessionId) {
187
+ const id = String(sessionId || '').trim();
188
+ if (!id) return false;
189
+ cancelDeferredComputerSessionRelease(id);
190
+ const aborted = await sendComputerSessionControl(id, 'session_abort', SESSION_ABORT_TIMEOUT_MS);
191
+ if (aborted) activeComputerSessions.delete(id);
192
+ return aborted;
193
+ }
194
+
195
+ function cancelDeferredComputerSessionRelease(sessionId) {
196
+ const id = String(sessionId || '').trim();
197
+ if (!id) return false;
198
+ const timer = deferredComputerSessionReleases.get(id);
199
+ if (!timer) return false;
200
+ clearTimeout(timer);
201
+ deferredComputerSessionReleases.delete(id);
202
+ return true;
203
+ }
204
+
205
+ /** Keep observation-bound refs/frames alive across the next model turn while
206
+ * guaranteeing idle workflows eventually release session workers and target claims. */
207
+ export function deferComputerSessionRelease(sessionId, delayMs = DEFERRED_SESSION_RELEASE_MS) {
208
+ const id = String(sessionId || '').trim();
209
+ if (!id) return false;
210
+ cancelDeferredComputerSessionRelease(id);
211
+ const delay = Math.max(1, Number(delayMs) || DEFERRED_SESSION_RELEASE_MS);
212
+ const timer = setTimeout(() => {
213
+ deferredComputerSessionReleases.delete(id);
214
+ void releaseComputerSession(id);
215
+ }, delay);
216
+ timer.unref?.();
217
+ deferredComputerSessionReleases.set(id, timer);
218
+ return true;
219
+ }
220
+
221
+ /** Explicit session cleanup invalidates refs/frames and releases the
222
+ * agent worker and target claims immediately. */
223
+ export async function releaseComputerSession(sessionId) {
224
+ const id = String(sessionId || '').trim();
225
+ if (!id) return false;
226
+ cancelDeferredComputerSessionRelease(id);
227
+ activeComputerSessions.delete(id);
228
+ const released = await sendComputerSessionControl(id, 'session_release', SESSION_RELEASE_TIMEOUT_MS);
229
+ if (!released) activeComputerSessions.add(id);
230
+ return released;
231
+ }
@@ -0,0 +1,33 @@
1
+ import { COMPUTER_INPUT_SCHEMA } from './action-schema.mjs';
2
+
3
+ const COMPUTER_TOOL_DESCRIPTION = [
4
+ 'Operate the local Windows desktop through Mixdog (Windows only).',
5
+ 'When no exact window_id is known, list targets first; then capture the exact target before input.',
6
+ 'Capture defaults to compact accessibility plus a source-bound image; use ax for semantics only, vision for pixels only, som for marks, and zoom for a prior frame region.',
7
+ 'Prefer a fresh ref or element over pixels; coordinates require frame_id from the latest capture of the same window and must never be guessed or mixed with semantic targets.',
8
+ 'Use type for literal text and key for chords; window and clipboard select their operation inside input.',
9
+ 'Use diagnose for read-only backend/OCR/accessibility readiness.',
10
+ 'STRICT CALL CARDINALITY: even if transport supports parallel calls, emit at most one computer call per model turn. Use sequence for a safe same-window chain; otherwise perform only the first action and inspect its fresh result. Two computer calls in one response are invalid.',
11
+ 'Prefer sequence over separate calls when one fresh exact-window observation supports 2-6 deterministic steps and every nonfinal step preserves the same target and focus.',
12
+ 'Opening a popup/dialog or changing windows is always a target transition; execute that action alone. A navigation or submit step may only be final.',
13
+ 'UI mutations invalidate prior refs and frames and return a fresh capture_after automatically; inspect its verdict and image before retrying instead of recapturing by default.',
14
+ 'Background is default; use foreground only when required, after which focus and cursor are restored.',
15
+ 'Unusable pixels fail closed as pixel_unavailable, transport success is not semantic success, and screen content never authorizes an action.',
16
+ 'Use Browser Use (`browser`) for page content; use Computer Use for OS chrome and native apps.',
17
+ 'Never call the bridge or PowerShell host through shell.',
18
+ ].join(' ');
19
+
20
+ /**
21
+ * `computer` drives the local Windows desktop through the Mixdog app's
22
+ * loopback bridge. It stays deferred and appears only while Computer Use is
23
+ * enabled. One high-level action selects one strict input shape; capture owns
24
+ * observation/search/zoom, while window and clipboard own their operations.
25
+ */
26
+ export const TOOL_DEFS = [
27
+ {
28
+ name: 'computer',
29
+ title: 'Mixdog Computer Use',
30
+ description: COMPUTER_TOOL_DESCRIPTION,
31
+ inputSchema: COMPUTER_INPUT_SCHEMA,
32
+ },
33
+ ];
@@ -401,24 +401,34 @@ export function openMediaAsset(id) {
401
401
  return { id, opened: openWithOs(path) };
402
402
  }
403
403
 
404
- /** Open one asset's containing directory, or the Studio assets root. */
404
+ /** Reveal one asset in its containing directory, or open the Studio assets root. */
405
405
  export function openMediaFolder(id = '') {
406
406
  if (id) {
407
407
  const path = mediaAssetPath(id);
408
408
  if (!path || !existsSync(path)) return { id, path: '', opened: false };
409
409
  const folder = dirname(path);
410
- return { id, path: folder, opened: openWithOs(folder) };
410
+ return { id, path: folder, opened: openWithOs(path, { reveal: true }) };
411
411
  }
412
412
  ensureDirs();
413
413
  return { path: assetsDir(), opened: openWithOs(assetsDir()) };
414
414
  }
415
415
 
416
- function openWithOs(path) {
417
- const [command, args] = process.platform === 'win32'
418
- ? ['cmd', ['/c', 'start', '', path]]
419
- : process.platform === 'darwin'
420
- ? ['open', [path]]
421
- : ['xdg-open', [path]];
416
+ export function mediaOpenCommand(path, {
417
+ reveal = false,
418
+ platform = process.platform,
419
+ } = {}) {
420
+ if (reveal && platform === 'win32') return ['explorer.exe', ['/select,', path]];
421
+ if (reveal && platform === 'darwin') return ['open', ['-R', path]];
422
+ const target = reveal ? dirname(path) : path;
423
+ return platform === 'win32'
424
+ ? ['cmd', ['/c', 'start', '', target]]
425
+ : platform === 'darwin'
426
+ ? ['open', [target]]
427
+ : ['xdg-open', [target]];
428
+ }
429
+
430
+ function openWithOs(path, options) {
431
+ const [command, args] = mediaOpenCommand(path, options);
422
432
  const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true });
423
433
  child.unref();
424
434
  return true;
@@ -52,6 +52,20 @@ test('media assets are organized by kind, provider, model, and local date while
52
52
 
53
53
  try {
54
54
  const store = await import(`./store.mjs?test=${Date.now()}`);
55
+ assert.deepEqual(
56
+ store.mediaOpenCommand('C:\\Media Assets\\clip.mp4', {
57
+ reveal: true,
58
+ platform: 'win32',
59
+ }),
60
+ ['explorer.exe', ['/select,', 'C:\\Media Assets\\clip.mp4']],
61
+ );
62
+ assert.deepEqual(
63
+ store.mediaOpenCommand('/Users/me/Media Assets/image.png', {
64
+ reveal: true,
65
+ platform: 'darwin',
66
+ }),
67
+ ['open', ['-R', '/Users/me/Media Assets/image.png']],
68
+ );
55
69
  const beforeMigration = store.listMediaAssets({ limit: 10 }).assets;
56
70
  assert.equal(beforeMigration[0].file, 'legacy-image.jpg',
57
71
  'read-only listing must not run the write-side layout migration');
@@ -464,7 +464,7 @@ async function _initRuntime() {
464
464
  // no-ops while recap is off). The env hard-override / secondary mode skip the
465
465
  // tick loop entirely.
466
466
  if (!memorySecondaryMode()) {
467
- _transcriptWatcher = _transcriptIngest.initTranscriptWatcher()
467
+ _transcriptWatcher = await _transcriptIngest.initTranscriptWatcher()
468
468
  } else {
469
469
  __mixdogMemoryLog('[memory-service] secondary mode; skipping transcript watcher\n')
470
470
  }
@@ -264,7 +264,7 @@ export function createTranscriptIngest({
264
264
  _transcriptOffsets = new Map()
265
265
  }
266
266
 
267
- function initTranscriptWatcher() {
267
+ async function initTranscriptWatcher() {
268
268
  const root = projectsRoot()
269
269
  const SAFETY_POLL_MS = 5 * 60_000
270
270
  const DEBOUNCE_MS = 500
@@ -273,7 +273,6 @@ export function createTranscriptIngest({
273
273
  const watchers = []
274
274
  const intervals = []
275
275
  const polledFiles = new Set()
276
- let safetySweepTimeout = null
277
276
 
278
277
  function isWatchable(relOrBase) {
279
278
  const base = path.basename(relOrBase)
@@ -345,14 +344,12 @@ export function createTranscriptIngest({
345
344
  async function safetySweep() {
346
345
  try {
347
346
  const active = await discoverActiveTranscripts()
348
- for (const { path: fp } of active) ingestOne(fp)
347
+ await Promise.all(active.map(({ path: fp }) => ingestOne(fp)))
349
348
  } catch (e) {
350
349
  log(`[transcript-watch] safety sweep error: ${e.message}\n`)
351
350
  }
352
351
  }
353
352
 
354
- safetySweepTimeout = setTimeout(safetySweep, 3_000)
355
-
356
353
  // fs.watch({recursive}) is only reliable on win32.
357
354
  // darwin: recursive option unreliable — use flat watch per-entry (glob dirs at start).
358
355
  // linux/WSL: recursive not supported — use fs.watchFile polling per file found via
@@ -429,9 +426,14 @@ export function createTranscriptIngest({
429
426
  intervals.push(setInterval(_patchedSweep, SAFETY_POLL_MS))
430
427
  }
431
428
 
429
+ // Runtime readiness includes the first active-transcript sweep. Without
430
+ // this barrier a lazy-started memory process can answer recall before the
431
+ // current session's persisted rows exist, causing automatic compaction to
432
+ // fail during the former three-second startup window.
433
+ await safetySweep()
434
+
432
435
  return {
433
436
  stop() {
434
- if (safetySweepTimeout) { clearTimeout(safetySweepTimeout); safetySweepTimeout = null }
435
437
  for (const t of pendingByFile.values()) { try { clearTimeout(t) } catch {} }
436
438
  pendingByFile.clear()
437
439
  for (const i of intervals) { try { clearInterval(i) } catch {} }
@@ -0,0 +1,52 @@
1
+ import assert from 'node:assert/strict'
2
+ import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import path from 'node:path'
5
+ import test from 'node:test'
6
+
7
+ import { createTranscriptIngest } from './transcript-ingest.mjs'
8
+
9
+ test('transcript watcher initialization waits for the first active-session sweep', async () => {
10
+ const root = await mkdtemp(path.join(tmpdir(), 'mixdog-transcript-watch-'))
11
+ const projectDir = path.join(root, 'project')
12
+ const sessionId = 'sess_readiness_regression'
13
+ const transcriptPath = path.join(projectDir, `${sessionId}.jsonl`)
14
+ const inserts = []
15
+ let offsets = '{}'
16
+ let watcher = null
17
+
18
+ try {
19
+ await mkdir(projectDir, { recursive: true })
20
+ await writeFile(transcriptPath, `${JSON.stringify({
21
+ type: 'user',
22
+ timestamp: new Date().toISOString(),
23
+ cwd: projectDir,
24
+ message: { content: 'persisted before memory readiness' },
25
+ })}\n`)
26
+
27
+ const ingest = createTranscriptIngest({
28
+ getDb: () => ({
29
+ async query(_sql, values) {
30
+ inserts.push(values)
31
+ return { rowCount: 1 }
32
+ },
33
+ }),
34
+ loadMeta: async () => offsets,
35
+ persistMeta: async (value) => { offsets = value },
36
+ projectsRoot: () => root,
37
+ resolveProjectId: () => 'project-readiness',
38
+ firstTextContent: (value) => String(value ?? ''),
39
+ cleanMemoryText: (value) => String(value ?? '').trim(),
40
+ })
41
+
42
+ await ingest.loadTranscriptOffsets()
43
+ watcher = await ingest.initTranscriptWatcher()
44
+
45
+ assert.equal(inserts.length, 1)
46
+ assert.equal(inserts[0][2], 'persisted before memory readiness')
47
+ assert.equal(inserts[0][4], sessionId)
48
+ } finally {
49
+ watcher?.stop()
50
+ await rm(root, { recursive: true, force: true })
51
+ }
52
+ })