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,283 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createInterface } from 'node:readline';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ export function resolveMicrosoftOfficeHostScriptPath(scriptPath) {
6
+ return scriptPath.replace(/([\\/][^\\/]+\.asar)([\\/])/i, '$1.unpacked$2');
7
+ }
8
+
9
+ const HOST_SCRIPT = resolveMicrosoftOfficeHostScriptPath(
10
+ fileURLToPath(new URL('./office-com-host.ps1', import.meta.url)),
11
+ );
12
+ const SESSION_HOST_SCRIPT = resolveMicrosoftOfficeHostScriptPath(
13
+ fileURLToPath(new URL('./office-com-session-host.ps1', import.meta.url)),
14
+ );
15
+ const DEFAULT_TIMEOUT_MS = 90_000;
16
+ const sessionClients = new Map();
17
+ let nextRequestId = 1;
18
+
19
+ function powershellProgram() {
20
+ return process.env.SystemRoot
21
+ ? `${process.env.SystemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
22
+ : 'powershell.exe';
23
+ }
24
+
25
+ export function microsoftOfficeComSupported() {
26
+ return process.platform === 'win32';
27
+ }
28
+
29
+ function spawnPowerShell(script) {
30
+ return spawn(powershellProgram(), [
31
+ '-NoLogo',
32
+ '-NoProfile',
33
+ '-NonInteractive',
34
+ '-Sta',
35
+ '-ExecutionPolicy',
36
+ 'Bypass',
37
+ '-File',
38
+ script,
39
+ ], {
40
+ windowsHide: true,
41
+ stdio: ['pipe', 'pipe', 'pipe'],
42
+ });
43
+ }
44
+
45
+ async function callMicrosoftOfficeOnce(payload, {
46
+ timeoutMs = DEFAULT_TIMEOUT_MS,
47
+ signal = null,
48
+ } = {}) {
49
+ return await new Promise((resolve) => {
50
+ const child = spawnPowerShell(HOST_SCRIPT);
51
+ let stdout = '';
52
+ let stderr = '';
53
+ let settled = false;
54
+ const finish = (value) => {
55
+ if (settled) return;
56
+ settled = true;
57
+ clearTimeout(timer);
58
+ signal?.removeEventListener?.('abort', onAbort);
59
+ resolve(value);
60
+ };
61
+ const onAbort = () => {
62
+ try { child.kill(); } catch {}
63
+ finish({ ok: false, cancelled: true, error: 'Microsoft Office operation was cancelled' });
64
+ };
65
+ const timer = setTimeout(() => {
66
+ try { child.kill(); } catch {}
67
+ finish({ ok: false, error: `Microsoft Office operation timed out after ${timeoutMs}ms` });
68
+ }, timeoutMs);
69
+ child.stdout.setEncoding('utf8');
70
+ child.stderr.setEncoding('utf8');
71
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
72
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
73
+ child.on('error', (error) => finish({ ok: false, error: error?.message || String(error) }));
74
+ child.on('close', (code) => {
75
+ if (settled) return;
76
+ const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
77
+ const last = lines.at(-1) || '';
78
+ try {
79
+ const parsed = JSON.parse(last);
80
+ finish(parsed);
81
+ } catch {
82
+ finish({
83
+ ok: false,
84
+ error: stderr.trim() || last || `Microsoft Office host exited with code ${code}`,
85
+ });
86
+ }
87
+ });
88
+ if (signal?.aborted) return onAbort();
89
+ signal?.addEventListener?.('abort', onAbort, { once: true });
90
+ child.stdin.end(JSON.stringify(payload ?? {}), 'utf8');
91
+ });
92
+ }
93
+
94
+ function stopSessionClient(client, error = '') {
95
+ if (!client || client.closed) return;
96
+ client.closed = true;
97
+ sessionClients.delete(client.sessionId);
98
+ const failure = {
99
+ ok: false,
100
+ backend: 'microsoft-office-com',
101
+ error: error || 'Microsoft Office session host stopped',
102
+ };
103
+ for (const pending of client.pending.values()) {
104
+ clearTimeout(pending.timer);
105
+ pending.resolve(failure);
106
+ }
107
+ client.pending.clear();
108
+ try { client.readline.close(); } catch {}
109
+ try { client.child.stdin.end(); } catch {}
110
+ try { client.child.kill(); } catch {}
111
+ if (client.ownership === 'owned' && Number(client.appPid) > 0) {
112
+ try { process.kill(Number(client.appPid)); } catch {}
113
+ }
114
+ }
115
+
116
+ function createSessionClient(sessionId) {
117
+ const child = spawnPowerShell(SESSION_HOST_SCRIPT);
118
+ child.unref();
119
+ child.stdin.unref?.();
120
+ child.stdout.unref?.();
121
+ child.stderr.unref?.();
122
+ const client = {
123
+ sessionId,
124
+ child,
125
+ pending: new Map(),
126
+ stderr: '',
127
+ closed: false,
128
+ readline: createInterface({ input: child.stdout }),
129
+ };
130
+ client.readline.on('line', (line) => {
131
+ const text = String(line || '').trim();
132
+ if (!text) return;
133
+ let message;
134
+ try {
135
+ message = JSON.parse(text);
136
+ } catch {
137
+ return;
138
+ }
139
+ const requestId = String(message.requestId || '');
140
+ const pending = client.pending.get(requestId);
141
+ if (!pending) return;
142
+ client.pending.delete(requestId);
143
+ clearTimeout(pending.timer);
144
+ delete message.requestId;
145
+ pending.resolve(message);
146
+ });
147
+ child.stderr.setEncoding('utf8');
148
+ child.stderr.on('data', (chunk) => {
149
+ client.stderr = `${client.stderr}${chunk}`.slice(-16_000);
150
+ });
151
+ child.on('error', (error) => stopSessionClient(client, error?.message || String(error)));
152
+ child.on('close', (code) => {
153
+ if (client.closed) return;
154
+ stopSessionClient(
155
+ client,
156
+ client.stderr.trim() || `Microsoft Office session host exited with code ${code}`,
157
+ );
158
+ });
159
+ sessionClients.set(sessionId, client);
160
+ return client;
161
+ }
162
+
163
+ async function requestSessionClient(client, payload, timeoutMs = DEFAULT_TIMEOUT_MS, signal = null) {
164
+ if (!client || client.closed) {
165
+ return { ok: false, backend: 'microsoft-office-com', error: 'Microsoft Office session host is unavailable' };
166
+ }
167
+ const requestId = `office_request_${nextRequestId++}`;
168
+ return await new Promise((resolve) => {
169
+ const settle = (value) => {
170
+ signal?.removeEventListener?.('abort', onAbort);
171
+ resolve(value);
172
+ };
173
+ const onAbort = () => {
174
+ client.pending.delete(requestId);
175
+ clearTimeout(timer);
176
+ settle({ ok: false, backend: 'microsoft-office-com', cancelled: true, error: 'Microsoft Office operation was cancelled' });
177
+ stopSessionClient(client, 'Microsoft Office operation was cancelled');
178
+ };
179
+ const timer = setTimeout(() => {
180
+ client.pending.delete(requestId);
181
+ settle({
182
+ ok: false,
183
+ backend: 'microsoft-office-com',
184
+ error: `Microsoft Office operation timed out after ${timeoutMs}ms`,
185
+ });
186
+ stopSessionClient(client, `Microsoft Office session timed out after ${timeoutMs}ms`);
187
+ }, timeoutMs);
188
+ if (signal?.aborted) return onAbort();
189
+ signal?.addEventListener?.('abort', onAbort, { once: true });
190
+ client.pending.set(requestId, { resolve: settle, timer });
191
+ try {
192
+ client.child.stdin.write(`${JSON.stringify({ ...payload, requestId })}\n`, 'utf8');
193
+ } catch (error) {
194
+ clearTimeout(timer);
195
+ client.pending.delete(requestId);
196
+ settle({ ok: false, backend: 'microsoft-office-com', error: error?.message || String(error) });
197
+ stopSessionClient(client, error?.message || String(error));
198
+ }
199
+ });
200
+ }
201
+
202
+ export async function openMicrosoftOfficeSession(payload, {
203
+ timeoutMs = DEFAULT_TIMEOUT_MS,
204
+ signal = null,
205
+ } = {}) {
206
+ if (!microsoftOfficeComSupported()) {
207
+ return { ok: false, available: false, error: 'Microsoft Office COM is available on Windows only' };
208
+ }
209
+ const sessionId = String(payload?.session || '');
210
+ if (!sessionId) return { ok: false, error: 'Microsoft Office session id is required' };
211
+ if (sessionClients.has(sessionId)) return { ok: false, error: `Microsoft Office session already exists: ${sessionId}` };
212
+ const client = createSessionClient(sessionId);
213
+ const result = await requestSessionClient(client, { ...payload, action: 'open_session' }, timeoutMs, signal);
214
+ if (!result.ok) {
215
+ stopSessionClient(client, result.error);
216
+ } else {
217
+ client.ownership = result.ownership;
218
+ client.appPid = result.appPid;
219
+ }
220
+ return result;
221
+ }
222
+
223
+ export async function closeMicrosoftOfficeSession(sessionId, {
224
+ save = false,
225
+ timeoutMs = DEFAULT_TIMEOUT_MS,
226
+ closeTimeoutMs = 15_000,
227
+ signal = null,
228
+ } = {}) {
229
+ const client = sessionClients.get(String(sessionId));
230
+ if (!client) return { ok: true, closed: true, alreadyClosed: true };
231
+ const result = await requestSessionClient(client, {
232
+ action: 'close_session',
233
+ session: String(sessionId),
234
+ save,
235
+ }, timeoutMs, signal);
236
+ if (result.ok) {
237
+ sessionClients.delete(String(sessionId));
238
+ try { client.child.stdin.end(); } catch {}
239
+ const graceful = client.closed || client.child.exitCode !== null
240
+ ? true
241
+ : await Promise.race([
242
+ new Promise((resolve) => client.child.once('close', () => resolve(true))),
243
+ new Promise((resolve) => setTimeout(() => resolve(false), closeTimeoutMs)),
244
+ ]);
245
+ client.closed = true;
246
+ try { client.readline.close(); } catch {}
247
+ if (!graceful) {
248
+ try { client.child.kill(); } catch {}
249
+ if (result.ownership === 'owned' && Number(result.appPid) > 0) {
250
+ try { process.kill(Number(result.appPid)); } catch {}
251
+ }
252
+ result.forcedProcessCleanup = result.ownership === 'owned';
253
+ }
254
+ }
255
+ return result;
256
+ }
257
+
258
+ export async function callMicrosoftOffice(payload, {
259
+ timeoutMs = DEFAULT_TIMEOUT_MS,
260
+ signal = null,
261
+ } = {}) {
262
+ if (!microsoftOfficeComSupported()) {
263
+ return { ok: false, available: false, error: 'Microsoft Office COM is available on Windows only' };
264
+ }
265
+ const sessionId = String(payload?.session || '');
266
+ if (sessionId) {
267
+ const client = sessionClients.get(sessionId);
268
+ if (!client) {
269
+ return { ok: false, backend: 'microsoft-office-com', error: `Unknown Microsoft Office session: ${sessionId}` };
270
+ }
271
+ return await requestSessionClient(client, payload, timeoutMs, signal);
272
+ }
273
+ return await callMicrosoftOfficeOnce(payload, { timeoutMs, signal });
274
+ }
275
+
276
+ export async function detectMicrosoftOffice({ format = '', path = '' } = {}) {
277
+ return await callMicrosoftOfficeOnce({ action: 'detect', format, path }, { timeoutMs: 20_000 });
278
+ }
279
+
280
+ export function resetMicrosoftOfficeSessionsForTest() {
281
+ for (const client of sessionClients.values()) stopSessionClient(client, 'Microsoft Office test reset');
282
+ sessionClients.clear();
283
+ }
@@ -0,0 +1,183 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ function plainObject(value) {
4
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
5
+ }
6
+
7
+ function clone(value) {
8
+ return value == null ? value : JSON.parse(JSON.stringify(value));
9
+ }
10
+
11
+ function stableValue(value) {
12
+ if (Array.isArray(value)) return value.map(stableValue);
13
+ if (!plainObject(value)) return value;
14
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
15
+ }
16
+
17
+ function safeId(value, label) {
18
+ const normalized = String(value || '').trim().toLowerCase();
19
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(normalized)) {
20
+ throw new Error(`Office content ${label} must use 1-64 lowercase letters, digits, dots, underscores, or hyphens`);
21
+ }
22
+ return normalized;
23
+ }
24
+
25
+ function normalizeSource(value) {
26
+ if (typeof value === 'string') return value.trim();
27
+ if (!plainObject(value)) return null;
28
+ const document = String(value.document || '').trim();
29
+ if (!document) return null;
30
+ return {
31
+ document,
32
+ ...(value.target ? { target: String(value.target) } : {}),
33
+ ...(value.label ? { label: String(value.label) } : {}),
34
+ };
35
+ }
36
+
37
+ function sourceText(value) {
38
+ if (typeof value === 'string') return value;
39
+ if (!plainObject(value)) return '';
40
+ const document = String(value.document || '');
41
+ const target = String(value.target || '');
42
+ const label = String(value.label || '');
43
+ return `${document}${target ? `#${target}` : ''}${label ? ` (${label})` : ''}`;
44
+ }
45
+
46
+ export function normalizeOfficeContentModel(value) {
47
+ if (value == null) return null;
48
+ if (!plainObject(value)) throw new Error('Office design.content must be an object');
49
+ const rawFacts = Array.isArray(value.facts) ? value.facts : [];
50
+ const rawClaims = Array.isArray(value.claims) ? value.claims : [];
51
+ if (rawFacts.length > 1_000 || rawClaims.length > 500) {
52
+ throw new Error('Office content model exceeds the supported fact or claim count');
53
+ }
54
+ const factIds = new Set();
55
+ const facts = rawFacts.map((fact) => {
56
+ if (!plainObject(fact)) throw new Error('Office content facts must be objects');
57
+ const id = safeId(fact.id, 'fact id');
58
+ if (factIds.has(id)) throw new Error(`Office content model has duplicate fact id ${id}`);
59
+ factIds.add(id);
60
+ return {
61
+ id,
62
+ label: String(fact.label || id),
63
+ value: clone(fact.value),
64
+ ...(fact.unit ? { unit: String(fact.unit) } : {}),
65
+ ...(fact.detail ? { detail: String(fact.detail) } : {}),
66
+ ...(fact.numberFormat ? { numberFormat: String(fact.numberFormat) } : {}),
67
+ ...(normalizeSource(fact.source) ? { source: normalizeSource(fact.source) } : {}),
68
+ };
69
+ });
70
+ const claimIds = new Set();
71
+ const claims = rawClaims.map((claim) => {
72
+ if (!plainObject(claim)) throw new Error('Office content claims must be objects');
73
+ const id = safeId(claim.id, 'claim id');
74
+ if (claimIds.has(id)) throw new Error(`Office content model has duplicate claim id ${id}`);
75
+ claimIds.add(id);
76
+ const factRefs = [...new Set((Array.isArray(claim.factIds) ? claim.factIds : [])
77
+ .map((entry) => safeId(entry, `claim ${id} fact reference`)))];
78
+ for (const factId of factRefs) {
79
+ if (!factIds.has(factId)) throw new Error(`Office content claim ${id} references unknown fact ${factId}`);
80
+ }
81
+ return {
82
+ id,
83
+ text: String(claim.text || '').trim(),
84
+ factIds: factRefs,
85
+ ...(claim.implication ? { implication: String(claim.implication) } : {}),
86
+ };
87
+ });
88
+ const normalized = {
89
+ packageId: value.packageId ? safeId(value.packageId, 'package id') : '',
90
+ audience: String(value.audience || ''),
91
+ objective: String(value.objective || ''),
92
+ decision: String(value.decision || ''),
93
+ period: String(value.period || ''),
94
+ facts,
95
+ claims,
96
+ };
97
+ normalized.fingerprint = createHash('sha256')
98
+ .update(JSON.stringify(stableValue(normalized)))
99
+ .digest('hex');
100
+ return normalized;
101
+ }
102
+
103
+ export function summarizeOfficeContentModel(model) {
104
+ if (!model) return null;
105
+ return {
106
+ packageId: model.packageId,
107
+ fingerprint: model.fingerprint,
108
+ audience: model.audience,
109
+ objective: model.objective,
110
+ decision: model.decision,
111
+ period: model.period,
112
+ factCount: model.facts.length,
113
+ claimCount: model.claims.length,
114
+ sourcedFactCount: model.facts.filter((fact) => fact.source).length,
115
+ };
116
+ }
117
+
118
+ export function bindOfficeContent(operation, model) {
119
+ if (!model || !plainObject(operation)) return { operation, binding: null };
120
+ const facts = new Map(model.facts.map((fact) => [fact.id, fact]));
121
+ const claims = new Map(model.claims.map((claim) => [claim.id, claim]));
122
+ const used = new Set();
123
+ const fact = (id) => {
124
+ const normalized = safeId(id, 'fact reference');
125
+ const resolved = facts.get(normalized);
126
+ if (!resolved) throw new Error(`Office semantic operation references unknown fact ${normalized}`);
127
+ used.add(normalized);
128
+ return resolved;
129
+ };
130
+ const resolveValue = (entry) => {
131
+ if (Array.isArray(entry)) return entry.map(resolveValue);
132
+ if (!plainObject(entry)) return entry;
133
+ if (entry.factId) {
134
+ const resolved = fact(entry.factId);
135
+ const field = String(entry.field || 'value');
136
+ if (!Object.hasOwn(resolved, field)) throw new Error(`Office fact ${resolved.id} has no field ${field}`);
137
+ return clone(resolved[field]);
138
+ }
139
+ return Object.fromEntries(Object.entries(entry).map(([key, child]) => [key, resolveValue(child)]));
140
+ };
141
+ const bound = clone(operation);
142
+ if (bound.claimId) {
143
+ const claimId = safeId(bound.claimId, 'claim reference');
144
+ const claim = claims.get(claimId);
145
+ if (!claim) throw new Error(`Office semantic operation references unknown claim ${claimId}`);
146
+ for (const factId of claim.factIds) used.add(factId);
147
+ if (!bound.title) bound.title = claim.text;
148
+ if (!bound.takeaway) bound.takeaway = claim.implication || claim.text;
149
+ }
150
+ if (Array.isArray(bound.metrics)) {
151
+ bound.metrics = bound.metrics.map((metric) => {
152
+ if (!plainObject(metric) || !metric.factId) return resolveValue(metric);
153
+ const resolved = fact(metric.factId);
154
+ return {
155
+ ...metric,
156
+ value: metric.value ?? clone(resolved.value),
157
+ label: metric.label || resolved.label,
158
+ detail: metric.detail || resolved.detail || resolved.unit || '',
159
+ numberFormat: metric.numberFormat || resolved.numberFormat || '',
160
+ };
161
+ });
162
+ }
163
+ for (const key of ['rows', 'table', 'chart', 'columns', 'steps']) {
164
+ if (bound[key] != null) bound[key] = resolveValue(bound[key]);
165
+ }
166
+ const sources = [...used]
167
+ .map((id) => facts.get(id)?.source)
168
+ .filter(Boolean)
169
+ .map(sourceText)
170
+ .filter((entry, index, values) => values.indexOf(entry) === index);
171
+ if (!bound.source && sources.length) bound.source = sources.join('; ');
172
+ return {
173
+ operation: bound,
174
+ binding: {
175
+ packageId: model.packageId,
176
+ contentFingerprint: model.fingerprint,
177
+ claimId: bound.claimId ? String(bound.claimId).toLowerCase() : '',
178
+ factIds: [...used].sort(),
179
+ sources,
180
+ },
181
+ };
182
+ }
183
+
@@ -0,0 +1,105 @@
1
+ import assert from 'node:assert/strict';
2
+ import { pathToFileURL } from 'node:url';
3
+
4
+ import { executeOfficeTool } from './index.mjs';
5
+
6
+ const CASES = [
7
+ {
8
+ format: 'docx',
9
+ backend: 'mixdog-ooxml',
10
+ operation: 'append_text',
11
+ required: ['op', 'text'],
12
+ },
13
+ {
14
+ format: 'xlsx',
15
+ backend: 'microsoft-office-com',
16
+ operation: 'add_chart',
17
+ required: ['op', 'range'],
18
+ },
19
+ {
20
+ format: 'pptx',
21
+ backend: 'microsoft-office-com',
22
+ operation: 'add_textbox',
23
+ required: ['op', 'slide', 'text'],
24
+ },
25
+ {
26
+ format: 'pdf',
27
+ backend: 'mixdog-pdf',
28
+ operation: 'add_text',
29
+ required: ['op', 'text'],
30
+ },
31
+ ];
32
+
33
+ function resultValue(result) {
34
+ const text = result?.content?.[0]?.text || '';
35
+ if (result?.isError) throw new Error(text);
36
+ return JSON.parse(text);
37
+ }
38
+
39
+ export async function runOfficeContractBenchmark() {
40
+ let deterministicToolCalls = 0;
41
+ const call = async (input) => {
42
+ deterministicToolCalls += 1;
43
+ return executeOfficeTool(input);
44
+ };
45
+
46
+ const summary = resultValue(await call({ action: 'describe' }));
47
+ assert.equal(summary.actions.length, 23);
48
+ for (const removed of ['set', 'add', 'remove', 'move']) assert.equal(summary.actions.includes(removed), false);
49
+ for (const entry of Object.values(summary.formats)) {
50
+ assert.equal(entry.operations, undefined);
51
+ assert.ok(entry.operationCount > 0);
52
+ }
53
+
54
+ const results = [];
55
+ for (const scenario of CASES) {
56
+ const described = resultValue(await call({ action: 'describe', ...scenario }));
57
+ assert.equal(described.operation.name, scenario.operation);
58
+ assert.equal(described.operation.supported, true);
59
+ assert.deepEqual(described.operation.input.required, scenario.required);
60
+ results.push({
61
+ format: scenario.format,
62
+ backend: scenario.backend,
63
+ operation: scenario.operation,
64
+ responseChars: JSON.stringify(described).length,
65
+ accurate: true,
66
+ });
67
+ }
68
+
69
+ const invalid = await call({
70
+ action: 'describe',
71
+ format: 'xlsx',
72
+ operation: 'add_chrt',
73
+ });
74
+ assert.equal(invalid.isError, true);
75
+ const invalidError = invalid.content?.[0]?.text || '';
76
+ assert.match(invalidError, /Did you mean: add_chart/);
77
+ assert.match(invalidError, /describe/);
78
+
79
+ return {
80
+ version: 2,
81
+ createdAt: new Date().toISOString(),
82
+ measurementKind: 'deterministic-contract-probe',
83
+ modelBehaviorMeasured: false,
84
+ deterministicToolCalls,
85
+ retries: 0,
86
+ unnecessaryRereads: 0,
87
+ broadCatalogMaterializations: 0,
88
+ publicActionCount: summary.actions.length,
89
+ requirementFulfillment: {
90
+ passed: results.length,
91
+ total: CASES.length,
92
+ rate: results.length / CASES.length,
93
+ },
94
+ summaryResponseChars: JSON.stringify(summary).length,
95
+ maxTargetedResponseChars: Math.max(...results.map((entry) => entry.responseChars)),
96
+ actionableErrorProbe: true,
97
+ accurate: results.every((entry) => entry.accurate),
98
+ results,
99
+ };
100
+ }
101
+
102
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
103
+ const report = await runOfficeContractBenchmark();
104
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
105
+ }