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,496 @@
1
+ import { copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
2
+ import { basename, dirname, extname, join, resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ import { executeOfficeTool, resetOfficeSessionsForTest } from './index.mjs';
6
+ import { renderPortableOoxml } from './portable-ooxml.mjs';
7
+ import { renderPdfPages } from './pdf-render.mjs';
8
+ import { compareRenderedPages } from './visual-diff.mjs';
9
+
10
+ const OFFICE_EXTENSIONS = new Set([
11
+ '.docx', '.dotx', '.docm', '.dotm',
12
+ '.xlsx', '.xltx', '.xlsm', '.xltm',
13
+ '.pptx', '.potx', '.pptm', '.potm',
14
+ '.csv', '.tsv', '.pdf',
15
+ ]);
16
+ const SNAPSHOT_LIMIT = 10_000;
17
+ const SNAPSHOT_MAX_CHARS = 100_000;
18
+ const LARGE_SPREADSHEET_VISUAL_CELLS = 50_000;
19
+ const DEFAULT_DOCUMENT_TIMEOUT_MS = 10 * 60_000;
20
+
21
+ export function officeBenchmarkSnapshotRequest(session, cursor = null) {
22
+ return {
23
+ action: 'snapshot',
24
+ session,
25
+ ...(cursor ? { cursor } : { limit: SNAPSHOT_LIMIT }),
26
+ maxChars: SNAPSHOT_MAX_CHARS,
27
+ };
28
+ }
29
+
30
+ export function officeBenchmarkVisualPolicy({
31
+ format = '',
32
+ totalCells = 0,
33
+ } = {}) {
34
+ const spreadsheet = /^(xlsx|xlsm|xltx|xltm)$/i.test(String(format));
35
+ if (spreadsheet && Number(totalCells) > LARGE_SPREADSHEET_VISUAL_CELLS) {
36
+ return {
37
+ mode: 'performance-only',
38
+ reason: `Spreadsheet has ${Number(totalCells)} cells; full-page rendering is excluded from the large-load gate.`,
39
+ };
40
+ }
41
+ return { mode: 'full' };
42
+ }
43
+
44
+ function resultValue(result) {
45
+ const text = result?.content?.[0]?.text || '';
46
+ if (result?.isError) throw new Error(text);
47
+ return JSON.parse(text);
48
+ }
49
+
50
+ async function timed(operation) {
51
+ const startedAt = performance.now();
52
+ try {
53
+ return {
54
+ ok: true,
55
+ value: await operation(),
56
+ durationMs: Number((performance.now() - startedAt).toFixed(2)),
57
+ };
58
+ } catch (error) {
59
+ return {
60
+ ok: false,
61
+ error: error?.message || String(error),
62
+ durationMs: Number((performance.now() - startedAt).toFixed(2)),
63
+ };
64
+ }
65
+ }
66
+
67
+ function wordOperations(index) {
68
+ return [
69
+ ...Array.from({ length: index === 7 ? 1_000 : 120 }, (_, paragraph) => ({
70
+ op: 'append_text',
71
+ text: `Document ${index + 1} paragraph ${paragraph + 1}: Office Use benchmark content with Unicode 한글 日本語.`,
72
+ })),
73
+ { op: 'set_header_footer', section: 1, kind: 'primary', header: true, text: `Benchmark document ${index + 1}` },
74
+ { op: 'add_page_numbers', section: 1, includeTotal: true },
75
+ { op: 'insert_toc', lowerHeadingLevel: 1, upperHeadingLevel: 3 },
76
+ ];
77
+ }
78
+
79
+ function workbookOperations(index) {
80
+ if (index === 7) {
81
+ return [
82
+ {
83
+ op: 'set_range',
84
+ sheet: 'Sheet1',
85
+ range: 'A1:A100000',
86
+ values: Array.from({ length: 100_000 }, (_, row) => [row + 1]),
87
+ },
88
+ { op: 'set_formula', sheet: 'Sheet1', cell: 'B100000', formula: '=SUM(A1:A100000)' },
89
+ { op: 'freeze_panes', sheet: 'Sheet1', row: 2, column: 0 },
90
+ ];
91
+ }
92
+ const rows = Array.from({ length: 500 }, (_, row) => (
93
+ Array.from({ length: 10 }, (_, column) => column === 0
94
+ ? `Item ${index + 1}-${row + 1}`
95
+ : (row + 1) * (column + 1))
96
+ ));
97
+ return [
98
+ { op: 'set_range', sheet: 'Sheet1', range: 'A1:J500', values: rows },
99
+ { op: 'set_formula', sheet: 'Sheet1', cell: 'K2', formula: '=SUM(B2:J2)' },
100
+ { op: 'add_note', sheet: 'Sheet1', cell: 'B2', text: 'Source: generated Office benchmark corpus' },
101
+ { op: 'add_table', sheet: 'Sheet1', range: 'A1:K500', name: `Benchmark${index + 1}` },
102
+ { op: 'add_conditional_format', sheet: 'Sheet1', range: 'K2:K500', formula: '=K2>1000', fillColor: 'FFF2CC' },
103
+ { op: 'freeze_panes', sheet: 'Sheet1', row: 2, column: 1 },
104
+ { op: 'define_name', name: `BenchmarkTotal${index + 1}`, refersTo: 'Sheet1!$K$2' },
105
+ ];
106
+ }
107
+
108
+ function presentationOperations(index) {
109
+ const operations = [];
110
+ for (let slide = 1; slide <= (index === 7 ? 100 : 12); slide += 1) {
111
+ operations.push(
112
+ { op: 'add_slide' },
113
+ {
114
+ op: 'add_textbox',
115
+ slide,
116
+ text: `Benchmark ${index + 1}: slide ${slide} takeaway`,
117
+ properties: { left: 40, top: 30, width: 620, height: 60, fontSize: 26 },
118
+ },
119
+ {
120
+ op: 'add_shape',
121
+ slide,
122
+ shapeType: 'rounded_rectangle',
123
+ paragraphs: [{ text: `Metric ${slide}: ${index * 100 + slide}`, fontSize: 18 }],
124
+ properties: { left: 60, top: 140, width: 260, height: 100, fillColor: 'D9EAF7', lineColor: '2F5597' },
125
+ },
126
+ {
127
+ op: 'add_table',
128
+ slide,
129
+ values: [['Metric', 'Value'], ['Actual', index * 100 + slide], ['Plan', index * 100 + slide + 5]],
130
+ properties: { left: 360, top: 140, width: 300, height: 120 },
131
+ },
132
+ { op: 'set_notes', slide, text: `Source: benchmark.xlsx, Sheet1!K${slide + 1}` },
133
+ );
134
+ }
135
+ return operations;
136
+ }
137
+
138
+ export async function generateOfficeBenchmarkCorpus(directory, {
139
+ documentsPerFormat = 8,
140
+ onProgress = null,
141
+ } = {}) {
142
+ const root = resolve(directory);
143
+ await mkdir(root, { recursive: true });
144
+ const generated = [];
145
+ const formats = [
146
+ ['docx', wordOperations],
147
+ ['xlsx', workbookOperations],
148
+ ['pptx', presentationOperations],
149
+ ];
150
+ for (const [format, operationsFor] of formats) {
151
+ for (let index = 0; index < documentsPerFormat; index += 1) {
152
+ const path = join(root, `${format}-benchmark-${String(index + 1).padStart(2, '0')}.${format}`);
153
+ const created = resultValue(await executeOfficeTool({
154
+ action: 'create',
155
+ path,
156
+ format,
157
+ mode: 'background',
158
+ overwrite: true,
159
+ }, { cwd: root }));
160
+ resultValue(await executeOfficeTool({
161
+ action: 'batch',
162
+ session: created.session,
163
+ operations: operationsFor(index),
164
+ save: true,
165
+ }, { cwd: root }));
166
+ resultValue(await executeOfficeTool({ action: 'close', session: created.session, save: true }, { cwd: root }));
167
+ generated.push(path);
168
+ onProgress?.({
169
+ phase: 'generate',
170
+ format,
171
+ index: index + 1,
172
+ total: documentsPerFormat,
173
+ path,
174
+ message: `generated ${format} ${index + 1}/${documentsPerFormat}`,
175
+ });
176
+ }
177
+ }
178
+ return generated;
179
+ }
180
+
181
+ async function corpusFiles(directory) {
182
+ const entries = await readdir(directory, { withFileTypes: true });
183
+ return entries
184
+ .filter((entry) => entry.isFile() && OFFICE_EXTENSIONS.has(extname(entry.name).toLowerCase()))
185
+ .map((entry) => join(directory, entry.name))
186
+ .sort();
187
+ }
188
+
189
+ async function renderAllPdfPages(path, { signal = null } = {}) {
190
+ const first = await renderPdfPages(path, { pages: [1], maxWidth: 900, signal });
191
+ const images = [...first.images];
192
+ for (let start = 2; start <= first.pageCount; start += 12) {
193
+ if (signal?.aborted) throw new Error('Office benchmark document timed out');
194
+ const pages = Array.from({ length: Math.min(12, first.pageCount - start + 1) }, (_, index) => start + index);
195
+ const rendered = await renderPdfPages(path, { pages, maxWidth: 900, signal });
196
+ images.push(...rendered.images);
197
+ }
198
+ return { pageCount: first.pageCount, images };
199
+ }
200
+
201
+ async function fullVisualDiff(source, output, outputDirectory, { signal = null } = {}) {
202
+ const stem = basename(source, extname(source));
203
+ const beforePdf = join(outputDirectory, `${stem}.before.pdf`);
204
+ const afterPdf = join(outputDirectory, `${stem}.after.pdf`);
205
+ if (extname(source).toLowerCase() === '.pdf') {
206
+ await copyFile(source, beforePdf);
207
+ await copyFile(output, afterPdf);
208
+ } else {
209
+ await renderPortableOoxml(source, beforePdf, { signal });
210
+ await renderPortableOoxml(output, afterPdf, { signal });
211
+ }
212
+ const [before, after] = await Promise.all([
213
+ renderAllPdfPages(beforePdf, { signal }),
214
+ renderAllPdfPages(afterPdf, { signal }),
215
+ ]);
216
+ if (signal?.aborted) throw new Error('Office benchmark document timed out');
217
+ const compared = await compareRenderedPages(before.images, after.images, afterPdf);
218
+ return {
219
+ available: compared.available,
220
+ pageCountBefore: before.pageCount,
221
+ pageCountAfter: after.pageCount,
222
+ changedPercent: compared.changedPercent,
223
+ pages: compared.pages,
224
+ images: compared.images.map(({ data, ...image }) => image),
225
+ };
226
+ }
227
+
228
+ async function benchmarkDocument(path, outputDirectory, {
229
+ onProgress = null,
230
+ documentTimeoutMs = DEFAULT_DOCUMENT_TIMEOUT_MS,
231
+ } = {}) {
232
+ const extension = extname(path);
233
+ const output = join(outputDirectory, `${basename(path, extension)}.roundtrip${extension}`);
234
+ const report = {
235
+ path,
236
+ output,
237
+ bytes: (await stat(path)).size,
238
+ format: extension.slice(1).toLowerCase(),
239
+ timeoutMs: documentTimeoutMs,
240
+ };
241
+ const controller = new AbortController();
242
+ const timer = setTimeout(() => controller.abort(), documentTimeoutMs);
243
+ timer.unref?.();
244
+ let session = '';
245
+ try {
246
+ const context = { cwd: dirnameOf(path), signal: controller.signal };
247
+ const opened = await timed(async () => resultValue(await executeOfficeTool({
248
+ action: 'open',
249
+ path,
250
+ output,
251
+ mode: 'background',
252
+ }, context)));
253
+ report.open = opened;
254
+ if (!opened.ok) {
255
+ report.timedOut = controller.signal.aborted;
256
+ return report;
257
+ }
258
+ session = opened.value.session;
259
+ const snapshot = await timed(async () => {
260
+ let cursor = null;
261
+ let calls = 0;
262
+ let returned = 0;
263
+ let scanned = 0;
264
+ let total = 0;
265
+ let finalLimit = 0;
266
+ do {
267
+ const page = resultValue(await executeOfficeTool(
268
+ officeBenchmarkSnapshotRequest(session, cursor),
269
+ context,
270
+ ));
271
+ const pagination = page.document?.pagination || {};
272
+ calls += 1;
273
+ returned += Number(pagination.returned || 0);
274
+ scanned += Number(pagination.scanned || 0);
275
+ total = Math.max(total, Number(pagination.total || 0));
276
+ finalLimit = Number(pagination.limit || finalLimit);
277
+ cursor = pagination.nextCursor || null;
278
+ if (calls === 1 || calls % 25 === 0 || !cursor) {
279
+ onProgress?.({
280
+ phase: 'snapshot',
281
+ path,
282
+ calls,
283
+ scanned,
284
+ total,
285
+ message: `snapshot ${basename(path)}: ${scanned}/${total || '?'} units in ${calls} call(s)`,
286
+ });
287
+ }
288
+ } while (cursor && calls < 10_000);
289
+ if (cursor) throw new Error('Office benchmark snapshot exceeded 10,000 pagination calls');
290
+ return { calls, returned, scanned, total, finalLimit };
291
+ });
292
+ report.snapshot = snapshot;
293
+ if (controller.signal.aborted) {
294
+ report.timedOut = true;
295
+ report.success = false;
296
+ return report;
297
+ }
298
+ report.validation = await timed(async () => resultValue(await executeOfficeTool({
299
+ action: 'validate',
300
+ session,
301
+ compatibility: true,
302
+ }, context)));
303
+ report.save = await timed(async () => resultValue(await executeOfficeTool({
304
+ action: 'save',
305
+ session,
306
+ }, context)));
307
+ report.visualPolicy = officeBenchmarkVisualPolicy({
308
+ format: report.format,
309
+ totalCells: report.snapshot.value?.total || 0,
310
+ });
311
+ report.visualRequired = report.visualPolicy.mode === 'full'
312
+ && (report.format === 'pdf' || report.validation.value?.compatibility?.available === true);
313
+ if (report.visualRequired) {
314
+ const previewPath = join(outputDirectory, `${basename(path, extension)}.preview.pdf`);
315
+ report.render = await timed(async () => resultValue(await executeOfficeTool({
316
+ action: 'render',
317
+ session,
318
+ output: previewPath,
319
+ pages: [1],
320
+ maxWidth: 900,
321
+ }, context)));
322
+ report.visual = await timed(async () => await fullVisualDiff(path, output, outputDirectory, {
323
+ signal: controller.signal,
324
+ }));
325
+ } else {
326
+ const reason = report.visualPolicy.mode !== 'full'
327
+ ? report.visualPolicy.reason
328
+ : 'LibreOffice compatibility rendering is unavailable; visual comparison was not run.';
329
+ report.render = {
330
+ ok: true,
331
+ skipped: true,
332
+ reason,
333
+ durationMs: 0,
334
+ };
335
+ report.visual = {
336
+ ok: true,
337
+ skipped: true,
338
+ available: false,
339
+ reason,
340
+ durationMs: 0,
341
+ };
342
+ }
343
+ report.timedOut = controller.signal.aborted;
344
+ report.visualThresholdPercent = 0.5;
345
+ report.success = !report.timedOut
346
+ && report.snapshot.ok
347
+ && report.validation.ok
348
+ && report.validation.value.ok
349
+ && report.render.ok
350
+ && (!report.visualRequired || (
351
+ report.visual.ok
352
+ && report.visual.value.available
353
+ && report.visual.value.pageCountBefore === report.visual.value.pageCountAfter
354
+ && report.visual.value.changedPercent <= report.visualThresholdPercent
355
+ ));
356
+ report.roundTrip = {
357
+ lostProtectedParts: report.validation.value?.baseline?.lostProtectedParts || [],
358
+ changedProtectedParts: report.validation.value?.baseline?.changedProtectedParts || [],
359
+ digitalSignatureInvalidated: report.validation.value?.security?.digitalSignatureInvalidated === true,
360
+ };
361
+ } finally {
362
+ clearTimeout(timer);
363
+ if (session) {
364
+ await executeOfficeTool({ action: 'close', session }, { cwd: dirnameOf(path) }).catch(() => {});
365
+ }
366
+ }
367
+ return report;
368
+ }
369
+
370
+ function dirnameOf(path) {
371
+ return dirname(path);
372
+ }
373
+
374
+ function summarize(results) {
375
+ const successful = results.filter((entry) => entry.success).length;
376
+ const durations = results.map((entry) => (
377
+ Number(entry.open?.durationMs || 0)
378
+ + Number(entry.snapshot?.durationMs || 0)
379
+ + Number(entry.validation?.durationMs || 0)
380
+ + Number(entry.render?.durationMs || 0)
381
+ + Number(entry.visual?.durationMs || 0)
382
+ ));
383
+ return {
384
+ documents: results.length,
385
+ successful,
386
+ failed: results.length - successful,
387
+ successRate: results.length ? Number((successful / results.length).toFixed(4)) : 0,
388
+ totalDurationMs: Number(durations.reduce((sum, value) => sum + value, 0).toFixed(2)),
389
+ averageDurationMs: results.length
390
+ ? Number((durations.reduce((sum, value) => sum + value, 0) / results.length).toFixed(2))
391
+ : 0,
392
+ regressions: results.filter((entry) => !entry.success).map((entry) => ({
393
+ path: entry.path,
394
+ error: entry.open?.error
395
+ || entry.snapshot?.error
396
+ || entry.validation?.error
397
+ || entry.render?.error
398
+ || (entry.timedOut ? 'document timed out' : 'verification failed'),
399
+ })),
400
+ };
401
+ }
402
+
403
+ export async function runOfficeBenchmark({
404
+ corpus,
405
+ output,
406
+ baseline = '',
407
+ generate = false,
408
+ documentsPerFormat = 8,
409
+ documentTimeoutMs = DEFAULT_DOCUMENT_TIMEOUT_MS,
410
+ onProgress = null,
411
+ } = {}) {
412
+ const corpusDirectory = resolve(corpus);
413
+ const outputDirectory = resolve(output || join(corpusDirectory, 'benchmark-output'));
414
+ try {
415
+ await mkdir(outputDirectory, { recursive: true });
416
+ if (generate) await generateOfficeBenchmarkCorpus(corpusDirectory, { documentsPerFormat, onProgress });
417
+ const files = await corpusFiles(corpusDirectory);
418
+ const results = [];
419
+ for (let index = 0; index < files.length; index += 1) {
420
+ const path = files[index];
421
+ onProgress?.({
422
+ phase: 'document-start',
423
+ index: index + 1,
424
+ total: files.length,
425
+ path,
426
+ message: `checking ${index + 1}/${files.length}: ${basename(path)}`,
427
+ });
428
+ const result = await benchmarkDocument(path, outputDirectory, { onProgress, documentTimeoutMs });
429
+ results.push(result);
430
+ onProgress?.({
431
+ phase: 'document-complete',
432
+ index: index + 1,
433
+ total: files.length,
434
+ path,
435
+ success: result.success === true,
436
+ message: `completed ${index + 1}/${files.length}: ${basename(path)} (${result.success ? 'pass' : 'fail'})`,
437
+ });
438
+ }
439
+ const report = {
440
+ version: 1,
441
+ createdAt: new Date().toISOString(),
442
+ corpus: corpusDirectory,
443
+ output: outputDirectory,
444
+ summary: summarize(results),
445
+ results,
446
+ };
447
+ if (baseline) {
448
+ const previous = JSON.parse(await readFile(resolve(baseline), 'utf8'));
449
+ report.comparison = {
450
+ baseline: resolve(baseline),
451
+ successRateDelta: Number((report.summary.successRate - Number(previous.summary?.successRate || 0)).toFixed(4)),
452
+ averageDurationDeltaMs: Number((report.summary.averageDurationMs - Number(previous.summary?.averageDurationMs || 0)).toFixed(2)),
453
+ newRegressions: report.summary.regressions.filter((entry) => !(previous.summary?.regressions || []).some((prior) => prior.path === entry.path)),
454
+ };
455
+ }
456
+ const reportPath = join(outputDirectory, 'office-benchmark-report.json');
457
+ await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
458
+ return { reportPath, report };
459
+ } finally {
460
+ resetOfficeSessionsForTest();
461
+ }
462
+ }
463
+
464
+ function argument(name, fallback = '') {
465
+ const index = process.argv.indexOf(name);
466
+ return index >= 0 ? process.argv[index + 1] : fallback;
467
+ }
468
+
469
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
470
+ const corpus = argument('--corpus');
471
+ if (!corpus) throw new Error('Usage: node benchmark.mjs --corpus DIR [--output DIR] [--generate] [--baseline REPORT]');
472
+ const terminate = (signal) => {
473
+ resetOfficeSessionsForTest();
474
+ process.stderr.write(`[office-benchmark] interrupted by ${signal}; owned Office sessions were stopped\n`);
475
+ process.exit(signal === 'SIGINT' ? 130 : 143);
476
+ };
477
+ const onSigint = () => terminate('SIGINT');
478
+ const onSigterm = () => terminate('SIGTERM');
479
+ process.once('SIGINT', onSigint);
480
+ process.once('SIGTERM', onSigterm);
481
+ try {
482
+ const result = await runOfficeBenchmark({
483
+ corpus,
484
+ output: argument('--output'),
485
+ baseline: argument('--baseline'),
486
+ generate: process.argv.includes('--generate'),
487
+ documentsPerFormat: Number(argument('--per-format', '8')),
488
+ documentTimeoutMs: Number(argument('--document-timeout-ms', String(DEFAULT_DOCUMENT_TIMEOUT_MS))),
489
+ onProgress: ({ message }) => process.stderr.write(`[office-benchmark] ${message}\n`),
490
+ });
491
+ process.stdout.write(`${JSON.stringify({ reportPath: result.reportPath, summary: result.report.summary }, null, 2)}\n`);
492
+ } finally {
493
+ process.removeListener('SIGINT', onSigint);
494
+ process.removeListener('SIGTERM', onSigterm);
495
+ }
496
+ }