mixdog 0.9.152 → 0.9.154

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 (300) hide show
  1. package/LICENSES/browser-import-NOTICE.txt +11 -0
  2. package/NOTICE.md +11 -0
  3. package/README.md +8 -10
  4. package/package.json +19 -3
  5. package/scripts/computer-task-cost-report.mjs +144 -0
  6. package/scripts/run-office-live-tests.mjs +32 -0
  7. package/src/agents/front-worker/AGENT.md +13 -0
  8. package/src/agents/front-worker/agent.json +6 -0
  9. package/src/agents/heavy-worker/AGENT.md +10 -11
  10. package/src/agents/heavy-worker/agent.json +1 -1
  11. package/src/agents/maintainer/AGENT.md +0 -2
  12. package/src/agents/reviewer/AGENT.md +9 -13
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/agents/security/AGENT.md +14 -0
  15. package/src/agents/security/agent.json +6 -0
  16. package/src/agents/worker/AGENT.md +8 -8
  17. package/src/agents/worker/agent.json +1 -1
  18. package/src/agents/writer/AGENT.md +14 -0
  19. package/src/agents/writer/agent.json +6 -0
  20. package/src/rules/agent/00-common.md +4 -7
  21. package/src/rules/lead/01-general.md +0 -3
  22. package/src/rules/shared/10-tool-workflow.md +10 -2
  23. package/src/rules/shared/60-verification.md +2 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
  25. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  26. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
  27. package/src/runtime/agent/orchestrator/config.mjs +7 -2
  28. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
  29. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
  30. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
  31. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
  32. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
  33. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
  34. package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
  35. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
  36. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
  37. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
  38. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
  39. package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
  40. package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
  41. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +11 -23
  43. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
  44. package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
  45. package/src/runtime/agent/orchestrator/session/context-utils.mjs +86 -11
  46. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
  47. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
  48. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
  49. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
  50. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
  51. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
  52. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
  53. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
  54. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
  55. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
  56. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
  57. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
  58. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
  59. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
  60. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +39 -15
  61. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
  62. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
  63. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
  64. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -2
  65. package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
  66. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
  67. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
  68. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
  69. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
  70. package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
  73. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
  74. package/src/runtime/attachments/pdf-extract.mjs +3 -0
  75. package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
  76. package/src/runtime/browser-bridge/action-schema.mjs +251 -0
  77. package/src/runtime/browser-bridge/client.mjs +75 -26
  78. package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
  79. package/src/runtime/computer-bridge/action-schema.mjs +653 -0
  80. package/src/runtime/computer-bridge/client.mjs +211 -28
  81. package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
  82. package/src/runtime/office/assurance-benchmark.mjs +358 -0
  83. package/src/runtime/office/assurance.mjs +833 -0
  84. package/src/runtime/office/benchmark.mjs +496 -0
  85. package/src/runtime/office/capabilities.mjs +551 -0
  86. package/src/runtime/office/com-adapter.mjs +283 -0
  87. package/src/runtime/office/content-model.mjs +183 -0
  88. package/src/runtime/office/contract-benchmark.mjs +105 -0
  89. package/src/runtime/office/design-library.mjs +1562 -0
  90. package/src/runtime/office/design-system.mjs +1966 -0
  91. package/src/runtime/office/index.mjs +2413 -0
  92. package/src/runtime/office/journal.mjs +82 -0
  93. package/src/runtime/office/office-com-host.ps1 +3781 -0
  94. package/src/runtime/office/office-com-session-host.ps1 +855 -0
  95. package/src/runtime/office/ooxml-validator.mjs +271 -0
  96. package/src/runtime/office/pagination.mjs +132 -0
  97. package/src/runtime/office/pdf-adapter.mjs +591 -0
  98. package/src/runtime/office/pdf-analysis.mjs +372 -0
  99. package/src/runtime/office/pdf-render.mjs +167 -0
  100. package/src/runtime/office/pdf-security.mjs +63 -0
  101. package/src/runtime/office/polish-benchmark.mjs +190 -0
  102. package/src/runtime/office/portable-ooxml.mjs +1867 -0
  103. package/src/runtime/office/quality-live-benchmark.mjs +486 -0
  104. package/src/runtime/office/quality-pipeline.mjs +136 -0
  105. package/src/runtime/office/tabular.mjs +302 -0
  106. package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
  107. package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
  108. package/src/runtime/office/tool-defs.mjs +113 -0
  109. package/src/runtime/office/visual-diff.mjs +89 -0
  110. package/src/runtime/office/xlsx-assertions.mjs +172 -0
  111. package/src/runtime/office/xlsx-contract.mjs +147 -0
  112. package/src/runtime/shared/agent-route-config.mjs +9 -0
  113. package/src/runtime/shared/tool-execution-contract.mjs +16 -0
  114. package/src/runtime/shared/tool-surface.mjs +9 -0
  115. package/src/session-runtime/context-status.mjs +9 -3
  116. package/src/session-runtime/deferred-tool-delta.mjs +104 -0
  117. package/src/session-runtime/goal-runtime.mjs +617 -197
  118. package/src/session-runtime/lifecycle-api.mjs +50 -8
  119. package/src/session-runtime/provider-request-snapshot.mjs +33 -9
  120. package/src/session-runtime/resource-api.mjs +31 -94
  121. package/src/session-runtime/runtime-core.mjs +78 -26
  122. package/src/session-runtime/session-lifecycle.mjs +34 -12
  123. package/src/session-runtime/session-title.mjs +2 -2
  124. package/src/session-runtime/session-turn-api.mjs +15 -14
  125. package/src/session-runtime/tool-catalog-data.mjs +1 -1
  126. package/src/session-runtime/tool-catalog.mjs +38 -99
  127. package/src/session-runtime/tool-defs.mjs +6 -1
  128. package/src/session-runtime/tool-surface.mjs +18 -8
  129. package/src/session-runtime/workflow-agents-api.mjs +8 -25
  130. package/src/session-runtime/workflow.mjs +8 -6
  131. package/src/standalone/agent-tool/helpers.mjs +2 -2
  132. package/src/standalone/agent-tool/job-views.mjs +3 -1
  133. package/src/standalone/agent-tool/notify.mjs +5 -1
  134. package/src/standalone/agent-tool/render.mjs +3 -0
  135. package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
  136. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  137. package/src/standalone/agent-tool/worker-index.mjs +123 -0
  138. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  139. package/src/standalone/agent-tool.mjs +57 -10
  140. package/src/standalone/daemon.mjs +105 -6
  141. package/src/standalone/session-runtime-host.mjs +61 -48
  142. package/src/standalone/session-runtime-inline-host.mjs +34 -127
  143. package/src/standalone/session-runtime-worker.mjs +20 -198
  144. package/src/standalone/session-service.mjs +632 -7
  145. package/src/tui/dist/index.mjs +24 -1
  146. package/src/tui/lib/voice-setup.mjs +4 -1
  147. package/src/tui/session/context-state.mjs +2 -13
  148. package/src/tui/session/goal-continuation.mjs +38 -7
  149. package/src/tui/session/session-api-ext.mjs +9 -0
  150. package/src/tui/session/session-api.mjs +15 -1
  151. package/src/tui/session/session-flow.mjs +25 -1
  152. package/src/tui/session/turn.mjs +88 -16
  153. package/src/tui/session-local.mjs +25 -3
  154. package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
  155. package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
  156. package/scripts/native-binary-arch.test.mjs +0 -111
  157. package/scripts/patch-replay.test.mjs +0 -94
  158. package/src/headless-exec.test.mjs +0 -539
  159. package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
  160. package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
  161. package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
  162. package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
  163. package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
  164. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
  165. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
  166. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
  167. package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -142
  168. package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
  169. package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
  170. package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -190
  171. package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
  172. package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
  173. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
  174. package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
  175. package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
  176. package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
  177. package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
  178. package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
  179. package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
  180. package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -70
  182. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
  183. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
  184. package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
  185. package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
  186. package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -583
  187. package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
  188. package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
  189. package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
  192. package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
  193. package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
  194. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
  195. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
  199. package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
  201. package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
  202. package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
  203. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
  204. package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
  206. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
  207. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
  208. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
  209. package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
  210. package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
  211. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
  212. package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
  213. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
  214. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
  215. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
  216. package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
  217. package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
  218. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
  219. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
  220. package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
  221. package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
  222. package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
  223. package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
  224. package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
  225. package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
  226. package/src/runtime/attachments/store.test.mjs +0 -209
  227. package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
  228. package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
  230. package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
  231. package/src/runtime/media/download.test.mjs +0 -52
  232. package/src/runtime/media/renditions.test.mjs +0 -99
  233. package/src/runtime/media/store.test.mjs +0 -159
  234. package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
  235. package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
  236. package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
  237. package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
  238. package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
  239. package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
  240. package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
  241. package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
  242. package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
  243. package/src/runtime/shared/background-tasks.test.mjs +0 -78
  244. package/src/runtime/shared/bounded-download.test.mjs +0 -41
  245. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
  246. package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
  247. package/src/runtime/shared/remote-intent.test.mjs +0 -40
  248. package/src/runtime/shared/resource-admission.test.mjs +0 -43
  249. package/src/runtime/shared/runtime-root.test.mjs +0 -34
  250. package/src/runtime/shared/schedule-time.test.mjs +0 -29
  251. package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
  252. package/src/runtime/shared/skill-document.test.mjs +0 -81
  253. package/src/runtime/shared/tool-surface.test.mjs +0 -87
  254. package/src/runtime/shared/update-checker.test.mjs +0 -33
  255. package/src/runtime/shared/user-cwd.test.mjs +0 -43
  256. package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
  257. package/src/session-runtime/agent-disable.test.mjs +0 -123
  258. package/src/session-runtime/cwd-plugins.test.mjs +0 -75
  259. package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
  260. package/src/session-runtime/global-extensions.test.mjs +0 -28
  261. package/src/session-runtime/goal-runtime.test.mjs +0 -108
  262. package/src/session-runtime/lifecycle-api.test.mjs +0 -21
  263. package/src/session-runtime/model-settings-persist.test.mjs +0 -105
  264. package/src/session-runtime/notification-bus.test.mjs +0 -148
  265. package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
  266. package/src/session-runtime/prewarm.test.mjs +0 -52
  267. package/src/session-runtime/provider-request-snapshot.test.mjs +0 -37
  268. package/src/session-runtime/resource-api-global.test.mjs +0 -69
  269. package/src/session-runtime/session-lifecycle.test.mjs +0 -125
  270. package/src/session-runtime/session-title.test.mjs +0 -36
  271. package/src/session-runtime/skills-api.test.mjs +0 -99
  272. package/src/session-runtime/tool-policy-surface.test.mjs +0 -331
  273. package/src/standalone/agent-tool/helpers.test.mjs +0 -62
  274. package/src/standalone/agent-tool/notify.test.mjs +0 -37
  275. package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -63
  276. package/src/standalone/channel-restart.test.mjs +0 -145
  277. package/src/standalone/channel-session-router.test.mjs +0 -37
  278. package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
  279. package/src/standalone/hook-bus/handlers.test.mjs +0 -88
  280. package/src/standalone/plugin-admin.test.mjs +0 -173
  281. package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
  282. package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
  283. package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
  284. package/src/standalone/session-runtime-host-health.test.mjs +0 -160
  285. package/src/standalone/session-runtime-inline-host.test.mjs +0 -83
  286. package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
  287. package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
  288. package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
  289. package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
  290. package/src/tui/app/panel-epoch.test.mjs +0 -1009
  291. package/src/tui/app/panel-handoff.test.mjs +0 -311
  292. package/src/tui/app/prompt-submit.test.mjs +0 -206
  293. package/src/tui/session/completion-card-restore.test.mjs +0 -111
  294. package/src/tui/session/goal-continuation.test.mjs +0 -84
  295. package/src/tui/session/oauth-flows.test.mjs +0 -134
  296. package/src/tui/session/queue-helpers.test.mjs +0 -46
  297. package/src/tui/session/session-action-surface.test.mjs +0 -108
  298. package/src/tui/session/task-wait-submit.test.mjs +0 -80
  299. package/src/tui/session/tool-result-status.test.mjs +0 -131
  300. package/src/tui/session/tui-steering-persist.test.mjs +0 -104
@@ -1,99 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import {
3
- existsSync,
4
- mkdirSync,
5
- mkdtempSync,
6
- readFileSync,
7
- rmSync,
8
- writeFileSync,
9
- } from 'node:fs';
10
- import { tmpdir } from 'node:os';
11
- import { join } from 'node:path';
12
- import test from 'node:test';
13
-
14
- import * as contextMod from '../runtime/agent/orchestrator/context/collect.mjs';
15
- import { parseSkillDocument } from '../runtime/shared/skill-document.mjs';
16
- import { createSkillsApi } from './skills-api.mjs';
17
-
18
- test('edits existing skills without dropping optional metadata or sibling resources', () => {
19
- const root = mkdtempSync(join(tmpdir(), 'mixdog-skill-edit-'));
20
- const previousDataDir = process.env.MIXDOG_DATA_DIR;
21
- process.env.MIXDOG_DATA_DIR = join(root, 'data');
22
- try {
23
- const cwd = join(root, 'project');
24
- const originalDir = join(process.env.MIXDOG_DATA_DIR, 'skills', 'existing-skill');
25
- mkdirSync(join(originalDir, 'scripts'), { recursive: true });
26
- writeFileSync(join(originalDir, 'SKILL.md'), [
27
- '---',
28
- 'name: existing-skill',
29
- 'description: Use when editing an existing skill.',
30
- 'license: MIT',
31
- 'metadata:',
32
- ' author: example',
33
- 'allowed-tools:',
34
- ' - shell',
35
- '---',
36
- '',
37
- '# Original instructions',
38
- '',
39
- ].join('\n'));
40
- writeFileSync(join(originalDir, 'scripts', 'check.py'), 'print("ok")\n');
41
-
42
- const api = createSkillsApi({ contextMod, getCwd: () => cwd });
43
- const saved = api.saveSkillDocument({
44
- originalName: 'existing-skill',
45
- name: 'renamed-skill',
46
- description: 'Use when the renamed skill is needed.',
47
- instructions: '# Updated instructions',
48
- });
49
- const renamedDir = join(process.env.MIXDOG_DATA_DIR, 'skills', 'renamed-skill');
50
- const parsed = parseSkillDocument(readFileSync(saved.filePath, 'utf8'));
51
-
52
- assert.equal(existsSync(originalDir), false);
53
- assert.equal(saved.filePath, join(renamedDir, 'SKILL.md'));
54
- assert.equal(existsSync(join(renamedDir, 'scripts', 'check.py')), true);
55
- assert.equal(parsed.name, 'renamed-skill');
56
- assert.equal(parsed.description, 'Use when the renamed skill is needed.');
57
- assert.equal(parsed.body, '# Updated instructions\n');
58
- assert.equal(parsed.frontmatter.license, 'MIT');
59
- assert.deepEqual(parsed.frontmatter.metadata, { author: 'example' });
60
- assert.deepEqual(parsed.frontmatter['allowed-tools'], ['shell']);
61
- assert.ok(api.skillsStatus().skills.some((skill) => skill.name === 'renamed-skill'));
62
- } finally {
63
- contextMod.invalidateSkillsCache(join(root, 'project'));
64
- if (previousDataDir === undefined) delete process.env.MIXDOG_DATA_DIR;
65
- else process.env.MIXDOG_DATA_DIR = previousDataDir;
66
- rmSync(root, { recursive: true, force: true });
67
- }
68
- });
69
-
70
- test('creates new global skills with all three standard fields', () => {
71
- const root = mkdtempSync(join(tmpdir(), 'mixdog-skill-create-'));
72
- const previousDataDir = process.env.MIXDOG_DATA_DIR;
73
- process.env.MIXDOG_DATA_DIR = join(root, 'data');
74
- try {
75
- const cwd = join(root, 'project');
76
- const api = createSkillsApi({ contextMod, getCwd: () => cwd });
77
- const created = api.addGlobalSkill({
78
- name: 'new-skill',
79
- description: 'Use when creating a standard skill.',
80
- instructions: '# Instructions\n\nDo the work.',
81
- });
82
- const parsed = parseSkillDocument(readFileSync(created.filePath, 'utf8'));
83
-
84
- assert.equal(parsed.name, 'new-skill');
85
- assert.equal(parsed.description, 'Use when creating a standard skill.');
86
- assert.equal(parsed.body, '# Instructions\n\nDo the work.\n');
87
- assert.equal(created.filePath, join(process.env.MIXDOG_DATA_DIR, 'skills', 'new-skill', 'SKILL.md'));
88
- assert.throws(() => api.addGlobalSkill({
89
- name: 'Invalid_Name',
90
- description: 'Use when invalid.',
91
- instructions: '# Instructions',
92
- }), /lowercase letters/);
93
- } finally {
94
- contextMod.invalidateSkillsCache(join(root, 'project'));
95
- if (previousDataDir === undefined) delete process.env.MIXDOG_DATA_DIR;
96
- else process.env.MIXDOG_DATA_DIR = previousDataDir;
97
- rmSync(root, { recursive: true, force: true });
98
- }
99
- });
@@ -1,331 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { createRequire } from 'node:module';
3
- import { test } from 'node:test';
4
- import { join } from 'node:path';
5
- import { createToolSurface } from './tool-surface.mjs';
6
- import { createToolPolicyRefresh } from './tool-policy-refresh.mjs';
7
- import { toSessionWorkflowMeta, workflowDisallowsAgentTool } from './workflow.mjs';
8
- import { PATCH_TOOL_DEFS } from '../runtime/agent/orchestrator/tools/patch-tool-defs.mjs';
9
- import { DEFERRED_DEFAULT_LEAD_TOOLS } from './tool-catalog-data.mjs';
10
-
11
- const require = createRequire(import.meta.url);
12
- const { omitToolRoutes, buildSharedToolContent, buildAgentRoleContent } = require('../lib/rules-builder.cjs');
13
-
14
- // Tool dependency is declared by `<!-- tools: … -->` markers, so this fixture
15
- // carries the markers rather than prose the builder would have to match.
16
- const SAMPLE_ROUTES = [
17
- '<!-- tools: web_search, web_fetch -->',
18
- '# Research',
19
- '',
20
- '<!-- tools: web_search, web_fetch -->',
21
- '- Research routes:',
22
- '<!-- tools: web_search -->',
23
- ' current or external information discovery→`web_search`;',
24
- '<!-- tools: web_fetch -->',
25
- ' page or documentation body retrieval from a known URL→`web_fetch`.',
26
- '<!-- tools: recall, memory -->',
27
- '# Memory',
28
- '',
29
- '<!-- tools: recall -->',
30
- '- past facts recorded in prior work or sessions→`recall`',
31
- ' (stored history only, never current local state).',
32
- '<!-- tools: memory -->',
33
- '- Durable memory creation or update→`memory`; store a compact English',
34
- ' statement.',
35
- '<!-- tools: memory -->',
36
- '- Use judgment to decide whether a durable memory should be stored.',
37
- ].join('\n');
38
-
39
- test('omitToolRoutes strips markers and keeps every clause when nothing is omitted', () => {
40
- const kept = omitToolRoutes(SAMPLE_ROUTES, []);
41
- assert.equal(kept.includes('<!--'), false);
42
- assert.equal(kept.includes('`web_search`'), true);
43
- assert.equal(kept.includes('`web_fetch`'), true);
44
- assert.equal(kept.includes('`recall`'), true);
45
- assert.equal(kept.includes('`memory`'), true);
46
- assert.equal(kept.includes('# Research'), true);
47
- assert.equal(kept.includes('# Memory'), true);
48
- // The continuation line stays attached to the clause it belongs to.
49
- assert.match(kept, /→`recall`\n\s+\(stored history only, never current local state\)\./);
50
- });
51
-
52
- test('omitToolRoutes drops a clause only when every tool it declares is omitted', () => {
53
- const noSearchOnly = omitToolRoutes(SAMPLE_ROUTES, ['web_search']);
54
- assert.equal(noSearchOnly.includes('`web_search`'), false);
55
- assert.equal(noSearchOnly.includes('`web_fetch`'), true);
56
- // The section and its lead-in survive while one route remains.
57
- assert.equal(noSearchOnly.includes('# Research'), true);
58
- assert.equal(noSearchOnly.includes('Research routes:'), true);
59
-
60
- const noMemoryOnly = omitToolRoutes(SAMPLE_ROUTES, ['memory']);
61
- assert.equal(noMemoryOnly.includes('`recall`'), true);
62
- assert.equal(noMemoryOnly.includes('`memory`'), false);
63
- // Guidance that only makes sense with the memory tool goes with it.
64
- assert.equal(noMemoryOnly.includes('Use judgment'), false);
65
- });
66
-
67
- test('omitToolRoutes drops web search and memory clauses independently', () => {
68
- const noSearch = omitToolRoutes(SAMPLE_ROUTES, ['web_search', 'web_fetch']);
69
- assert.equal(noSearch.includes('`web_search`'), false);
70
- assert.equal(noSearch.includes('`web_fetch`'), false);
71
- assert.equal(noSearch.includes('`recall`'), true);
72
- assert.equal(noSearch.includes('`memory`'), true);
73
-
74
- const noMemory = omitToolRoutes(SAMPLE_ROUTES, ['memory', 'recall']);
75
- assert.equal(noMemory.includes('`web_search`'), true);
76
- assert.equal(noMemory.includes('`recall`'), false);
77
- assert.equal(noMemory.includes('`memory`'), false);
78
- assert.equal(noMemory.includes('# Memory'), false);
79
-
80
- const noResearch = omitToolRoutes(SAMPLE_ROUTES, ['web_search', 'web_fetch']);
81
- assert.equal(noResearch.includes('# Research'), false);
82
- assert.equal(noResearch.includes('Research routes:'), false);
83
- assert.equal(noResearch.includes('# Memory'), true);
84
- });
85
-
86
- test('shared tool rules omit disabled web search and memory routes', () => {
87
- const pluginRoot = join(process.cwd(), 'src');
88
- const full = buildSharedToolContent({ PLUGIN_ROOT: pluginRoot });
89
- assert.match(
90
- full,
91
- /^# General\s+- When an internal Mixdog rule conflicts with the user's latest explicit\s+request, follow the user's request\./,
92
- );
93
- assert.match(full, /`web_search`/);
94
- assert.match(full, /`memory`/);
95
- const omitted = buildSharedToolContent({
96
- PLUGIN_ROOT: pluginRoot,
97
- omitTools: ['web_search', 'web_fetch', 'memory', 'recall'],
98
- });
99
- assert.doesNotMatch(omitted, /`web_search`/);
100
- assert.doesNotMatch(omitted, /`web_fetch`/);
101
- assert.doesNotMatch(omitted, /`recall`/);
102
- assert.doesNotMatch(omitted, /`memory`/);
103
- assert.doesNotMatch(omitted, /# Research/);
104
- assert.doesNotMatch(omitted, /# Memory/);
105
- assert.match(omitted, /`find`/);
106
-
107
- // Exactly one edit dialect ships per model, so the rules must never describe
108
- // the tool that is absent from the surface.
109
- const patchOnly = buildSharedToolContent({ PLUGIN_ROOT: pluginRoot, omitTools: ['edit'] });
110
- assert.doesNotMatch(patchOnly, /`edit`/);
111
- assert.match(patchOnly, /Placement: use exact unchanged context/i);
112
- assert.match(patchOnly, /source-file edits stay with `apply_patch`/i);
113
- const editOnly = buildSharedToolContent({ PLUGIN_ROOT: pluginRoot, omitTools: ['apply_patch'] });
114
- assert.doesNotMatch(editOnly, /`apply_patch`/);
115
- assert.doesNotMatch(editOnly, /Add File|Update File/);
116
- assert.match(editOnly, /Placement: use an exact unique target string/i);
117
- assert.match(editOnly, /source-file edits stay with `edit`\./i);
118
- });
119
-
120
- test('shared tool rules keep workflow and shell-boundary anchors', () => {
121
- // Advisory drift check: update these anchors when the rule text
122
- // intentionally changes.
123
- const full = buildSharedToolContent({ PLUGIN_ROOT: join(process.cwd(), 'src') });
124
- assert.match(full, /Determine the required outcome and missing evidence/i);
125
- assert.match(full, /Before exploration or implementation, consult prior work, current external\s+information, or repository state only when needed/i);
126
- assert.match(full, /Minimize tool turns by batching only calls that are independently necessary/i);
127
- assert.match(full, /A call whose necessity or scope can change after\s+another result waits for that result/i);
128
- assert.match(full, /Respect tool\/schema limits, never omit required fanout/i);
129
- assert.match(full, /A check runs at the strictness the task requires; never raise a tool's own\s+severity beyond it/i);
130
- assert.match(full, /Route each evidence facet once to its primary owner/i);
131
- assert.match(full, /A summary,\s+overview, or enumeration is not a prerequisite to an operation whose complete\s+inputs are already known/i);
132
- assert.match(full, /apply one analysis\s+to many targets as one parameterized call/i);
133
- assert.match(full, /Evidence or artifacts available only through program execution, calculation,\s+data transformation, generated output, or unsupported-format decoding→`shell`/i);
134
- assert.match(full, /an already-open shell is never a routing reason/i);
135
- assert.match(full, /Route the missing evidence to its primary owner/i);
136
- assert.match(full, /repository state, history, or diff→`git`/i);
137
- assert.match(full, /Ownership is exclusive: each evidence type has one owner/i);
138
- assert.match(full, /a\s+successful owner result closes that facet/i);
139
- assert.match(full, /Blocking checks cover only essential integrity, security, compatibility, and\s+buildability invariants/i);
140
- assert.match(full, /environment variable or the home directory are\s+resolved locations/i);
141
- assert.match(full, /Use read-only means for inspection; never mutate to clear an obstacle or\s+unexpected state/i);
142
- assert.match(full, /literal, regex, or text location→`grep`;\s+known-file content, range, or image→`read`/i);
143
- assert.match(full, /is never re-found, re-derived, or\s+re-verified at any granularity/i);
144
- assert.match(full, /Mine each returned result fully before opening the next round/i);
145
- assert.match(full, /Stop exploring once sufficient evidence determines the next action required\s+by the request/i);
146
- assert.match(full, /Enter Verification only after all planned work is complete/i);
147
- assert.match(full, /Use an umbrella suite only when explicitly requested or required by the\s+documented project or release process/i);
148
- assert.match(full, /If verification fails, collect all failures, leave Verification/i);
149
- assert.match(full, /A successful verification closes the task unless later changes affect it/i);
150
- assert.doesNotMatch(full, /affected failed checks once/i);
151
- assert.match(full, /Every Git operation→`git`; source-file edits stay with `edit`\./i);
152
- assert.match(full, /Every Git operation→`git`; source-file edits stay with `apply_patch`\./i);
153
- assert.doesNotMatch(full, /Every repository mutation→`git`/i);
154
- assert.doesNotMatch(full, /always batch safely in parallel/i);
155
- assert.match(full, /A required new file is created directly: Add File is itself the atomic\s+absence check/i);
156
- assert.match(full, /Source: use exact current target text from any visible evidence/i);
157
- assert.match(full, /Placement: use an exact unique target string/i);
158
- assert.match(full, /Placement: use exact unchanged context/i);
159
- assert.match(full, /Apply all determined changes in the fewest safe calls the active tool\s+supports/i);
160
- assert.match(full, /One file, several changes: one Update File block carries every hunk/i);
161
- assert.match(full, /One file, several changes: issue the calls together in one turn/i);
162
- assert.match(full, /Defer only ambiguous or result-dependent changes/i);
163
- assert.match(full, /Commit, push, release, and deployment happen only on the user's explicit\s+request/i);
164
- assert.match(full, /past facts recorded in prior work or sessions→`recall`/i);
165
- assert.match(full, /Use judgment to decide whether a durable memory should be stored/i);
166
- assert.match(full, /Omit `project_id` for the current Project, use `"common"` for shared memory/i);
167
- const headings = ['# General', '# Tool Workflow', '# Research', '# Exploration', '# Editing', '# Execution', '# Verification', '# Delivery', '# Memory'];
168
- assert.deepEqual(headings.map((heading) => full.indexOf(heading)), headings.map((heading) => full.indexOf(heading)).toSorted((a, b) => a - b));
169
- assert.ok(DEFERRED_DEFAULT_LEAD_TOOLS.includes('git'));
170
- assert.equal(DEFERRED_DEFAULT_LEAD_TOOLS.includes('git_stage'), false);
171
- });
172
-
173
- test('agent git policy allows repository evidence but keeps mutations Lead-only', () => {
174
- const rules = buildAgentRoleContent({ PLUGIN_ROOT: join(process.cwd(), 'src') });
175
- assert.match(rules, /Use `git` only for read-only repository evidence/i);
176
- assert.match(rules, /Refuse Git mutations\s+including `add`\/`commit`\/`push`\/`stash`/i);
177
- assert.doesNotMatch(rules, /Never touch git/i);
178
- });
179
-
180
- test('apply_patch keeps grammar and mutation behavior on the freeform surface', () => {
181
- const applyPatch = PATCH_TOOL_DEFS.find((tool) => tool.name === 'apply_patch');
182
- assert.match(applyPatch.freeformDescription, /Add File atomically creates the file and missing parent directories/i);
183
- assert.match(applyPatch.freeformDescription, /failing without changes if the target already exists/i);
184
- assert.match(applyPatch.freeformDescription, /one Add\/Delete\/Update File block per target path/i);
185
- assert.match(applyPatch.freeformDescription, /Multi-file patches commit valid files and report rejected files separately/i);
186
- assert.equal(applyPatch.inputSchema.properties.patch.minLength, 1);
187
- });
188
-
189
- test('toSessionWorkflowMeta keeps delegatesAgents for Solo packs', () => {
190
- const meta = toSessionWorkflowMeta({
191
- id: 'solo',
192
- name: 'Solo',
193
- description: 'Lead works alone.',
194
- source: 'built-in',
195
- delegatesAgents: false,
196
- });
197
- assert.equal(meta.delegatesAgents, false);
198
- assert.equal(workflowDisallowsAgentTool(meta), true);
199
- assert.equal(workflowDisallowsAgentTool({ id: 'solo' }), false);
200
- });
201
-
202
- function surfaceFor({ session = null, denied = [], standalone = [] } = {}) {
203
- return createToolSurface({
204
- mgr: { previewSessionTools: () => standalone },
205
- mode: 'full',
206
- standaloneTools: standalone,
207
- agentToolNames: new Set(['agent']),
208
- getSession: () => session,
209
- getRoute: () => ({ provider: 'grok-oauth' }),
210
- getConfig: () => ({ workflow: { active: 'solo' } }),
211
- cfgMod: { getPluginData: () => '' },
212
- loadWorkflowPack: () => ({ id: 'solo', delegatesAgents: false }),
213
- activeWorkflowId: () => 'solo',
214
- dataDir: '',
215
- getFeatureDisallowedTools: () => denied,
216
- });
217
- }
218
-
219
- test('modelStandaloneTools hides agent and disabled web-search/memory tools', () => {
220
- const standalone = [
221
- { name: 'read' },
222
- { name: 'agent' },
223
- { name: 'web_search' },
224
- { name: 'web_fetch' },
225
- { name: 'memory' },
226
- { name: 'recall' },
227
- ];
228
- const { modelStandaloneTools } = surfaceFor({
229
- session: { workflow: { id: 'solo', delegatesAgents: false } },
230
- denied: ['web_search', 'web_fetch', 'memory', 'recall'],
231
- standalone,
232
- });
233
- assert.deepEqual(modelStandaloneTools().map((tool) => tool.name), ['read']);
234
- });
235
-
236
- test('activateTools refreshes a stale session catalog before selecting new runtime tools', () => {
237
- const read = { name: 'read', annotations: { readOnlyHint: true } };
238
- const updateGoal = { name: 'update_goal', annotations: { readOnlyHint: false } };
239
- const session = {
240
- provider: 'grok-oauth',
241
- messages: [{ role: 'system', content: '# Existing session' }],
242
- tools: [read],
243
- deferredToolCatalog: [read],
244
- deferredCallableTools: ['read'],
245
- deferredSelectedTools: ['read'],
246
- deferredToolBp2Applied: true,
247
- };
248
- const surface = surfaceFor({ session, standalone: [read, updateGoal] });
249
- const result = surface.activateTools(['update_goal']);
250
- assert.deepEqual(result.missing, []);
251
- assert.equal(
252
- new Set([
253
- ...session.tools.map((tool) => tool.name),
254
- ...(session.deferredCallableTools || []),
255
- ]).has('update_goal'),
256
- true,
257
- );
258
- });
259
-
260
- test('empty session refresh strips denied tools and BP1 routes', async () => {
261
- const session = {
262
- id: 'sess_empty',
263
- messages: [
264
- { role: 'system', content: '# Tool Use\nweb/current→`web_search`; returned URL body→`web_fetch`;\nprior work→`recall` (history only, never current local state);\ndurable compact English memory→`memory`;\n' },
265
- { role: 'system', content: '# Profile' },
266
- { role: 'system', content: '# Active Workflow: Cowork\n\n---\n\n# Lead Tools\n', cacheTier: 'tier3' },
267
- ],
268
- tools: [{ name: 'read' }, { name: 'agent' }, { name: 'web_search' }, { name: 'memory' }],
269
- deferredToolCatalog: [{ name: 'read' }, { name: 'agent' }, { name: 'web_search' }, { name: 'memory' }],
270
- deferredCallableTools: ['read', 'agent', 'web_search', 'memory'],
271
- workflow: { id: 'default', delegatesAgents: true },
272
- bp3EnvironmentContext: '- Shell: PowerShell.',
273
- };
274
- const { refreshEmptySessionToolPolicy } = createToolPolicyRefresh({
275
- getSession: () => session,
276
- getRoute: () => ({ provider: 'grok-oauth' }),
277
- getMode: () => 'full',
278
- getConfig: () => ({ workflow: { active: 'solo' } }),
279
- getDataDir: () => '',
280
- modelStandaloneTools: () => [{ name: 'read' }],
281
- featureDisallowedTools: () => ['web_search', 'web_fetch', 'memory', 'recall'],
282
- memoryToolsEnabled: () => false,
283
- loadCoreMemoryContext: async () => '# should not inject',
284
- activeWorkflowContext: () => ({
285
- summary: { id: 'solo', name: 'Solo', description: 'Lead works alone.', source: 'built-in', delegatesAgents: false },
286
- context: '# Active Workflow: Solo — Lead works alone.',
287
- }),
288
- invalidatePreSessionToolSurface: () => {},
289
- });
290
- const bp1BeforeRefresh = session.messages[0];
291
- const bp3BeforeRefresh = session.messages[2];
292
- const result = await refreshEmptySessionToolPolicy();
293
- assert.equal(result.appliedToCurrentSession, true);
294
- assert.equal(session.workflow.delegatesAgents, false);
295
- assert.deepEqual(session.tools.map((tool) => tool.name), ['read']);
296
- const bp1 = session.messages[0].content;
297
- assert.equal(bp1.includes('`web_search`'), false);
298
- assert.equal(bp1.includes('`memory`'), false);
299
- assert.notEqual(session.messages[0], bp1BeforeRefresh);
300
- assert.notEqual(session.messages[2], bp3BeforeRefresh);
301
- assert.match(session.messages[2].content, /# Active Workflow: Solo/);
302
- assert.equal(session.messages[2].content.includes('# Core Memory'), false);
303
- });
304
-
305
- test('refresh leaves a conversation session frozen', async () => {
306
- const session = {
307
- id: 'sess_live',
308
- messages: [{ role: 'user', content: 'hello' }, { role: 'assistant', content: 'hi' }],
309
- tools: [{ name: 'agent' }, { name: 'web_search' }],
310
- workflow: { id: 'default', delegatesAgents: true },
311
- };
312
- const { refreshEmptySessionToolPolicy } = createToolPolicyRefresh({
313
- getSession: () => session,
314
- getRoute: () => ({ provider: 'grok-oauth' }),
315
- getMode: () => 'full',
316
- getConfig: () => ({}),
317
- getDataDir: () => '',
318
- modelStandaloneTools: () => [{ name: 'read' }],
319
- featureDisallowedTools: () => ['web_search'],
320
- memoryToolsEnabled: () => false,
321
- loadCoreMemoryContext: async () => '',
322
- activeWorkflowContext: () => ({
323
- summary: { id: 'solo', delegatesAgents: false },
324
- context: '# Active Workflow: Solo',
325
- }),
326
- invalidatePreSessionToolSurface: () => {},
327
- });
328
- const result = await refreshEmptySessionToolPolicy();
329
- assert.equal(result.appliedToCurrentSession, false);
330
- assert.deepEqual(session.tools.map((tool) => tool.name), ['agent', 'web_search']);
331
- });
@@ -1,62 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import { mkdtemp, rm, writeFile } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import test from 'node:test';
6
-
7
- import { resolvePrompt, rowMatchesContext, sessionMatchesContext } from './helpers.mjs';
8
-
9
- test('parallel same-name agent prompts and files keep their own payloads', async () => {
10
- const cwd = await mkdtemp(join(tmpdir(), 'mixdog-agent-prompts-'));
11
- try {
12
- await Promise.all([
13
- writeFile(join(cwd, 'first.md'), 'first file payload'),
14
- writeFile(join(cwd, 'second.md'), 'second file payload'),
15
- ]);
16
- const calls = [
17
- { agent: 'reviewer', tag: 'first', prompt: 'first string prompt' },
18
- { agent: 'reviewer', tag: 'second', file: 'second.md' },
19
- { agent: 'reviewer', tag: 'third', file: 'first.md' },
20
- { agent: 'reviewer', tag: 'fourth', message: 'fourth string prompt' },
21
- ];
22
- assert.deepEqual(await Promise.all(calls.map((args) => resolvePrompt(args, cwd))), [
23
- 'first string prompt',
24
- 'second file payload',
25
- 'first file payload',
26
- 'fourth string prompt',
27
- ]);
28
- } finally {
29
- await rm(cwd, { recursive: true, force: true });
30
- }
31
- });
32
-
33
- test('same-tag reuse preserves string messages and rejects object coercion', async () => {
34
- const first = { type: 'spawn', tag: 'legacy-session', prompt: 'initial brief' };
35
- const reuse = { type: 'spawn', tag: 'legacy-session', message: 'follow-up brief' };
36
- assert.equal(await resolvePrompt(first), 'initial brief');
37
- assert.equal(await resolvePrompt(reuse), 'follow-up brief');
38
- await assert.rejects(resolvePrompt({ prompt: { text: 'not a string' } }), {
39
- name: 'TypeError',
40
- message: 'agent: prompt/message must be a string',
41
- });
42
- await assert.rejects(resolvePrompt({ message: ['not', 'a', 'string'] }), {
43
- name: 'TypeError',
44
- message: 'agent: prompt/message must be a string',
45
- });
46
- });
47
-
48
- test('agent scope is strictly session-owned when the context carries a session id', () => {
49
- const context = { callerSessionId: 'sess_owner', clientHostPid: 100 };
50
- // Owner match wins; a pid match alone must NOT leak a sibling session's agent.
51
- assert.equal(rowMatchesContext({ ownerSessionId: 'sess_owner', clientHostPid: 100 }, context), true);
52
- assert.equal(rowMatchesContext({ ownerSessionId: 'sess_other', clientHostPid: 100 }, context), false);
53
- assert.equal(rowMatchesContext({ ownerSessionId: null, clientHostPid: 100 }, context), false);
54
- assert.equal(sessionMatchesContext({ ownerSessionId: 'sess_owner', clientHostPid: 100 }, context), true);
55
- assert.equal(sessionMatchesContext({ ownerSessionId: 'sess_other', clientHostPid: 100 }, context), false);
56
- assert.equal(sessionMatchesContext({ parentSessionId: 'sess_owner', clientHostPid: 200 }, context), true);
57
- // Without a caller session id, pid scoping is unchanged.
58
- assert.equal(rowMatchesContext({ ownerSessionId: 'sess_other', clientHostPid: 100 }, { clientHostPid: 100 }), true);
59
- assert.equal(rowMatchesContext({ clientHostPid: 200 }, { clientHostPid: 100 }), false);
60
- // Unscoped context (scope:'all') sees everything.
61
- assert.equal(rowMatchesContext({ ownerSessionId: 'sess_other' }, {}), true);
62
- });
@@ -1,37 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
-
4
- import { createNotify } from './notify.mjs';
5
-
6
- test('Subagent tool completion stays in the worker session', () => {
7
- const queued = [];
8
- const ownerDeliveries = [];
9
- const { workerNotifyFn } = createNotify({
10
- enqueuePendingMessage(sessionId, message) {
11
- queued.push({ sessionId, message });
12
- return 1;
13
- },
14
- }, {
15
- notifySessionCompletion(sessionId, text, meta) {
16
- ownerDeliveries.push({ sessionId, text, meta });
17
- return true;
18
- },
19
- });
20
-
21
- const delivered = workerNotifyFn('sess_worker', {
22
- callerSessionId: 'sess_lead',
23
- })(
24
- 'Async shell task job_test (completed, exit 0) finished.\n\nresult',
25
- {
26
- type: 'shell_task_result',
27
- execution_surface: 'shell',
28
- execution_id: 'job_test',
29
- status: 'completed',
30
- },
31
- );
32
-
33
- assert.equal(delivered, true);
34
- assert.equal(ownerDeliveries.length, 0);
35
- assert.equal(queued.length, 1);
36
- assert.equal(queued[0].sessionId, 'sess_worker');
37
- });
@@ -1,63 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
-
4
- import { cleanupBackgroundTasks } from '../../runtime/shared/background-tasks.mjs';
5
- import { createSpawnFlow } from './spawn-flow.mjs';
6
-
7
- test('agent job lifecycle publishes status immediately and after terminal settlement', async () => {
8
- let publications = 0;
9
- const flow = createSpawnFlow({
10
- mgr: { closeSession() {} },
11
- notifyStatusChange: () => { publications += 1; },
12
- });
13
-
14
- const job = flow.startJob(
15
- 'spawn',
16
- { tag: 'status-test' },
17
- async () => ({ content: 'done' }),
18
- { callerSessionId: 'lead-test', clientHostPid: process.pid },
19
- );
20
-
21
- assert.equal(job.status, 'running');
22
- assert.equal(publications, 1);
23
- await job.promise;
24
- await new Promise((resolve) => setImmediate(resolve));
25
- assert.equal(job.status, 'completed');
26
- assert.ok(publications >= 2);
27
- cleanupBackgroundTasks({ surface: 'agent', force: true });
28
- });
29
-
30
- test('agent job completion uses immutable caller owner and never invokes stale notify context', async () => {
31
- const deliveries = [];
32
- let staleCalls = 0;
33
- const flow = createSpawnFlow({
34
- mgr: { closeSession() {} },
35
- notifySessionCompletion(sessionId, text, meta) {
36
- deliveries.push({ sessionId, text, meta });
37
- return true;
38
- },
39
- });
40
-
41
- const job = flow.startJob(
42
- 'spawn',
43
- { tag: 'owner-test' },
44
- async () => ({ content: 'owner handoff' }),
45
- {
46
- callerSessionId: 'lead-owner',
47
- routingSessionId: 'wrong-lead',
48
- clientHostPid: process.pid,
49
- notifyFn() {
50
- staleCalls += 1;
51
- return true;
52
- },
53
- },
54
- );
55
-
56
- await job.promise;
57
- assert.equal(staleCalls, 0);
58
- assert.ok(deliveries.length >= 1);
59
- assert.ok(deliveries.every((entry) => entry.sessionId === 'lead-owner'));
60
- assert.ok(deliveries.every((entry) => entry.meta.caller_session_id === 'lead-owner'));
61
- assert.match(deliveries.at(-1).text, /owner handoff/);
62
- cleanupBackgroundTasks({ surface: 'agent', force: true });
63
- });