mixdog 0.9.62 → 0.9.64

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 (403) hide show
  1. package/README.md +3 -0
  2. package/package.json +9 -2
  3. package/scripts/code-graph-description-contract.mjs +3 -3
  4. package/scripts/verify-release-assets.mjs +2 -2
  5. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
  6. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
  7. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
  8. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  9. package/src/runtime/agent/orchestrator/config.mjs +19 -124
  10. package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
  11. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
  12. package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
  13. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
  14. package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
  15. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
  16. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
  17. package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
  18. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
  19. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
  20. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
  21. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
  23. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
  24. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
  25. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
  26. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
  27. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
  30. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
  32. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
  35. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
  37. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
  38. package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
  39. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  41. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
  46. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
  49. package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
  50. package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
  51. package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
  54. package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
  57. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
  59. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
  60. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
  61. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
  62. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
  66. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
  67. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
  68. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
  69. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
  70. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
  73. package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
  74. package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
  75. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  76. package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
  77. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
  79. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  81. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
  83. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
  86. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
  91. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
  92. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
  93. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
  96. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
  97. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
  99. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
  100. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
  102. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
  105. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
  106. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
  107. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
  108. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
  109. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
  110. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
  111. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
  112. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  114. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
  116. package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
  119. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
  120. package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
  121. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
  124. package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
  127. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
  129. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
  130. package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
  131. package/src/runtime/channels/lib/scheduler.mjs +1 -1
  132. package/src/runtime/channels/lib/telegram-format.mjs +1 -1
  133. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
  134. package/src/runtime/channels/tool-defs.mjs +2 -3
  135. package/src/runtime/memory/index.mjs +1 -5
  136. package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
  137. package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
  138. package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
  139. package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
  140. package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
  141. package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
  142. package/src/runtime/memory/lib/ko-morph.mjs +2 -2
  143. package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
  144. package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
  145. package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
  146. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
  147. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
  148. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
  149. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
  150. package/src/runtime/memory/lib/memory-embed.mjs +2 -6
  151. package/src/runtime/memory/lib/memory-log.mjs +8 -0
  152. package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
  153. package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
  154. package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
  155. package/src/runtime/memory/lib/memory-score.mjs +1 -1
  156. package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
  157. package/src/runtime/memory/lib/memory.mjs +2 -6
  158. package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
  159. package/src/runtime/memory/lib/pg/process.mjs +1 -5
  160. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
  161. package/src/runtime/memory/lib/recall-format.mjs +2 -2
  162. package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
  163. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
  164. package/src/runtime/memory/lib/trace-store.mjs +2 -6
  165. package/src/runtime/search/lib/http-fetch.mjs +6 -6
  166. package/src/runtime/search/lib/web-tools.mjs +2 -2
  167. package/src/runtime/shared/buffered-appender.mjs +1 -1
  168. package/src/runtime/shared/child-guardian.mjs +1 -1
  169. package/src/runtime/shared/child-spawn-gate.mjs +1 -1
  170. package/src/runtime/shared/config.mjs +4 -4
  171. package/src/runtime/shared/launcher-control.mjs +9 -10
  172. package/src/runtime/shared/llm/index.mjs +5 -20
  173. package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
  174. package/src/runtime/shared/memory-snapshot.mjs +4 -4
  175. package/src/runtime/shared/pristine-execution.mjs +1 -1
  176. package/src/runtime/shared/process-lifecycle.mjs +0 -1
  177. package/src/runtime/shared/process-shutdown.mjs +1 -1
  178. package/src/runtime/shared/resource-admission.mjs +2 -2
  179. package/src/runtime/shared/schedule-session-run.mjs +1 -1
  180. package/src/runtime/shared/schedules-db.mjs +1 -1
  181. package/src/runtime/shared/service-discovery.mjs +2 -2
  182. package/src/runtime/shared/staged-update.mjs +6 -6
  183. package/src/runtime/shared/tool-execution-contract.mjs +1 -1
  184. package/src/runtime/shared/tool-primitives.mjs +1 -1
  185. package/src/runtime/shared/tool-surface.mjs +2 -2
  186. package/src/runtime/shared/update-checker.mjs +1 -1
  187. package/src/runtime/shared/user-cwd.mjs +3 -3
  188. package/src/runtime/shared/user-data-guard.mjs +4 -4
  189. package/src/runtime/shared/workspace-router.mjs +5 -5
  190. package/src/session-runtime/config-helpers.mjs +2 -3
  191. package/src/session-runtime/config-lifecycle.mjs +1 -1
  192. package/src/session-runtime/hitch-profile.mjs +1 -1
  193. package/src/session-runtime/lifecycle-api.mjs +6 -4
  194. package/src/session-runtime/model-recency.mjs +5 -5
  195. package/src/session-runtime/plugin-mcp.mjs +1 -1
  196. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  197. package/src/session-runtime/session-text.mjs +4 -4
  198. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  199. package/src/session-runtime/tool-catalog.mjs +6 -483
  200. package/src/session-runtime/tool-defs.mjs +1 -1
  201. package/src/standalone/agent-task-status.mjs +1 -1
  202. package/src/standalone/agent-tool/helpers.mjs +2 -2
  203. package/src/standalone/channel-admin.mjs +4 -4
  204. package/src/standalone/explore-tool.mjs +3 -3
  205. package/src/standalone/hook-bus/config.mjs +1 -1
  206. package/src/standalone/hook-bus/constants.mjs +1 -1
  207. package/src/standalone/hook-bus/rules.mjs +1 -1
  208. package/src/standalone/projects.mjs +1 -1
  209. package/src/standalone/provider-admin.mjs +2 -2
  210. package/src/tui/app/app-format.mjs +2 -2
  211. package/src/tui/app/clipboard.mjs +3 -3
  212. package/src/tui/app/input-parsers.mjs +1 -1
  213. package/src/tui/app/text-layout.mjs +1 -1
  214. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  215. package/src/tui/app/transcript-window.mjs +5 -347
  216. package/src/tui/app/use-mouse-input.mjs +1 -1
  217. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  218. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  219. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  220. package/src/tui/components/tool-output-format.mjs +7 -7
  221. package/src/tui/display-width.mjs +4 -4
  222. package/src/tui/dist/index.mjs +63 -97
  223. package/src/tui/engine/agent-envelope.mjs +5 -5
  224. package/src/tui/engine/notification-plan.mjs +1 -1
  225. package/src/tui/engine/queue-helpers.mjs +3 -3
  226. package/src/tui/engine/render-timing.mjs +4 -5
  227. package/src/tui/engine/session-api-ext.mjs +3 -3
  228. package/src/tui/engine/tool-result-status.mjs +5 -5
  229. package/src/tui/engine/tool-result-text.mjs +1 -1
  230. package/src/tui/engine/transcript-spill.mjs +585 -0
  231. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  232. package/src/tui/engine.mjs +2 -474
  233. package/src/tui/lib/voice-setup.mjs +1 -1
  234. package/src/tui/markdown/format-token.mjs +1 -1
  235. package/src/tui/markdown/render-ansi.mjs +1 -1
  236. package/src/tui/markdown/table-layout.mjs +4 -4
  237. package/src/tui/paste-attachments.mjs +1 -1
  238. package/src/tui/prompt-history-store.mjs +3 -3
  239. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  240. package/src/tui/theme.mjs +1 -1
  241. package/src/tui/transcript-tool-failures.mjs +2 -2
  242. package/src/ui/ansi.mjs +0 -9
  243. package/src/ui/statusline-format.mjs +2 -4
  244. package/scripts/_bench-cwc.json +0 -20
  245. package/scripts/_jitter-fuzz.jsx +0 -42
  246. package/scripts/_jitter-fuzz2.jsx +0 -30
  247. package/scripts/_jitter-probe.jsx +0 -35
  248. package/scripts/_jp2.jsx +0 -16
  249. package/scripts/_smoke_wd.mjs +0 -7
  250. package/scripts/abort-recovery-test.mjs +0 -175
  251. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  252. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  253. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  254. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  255. package/scripts/agent-loop-policy-test.mjs +0 -37
  256. package/scripts/agent-model-liveness-test.mjs +0 -763
  257. package/scripts/agent-parallel-smoke.mjs +0 -435
  258. package/scripts/agent-route-batch-test.mjs +0 -40
  259. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  260. package/scripts/agent-terminal-reap-test.mjs +0 -252
  261. package/scripts/agent-trace-io-test.mjs +0 -133
  262. package/scripts/ansi-color-capability-test.mjs +0 -90
  263. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  264. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  265. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  266. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  267. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  268. package/scripts/arg-guard-test.mjs +0 -93
  269. package/scripts/async-notify-settlement-test.mjs +0 -99
  270. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  271. package/scripts/background-task-meta-smoke.mjs +0 -38
  272. package/scripts/bench-run.mjs +0 -508
  273. package/scripts/boot-smoke.mjs +0 -137
  274. package/scripts/build-runtime-windows.ps1 +0 -242
  275. package/scripts/channel-daemon-smoke.mjs +0 -1103
  276. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  277. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  278. package/scripts/code-graph-dispatch-test.mjs +0 -96
  279. package/scripts/code-graph-root-federation-test.mjs +0 -273
  280. package/scripts/compact-active-turn-test.mjs +0 -68
  281. package/scripts/compact-file-reattach-test.mjs +0 -88
  282. package/scripts/compact-pressure-test.mjs +0 -788
  283. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  284. package/scripts/compact-recall-digest-test.mjs +0 -57
  285. package/scripts/compact-smoke.mjs +0 -1017
  286. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  287. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  288. package/scripts/context-mcp-metering-test.mjs +0 -1406
  289. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  290. package/scripts/deferred-tool-loading-test.mjs +0 -250
  291. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  292. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  293. package/scripts/embedding-worker-exit-test.mjs +0 -76
  294. package/scripts/execution-completion-dedup-test.mjs +0 -205
  295. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  296. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  297. package/scripts/explore-bench-tmp.mjs +0 -36
  298. package/scripts/explore-bench.mjs +0 -248
  299. package/scripts/explore-prompt-policy-test.mjs +0 -256
  300. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  301. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  302. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  303. package/scripts/freevar-smoke.mjs +0 -98
  304. package/scripts/gemini-provider-test.mjs +0 -1448
  305. package/scripts/generate-oc-icons.mjs +0 -11
  306. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  307. package/scripts/headless-pristine-execution-test.mjs +0 -614
  308. package/scripts/hook-bus-test.mjs +0 -552
  309. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  310. package/scripts/internal-comms-bench-test.mjs +0 -226
  311. package/scripts/internal-comms-bench.mjs +0 -853
  312. package/scripts/internal-comms-smoke.mjs +0 -277
  313. package/scripts/internal-tools-normalization-test.mjs +0 -62
  314. package/scripts/interrupted-turn-history-test.mjs +0 -399
  315. package/scripts/lead-workflow-smoke.mjs +0 -592
  316. package/scripts/lifecycle-api-test.mjs +0 -137
  317. package/scripts/live-share-test.mjs +0 -148
  318. package/scripts/live-worker-smoke.mjs +0 -333
  319. package/scripts/log-writer-guard-smoke.mjs +0 -131
  320. package/scripts/maintenance-default-routes-test.mjs +0 -164
  321. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  322. package/scripts/max-output-recovery-test.mjs +0 -368
  323. package/scripts/mcp-client-normalization-test.mjs +0 -45
  324. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  325. package/scripts/memory-core-input-test.mjs +0 -167
  326. package/scripts/memory-cycle-routing-test.mjs +0 -111
  327. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  328. package/scripts/memory-pg-recovery-test.mjs +0 -59
  329. package/scripts/memory-rule-contract-test.mjs +0 -93
  330. package/scripts/model-list-sanitize-test.mjs +0 -37
  331. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  332. package/scripts/native-edit-wire-test.mjs +0 -152
  333. package/scripts/notify-completion-mirror-test.mjs +0 -73
  334. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  335. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  336. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  337. package/scripts/output-style-bench.mjs +0 -292
  338. package/scripts/output-style-smoke.mjs +0 -145
  339. package/scripts/parent-abort-link-test.mjs +0 -90
  340. package/scripts/patch-binary-cache-test.mjs +0 -181
  341. package/scripts/path-suffix-test.mjs +0 -57
  342. package/scripts/pending-completion-drop-test.mjs +0 -239
  343. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  344. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  345. package/scripts/process-lifecycle-test.mjs +0 -461
  346. package/scripts/prompt-immediate-render-test.mjs +0 -89
  347. package/scripts/prompt-input-parity-test.mjs +0 -145
  348. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  349. package/scripts/provider-contract-test.mjs +0 -632
  350. package/scripts/provider-stream-stall-test.mjs +0 -276
  351. package/scripts/provider-toolcall-test.mjs +0 -3063
  352. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  353. package/scripts/recall-bench.mjs +0 -359
  354. package/scripts/resource-admission-test.mjs +0 -792
  355. package/scripts/result-classification-test.mjs +0 -75
  356. package/scripts/rg-runner-test.mjs +0 -240
  357. package/scripts/routing-corpus-test.mjs +0 -349
  358. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  359. package/scripts/session-bench-cache-break-test.mjs +0 -102
  360. package/scripts/session-bench.mjs +0 -1708
  361. package/scripts/session-context-bench.mjs +0 -344
  362. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  363. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  364. package/scripts/session-ingest-smoke.mjs +0 -177
  365. package/scripts/session-orphan-sweep-test.mjs +0 -109
  366. package/scripts/set-effort-config-test.mjs +0 -41
  367. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  368. package/scripts/shell-hardening-test.mjs +0 -209
  369. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  370. package/scripts/ship-mode-test.mjs +0 -98
  371. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  372. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  373. package/scripts/smoke-loop-report.mjs +0 -221
  374. package/scripts/smoke-loop.mjs +0 -202
  375. package/scripts/smoke-runtime-negative.ps1 +0 -106
  376. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  377. package/scripts/steering-drain-buckets-test.mjs +0 -467
  378. package/scripts/steering-fold-provenance-test.mjs +0 -71
  379. package/scripts/streaming-tail-window-test.mjs +0 -175
  380. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  381. package/scripts/task-bench.mjs +0 -205
  382. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  383. package/scripts/termio-input-smoke.mjs +0 -199
  384. package/scripts/tool-result-hook-test.mjs +0 -48
  385. package/scripts/tool-smoke.mjs +0 -2453
  386. package/scripts/tool-tui-presentation-test.mjs +0 -286
  387. package/scripts/toolcall-args-test.mjs +0 -239
  388. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  389. package/scripts/tui-background-failure-smoke.mjs +0 -73
  390. package/scripts/tui-perf-run.ps1 +0 -26
  391. package/scripts/tui-render-smoke.mjs +0 -90
  392. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  393. package/scripts/tui-runtime-load-bench.mjs +0 -50
  394. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  395. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  396. package/scripts/tui-transcript-perf-test.mjs +0 -934
  397. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  398. package/scripts/verify-release-assets-test.mjs +0 -281
  399. package/scripts/web-fetch-routing-test.mjs +0 -158
  400. package/scripts/webhook-smoke.mjs +0 -201
  401. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  402. package/scripts/worker-notify-rejection-test.mjs +0 -51
  403. package/scripts/write-backpressure-test.mjs +0 -147
@@ -1,62 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
- import {
4
- acceptQuotaSnapshot,
5
- fallbackStatusline,
6
- resolveContextUsedPct,
7
- } from '../src/ui/statusline.mjs';
8
-
9
- // Monotonic hysteresis for the 5H/7D usage segment: once a value has rendered,
10
- // an OLDER shared-cache snapshot (written by another mixdog instance) must not
11
- // displace it, but a NEWER one — or confirmed own-instance live data — must.
12
- test('older shared-cache snapshot is rejected, newer is accepted', () => {
13
- const displayedOwnLive = { segments: ['5H 12%'], asOf: 2000, owned: true };
14
-
15
- // Another instance overwrites the shared cache with an OLDER snapshot →
16
- // metricsMatch flips false, source alternates to that unowned older snapshot.
17
- assert.equal(
18
- acceptQuotaSnapshot(displayedOwnLive, { asOf: 1000, owned: false }),
19
- false,
20
- 'older unowned snapshot must not displace displayed own-live value',
21
- );
22
-
23
- // A strictly newer shared snapshot is allowed to advance the value.
24
- assert.equal(
25
- acceptQuotaSnapshot(displayedOwnLive, { asOf: 3000, owned: false }),
26
- true,
27
- 'newer snapshot must replace the displayed value',
28
- );
29
- });
30
-
31
- test('own-instance live data always wins; empty timestamps preserve prior behavior', () => {
32
- const displayedShared = { segments: ['5H 12%'], asOf: 5000, owned: false };
33
- // Own-instance live data replaces even an apparently newer shared snapshot.
34
- assert.equal(acceptQuotaSnapshot(displayedShared, { asOf: 1, owned: true }), true);
35
- // Nothing displayed yet → accept.
36
- assert.equal(acceptQuotaSnapshot(undefined, { asOf: 1000, owned: false }), true);
37
- // No comparable timestamps → accept (byte-for-byte prior behavior).
38
- assert.equal(acceptQuotaSnapshot({ asOf: 0, owned: true }, { asOf: 0, owned: false }), true);
39
- // Shared→shared same asOf → accept (idempotent refresh).
40
- assert.equal(acceptQuotaSnapshot({ asOf: 2000, owned: false }, { asOf: 2000, owned: false }), true);
41
- });
42
-
43
- test('context percentage uses compaction pressure over trigger and ignores gateway boundary percent', () => {
44
- const args = {
45
- stats: {
46
- currentEstimatedContextTokens: 75_000,
47
- currentContextSource: 'estimated',
48
- },
49
- contextWindow: 100_000,
50
- compactBoundaryTokens: 100_000,
51
- autoCompactTokenLimit: 75_000,
52
- };
53
- assert.equal(resolveContextUsedPct({
54
- ...args,
55
- gatewayStatus: { contextUsedPct: 75 },
56
- }), 100);
57
- assert.match(
58
- fallbackStatusline({ provider: 'openai', model: 'test', ...args })
59
- .replace(/\x1b\[[0-9;]*m/g, ''),
60
- /\b100%/,
61
- );
62
- });
@@ -1,467 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { createSessionFlow } from '../src/tui/engine/session-flow.mjs';
4
- import { createRunTurn } from '../src/tui/engine/turn.mjs';
5
-
6
- const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7
-
8
- // Minimal bag: drainPendingSteering only touches pending, the queue helpers,
9
- // and commitSteeringQueueEntries (which no-ops on disk when runtime.id is not
10
- // a valid session key). No provider/runTurn wiring needed.
11
- function makeFlow() {
12
- let seq = 0;
13
- let runTurns = 0;
14
- const state = { queued: [], busy: false };
15
- const bag = {
16
- runtime: { id: null },
17
- nextId: () => `id_${++seq}`,
18
- tuiDebug: () => {},
19
- flags: {},
20
- pending: [],
21
- pendingNotificationKeys: new Set(),
22
- displayedExecutionNotificationKeys: new Set(),
23
- getState: () => state,
24
- set: (patch) => Object.assign(state, patch),
25
- pushItem: () => {},
26
- replaceItems: () => {},
27
- pushNotice: () => {},
28
- pushUserOrSyntheticItem: () => {},
29
- autoClearState: () => ({ enabled: false }),
30
- agentStatusState: {},
31
- routeState: {},
32
- syncContextStats: () => {},
33
- flushDeferredExecutionPendingResumeKick: () => {},
34
- runTurn: async () => {
35
- runTurns += 1;
36
- return 'done';
37
- },
38
- };
39
- return { flow: createSessionFlow(bag), bag, getRunTurns: () => runTurns };
40
- }
41
-
42
- test('drainPendingSteering drains prompt/next mid-turn but leaves later notifications', () => {
43
- const { flow, bag } = makeFlow();
44
- bag.pending.push(flow.makeQueueEntry('steer the turn', { mode: 'prompt' }));
45
- bag.pending.push(flow.makeQueueEntry('task finished', { mode: 'task-notification', key: 'task-1' }));
46
-
47
- const out = flow.drainPendingSteering();
48
-
49
- assert.equal(out.length, 1, 'next-priority prompt is injected into the active continuation');
50
- assert.equal(out[0], 'steer the turn');
51
- assert.equal(bag.pending.length, 1, 'later task notification waits for post-turn or explicit later flush');
52
- assert.equal(bag.pending[0].content, 'task finished');
53
- });
54
-
55
- test('drainPendingSteering can explicitly flush later notifications', () => {
56
- const { flow, bag } = makeFlow();
57
- bag.pending.push(flow.makeQueueEntry('task finished', { mode: 'task-notification', key: 'task-1' }));
58
-
59
- const out = flow.drainPendingSteering({ maxPriority: 'later' });
60
-
61
- assert.equal(out.length, 1);
62
- assert.equal(out[0], 'task finished');
63
- assert.equal(bag.pending.length, 0);
64
- });
65
-
66
- test('drainPendingSteering accepts runtime callback signature', () => {
67
- const { flow, bag } = makeFlow();
68
- bag.pending.push(flow.makeQueueEntry('task finished', { mode: 'task-notification', key: 'task-1' }));
69
-
70
- const out = flow.drainPendingSteering('session-id', { maxPriority: 'later' });
71
-
72
- assert.deepEqual(out, ['task finished']);
73
- assert.equal(bag.pending.length, 0);
74
- });
75
-
76
- test('drained notification key is released while displayed key is not cleared', () => {
77
- const { flow, bag } = makeFlow();
78
- bag.getState().busy = true;
79
- assert.equal(flow.enqueue('task finished', { mode: 'task-notification', key: 'task-1' }), true);
80
- bag.displayedExecutionNotificationKeys.add('task-1');
81
-
82
- const out = flow.drainPendingSteering({ maxPriority: 'later' });
83
-
84
- assert.deepEqual(out, ['task finished']);
85
- assert.equal(flow.enqueue('task duplicate', { mode: 'task-notification', key: 'task-1' }), true);
86
- assert.equal(bag.displayedExecutionNotificationKeys.has('task-1'), true);
87
- });
88
-
89
- test('drainPendingSteering leaves slash commands pending', () => {
90
- const { flow, bag } = makeFlow();
91
- bag.pending.push(flow.makeQueueEntry('/clear', { mode: 'prompt' }));
92
- bag.pending.push(flow.makeQueueEntry('steer text', { mode: 'prompt' }));
93
-
94
- const out = flow.drainPendingSteering();
95
-
96
- assert.equal(out.length, 1, 'non-slash prompt drains');
97
- assert.equal(out[0], 'steer text');
98
- assert.equal(bag.pending.length, 1, 'slash command stays queued for post-turn processing');
99
- assert.equal(bag.pending[0].content, '/clear');
100
- });
101
-
102
- test('enqueue while busy does not start a parallel Lead turn', async () => {
103
- const { flow, bag, getRunTurns } = makeFlow();
104
- bag.getState().busy = true;
105
-
106
- assert.equal(flow.enqueue('scheduled message', { mode: 'task-notification' }), true);
107
- await new Promise((resolve) => setImmediate(resolve));
108
-
109
- assert.equal(getRunTurns(), 0, 'busy enqueue must wait for active turn boundary');
110
- assert.equal(bag.pending.length, 1, 'message remains pending for post-turn drain');
111
- });
112
-
113
- test('post-turn drain does not send queued slash command to model', async () => {
114
- const { flow, bag, getRunTurns } = makeFlow();
115
- bag.pending.push(flow.makeQueueEntry('/clear', { mode: 'prompt' }));
116
-
117
- await flow.drain();
118
- await new Promise((resolve) => setImmediate(resolve));
119
-
120
- assert.equal(getRunTurns(), 0, 'slash command must not be runTurn model text');
121
- assert.equal(bag.pending.length, 1, 'slash command remains for command dispatcher');
122
- assert.equal(bag.pending[0].content, '/clear');
123
- });
124
-
125
- test('restoreQueued can edit one visible steering entry without draining its siblings', () => {
126
- const { flow, bag } = makeFlow();
127
- const first = flow.makeQueueEntry('first follow-up', { mode: 'prompt' });
128
- const second = flow.makeQueueEntry('second follow-up', { mode: 'prompt' });
129
- bag.pending.push(first, second);
130
- bag.getState().queued = [first, second];
131
-
132
- const restored = flow.restoreQueued('current draft', second.id);
133
-
134
- assert.equal(restored.count, 1);
135
- assert.equal(restored.text, 'second follow-up\ncurrent draft');
136
- assert.deepEqual(bag.pending.map((entry) => entry.id), [first.id]);
137
- assert.deepEqual(bag.getState().queued.map((entry) => entry.id), [first.id]);
138
- });
139
-
140
- // Minimal store bag for createRunTurn: only the surface the streaming/steering
141
- // finalize path touches. runtime.ask is a caller-supplied mock that drives the
142
- // text-delta / steer-message callbacks.
143
- function makeTurnBag(ask, {
144
- timeoutMs = 300000,
145
- getTurnLiveness,
146
- abort,
147
- } = {}) {
148
- let seq = 0;
149
- const state = {
150
- items: [],
151
- stats: { turns: 0, inputTokens: 0, outputTokens: 0 },
152
- busy: false,
153
- spinner: null,
154
- thinking: null,
155
- };
156
- const itemIndexById = new Map();
157
- const findIndexById = (id) => state.items.findIndex((it) => it.id === id);
158
- const runtime = { id: null, toolMode: 'auto', ask, abort: abort || (() => {}) };
159
- if (typeof getTurnLiveness === 'function') runtime.getTurnLiveness = getTurnLiveness;
160
- const bag = {
161
- runtime,
162
- nextId: () => `id_${++seq}`,
163
- tuiDebug: () => {},
164
- LEAD_TURN_TIMEOUT_MS: timeoutMs,
165
- flags: { leadTurnEpoch: 0 },
166
- pending: [],
167
- itemIndexById,
168
- getState: () => state,
169
- set: (patch) => Object.assign(state, patch),
170
- pushItem: (spec) => {
171
- state.items = [...state.items, spec];
172
- if (spec?.id != null) itemIndexById.set(spec.id, state.items.length - 1);
173
- },
174
- patchItem: (id, patch) => {
175
- const idx = findIndexById(id);
176
- if (idx < 0) return;
177
- const items = state.items.slice();
178
- items[idx] = { ...items[idx], ...patch };
179
- state.items = items;
180
- },
181
- pushNotice: () => {},
182
- pushUserOrSyntheticItem: (text) => {
183
- state.items = [...state.items, { kind: 'user', id: `u_${++seq}`, text }];
184
- },
185
- markToolCallActive: () => {},
186
- markToolCallDone: () => {},
187
- clearActiveToolSummary: () => {},
188
- agentStatusState: () => ({}),
189
- routeState: () => ({}),
190
- syncContextStats: () => {},
191
- denyAllToolApprovals: () => {},
192
- requestToolApproval: async () => ({ approved: false }),
193
- patchToolCardResult: () => {},
194
- flushToolResults: () => {},
195
- flushDeferredExecutionPendingResumeKick: () => {},
196
- drain: async () => {},
197
- drainPendingSteering: () => [],
198
- };
199
- return { bag, getState: () => state };
200
- }
201
-
202
- test('non-cancellation turn exceptions publish and return failed lifecycle status', async () => {
203
- const notices = [];
204
- const { bag, getState } = makeTurnBag(async () => {
205
- throw new Error('provider request failed');
206
- });
207
- bag.pushNotice = (text, tone) => notices.push({ text, tone });
208
-
209
- const result = await createRunTurn(bag)('do a thing');
210
- const completion = getState().items.find((item) => item.kind === 'turndone');
211
-
212
- assert.equal(result, 'failed');
213
- assert.equal(completion?.status, 'failed');
214
- assert.equal(getState().busy, false);
215
- assert.equal(getState().spinner, null);
216
- assert.equal(notices.at(-1)?.tone, 'error');
217
- assert.match(notices.at(-1)?.text || '', /provider request failed/);
218
- });
219
-
220
- test('watchdog accepts fresh runtime liveness without tripping', async () => {
221
- let aborts = 0;
222
- const ask = async () => {
223
- await wait(45);
224
- return { result: { content: '' }, session: { messages: [] } };
225
- };
226
- const { bag } = makeTurnBag(ask, {
227
- timeoutMs: 15,
228
- abort: () => { aborts += 1; },
229
- getTurnLiveness: () => ({
230
- stage: 'tool_running',
231
- lastProgressAt: Date.now(),
232
- toolStartedAt: Date.now(),
233
- toolSelfDeadlineMs: 0,
234
- }),
235
- });
236
-
237
- assert.equal(await createRunTurn(bag)('do a thing'), 'done');
238
- assert.equal(aborts, 0, 'fresh orchestrator heartbeats defer the watchdog');
239
- });
240
-
241
- test('watchdog trips at the tool ceiling despite fresh liveness', async () => {
242
- const previousMax = process.env.MIXDOG_LEAD_TOOL_MAX_MS;
243
- const timeoutMs = 30;
244
- const ceilingMs = 95;
245
- process.env.MIXDOG_LEAD_TOOL_MAX_MS = String(ceilingMs);
246
- let rejectAsk;
247
- let heartbeatTimer = null;
248
- let heartbeatStartTimer = null;
249
- let aborts = 0;
250
- let abortAt = 0;
251
- let livenessCalls = 0;
252
- const toolStartedAt = Date.now();
253
- const ask = (_userText, options) => new Promise((_resolve, reject) => {
254
- rejectAsk = reject;
255
- // Let the first watchdog probe defer from orchestrator liveness, then keep
256
- // the TUI's local progress fresh so the ceiling-bounded re-arm is exercised.
257
- heartbeatStartTimer = setTimeout(() => {
258
- heartbeatTimer = setInterval(() => options.onStreamDelta(), 5);
259
- }, 40);
260
- });
261
- const { bag } = makeTurnBag(ask, {
262
- timeoutMs,
263
- abort: () => {
264
- aborts += 1;
265
- abortAt = Date.now();
266
- clearTimeout(heartbeatStartTimer);
267
- clearInterval(heartbeatTimer);
268
- const error = new Error('interrupted');
269
- error.name = 'SessionClosedError';
270
- rejectAsk(error);
271
- },
272
- getTurnLiveness: () => {
273
- livenessCalls += 1;
274
- return {
275
- stage: 'tool_running',
276
- lastProgressAt: Date.now(),
277
- toolStartedAt,
278
- toolSelfDeadlineMs: 0,
279
- };
280
- },
281
- });
282
-
283
- try {
284
- const result = await Promise.race([
285
- createRunTurn(bag)('do a thing'),
286
- wait(180).then(() => 'watchdog did not trip at the ceiling'),
287
- ]);
288
- assert.equal(result, 'cancelled');
289
- assert.equal(aborts, 1);
290
- assert.ok(livenessCalls >= 2, 'first probe deferred, then the ceiling probe rechecked liveness');
291
- assert.ok(abortAt - toolStartedAt < ceilingMs + timeoutMs, 'ceiling trips before an unbounded watchdog interval');
292
- } finally {
293
- clearTimeout(heartbeatStartTimer);
294
- clearInterval(heartbeatTimer);
295
- if (previousMax === undefined) delete process.env.MIXDOG_LEAD_TOOL_MAX_MS;
296
- else process.env.MIXDOG_LEAD_TOOL_MAX_MS = previousMax;
297
- }
298
- });
299
-
300
- for (const [name, getTurnLiveness] of [
301
- ['throws', () => { throw new Error('liveness unavailable'); }],
302
- ['returns null', () => null],
303
- ]) {
304
- test(`watchdog ${name} probe falls back to abort`, async () => {
305
- let rejectAsk;
306
- let aborts = 0;
307
- const ask = () => new Promise((_resolve, reject) => { rejectAsk = reject; });
308
- const { bag } = makeTurnBag(ask, {
309
- timeoutMs: 15,
310
- getTurnLiveness,
311
- abort: () => {
312
- aborts += 1;
313
- const error = new Error('interrupted');
314
- error.name = 'SessionClosedError';
315
- rejectAsk(error);
316
- },
317
- });
318
-
319
- const [result] = await Promise.all([createRunTurn(bag)('do a thing'), wait(40)]);
320
- assert.equal(result, 'cancelled');
321
- assert.equal(aborts, 1);
322
- });
323
- }
324
-
325
- test('stream-delta progress keeps the watchdog alive', async () => {
326
- let aborts = 0;
327
- let streamDeltas = 0;
328
- const ask = async (_userText, options) => {
329
- options.onStreamDelta();
330
- streamDeltas += 1;
331
- await wait(12);
332
- options.onStreamDelta();
333
- streamDeltas += 1;
334
- await wait(12);
335
- return { result: { content: '' }, session: { messages: [] } };
336
- };
337
- const { bag } = makeTurnBag(ask, {
338
- timeoutMs: 20,
339
- abort: () => { aborts += 1; },
340
- });
341
-
342
- assert.equal(await createRunTurn(bag)('do a thing'), 'done');
343
- assert.equal(streamDeltas, 2);
344
- assert.equal(aborts, 0);
345
- });
346
-
347
- test('onSteerMessage commits a streamed no-newline assistant tail into items', async () => {
348
- // A terminal no-tool response streams a single line WITHOUT a trailing '\n',
349
- // so no assistant row/currentAssistantId exists yet. A steering injection
350
- // races finalization and must seal the pending tail instead of dropping it.
351
- const TAIL = 'partial answer with no trailing newline';
352
- const ask = async (_userText, opts) => {
353
- opts.onTextDelta(TAIL);
354
- opts.onSteerMessage('steer now');
355
- return { result: { content: '' }, session: { messages: [] } };
356
- };
357
- const { bag, getState } = makeTurnBag(ask);
358
- const runTurn = createRunTurn(bag);
359
-
360
- await runTurn('do a thing');
361
-
362
- const assistant = getState().items.find((it) => it.kind === 'assistant');
363
- assert.ok(assistant, 'streamed no-newline tail must be committed as an assistant item');
364
- assert.equal(assistant.text, TAIL);
365
- assert.equal(assistant.streaming, false);
366
- });
367
-
368
- test('finalization does not duplicate a steer-committed tail when result.content repeats it', async () => {
369
- // Same steer race, but the provider's final content equals the already-
370
- // committed tail. Finalization must NOT re-emit it as a second item.
371
- const TAIL = 'partial answer with no trailing newline';
372
- const ask = async (_userText, opts) => {
373
- opts.onTextDelta(TAIL);
374
- opts.onSteerMessage('steer now');
375
- return { result: { content: TAIL }, session: { messages: [] } };
376
- };
377
- const { bag, getState } = makeTurnBag(ask);
378
- const runTurn = createRunTurn(bag);
379
-
380
- await runTurn('do a thing');
381
-
382
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
383
- assert.equal(assistants.length, 1, 'the committed tail must not be duplicated at finalize');
384
- assert.equal(assistants[0].text, TAIL);
385
- assert.equal(assistants[0].streaming, false);
386
- });
387
-
388
- test('finalization appends only the uncommitted remainder past a steer-committed tail', async () => {
389
- // Provider content extends past the committed tail: only the new remainder
390
- // becomes a fresh item, ordered after the committed segment (+ steering row).
391
- const TAIL = 'partial answer with no trailing newline';
392
- const REMAINDER = '\nmore text arriving after the steer';
393
- const ask = async (_userText, opts) => {
394
- opts.onTextDelta(TAIL);
395
- opts.onSteerMessage('steer now');
396
- return { result: { content: TAIL + REMAINDER }, session: { messages: [] } };
397
- };
398
- const { bag, getState } = makeTurnBag(ask);
399
- const runTurn = createRunTurn(bag);
400
-
401
- await runTurn('do a thing');
402
-
403
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
404
- assert.equal(assistants.length, 2, 'committed tail + remainder are two distinct items');
405
- assert.equal(assistants[0].text, TAIL, 'committed tail stays first, unchanged');
406
- assert.equal(assistants[1].text, REMAINDER, 'only the uncommitted remainder is appended');
407
- assert.equal(assistants[1].streaming, false);
408
- const steerIdx = getState().items.findIndex((it) => it.kind === 'user' && it.text === 'steer now');
409
- const remainderIdx = getState().items.findIndex((it) => it.kind === 'assistant' && it.text === REMAINDER);
410
- assert.ok(steerIdx >= 0 && remainderIdx > steerIdx, 'remainder appends after the injected steering row');
411
- });
412
-
413
- // Two committed segments this turn: a prior preamble P, then a no-newline TAIL
414
- // sealed by the steer race. The provider's final content may OMIT P — the
415
- // per-segment strip must still peel BOTH out (a single concatenated 'P+TAIL'
416
- // prefix would fail to match and duplicate TAIL after the steering row).
417
- const P = 'preamble sealed before the tail';
418
- const TAIL2 = 'terminal tail with no trailing newline';
419
- function makeTwoSegmentAsk(finalContent) {
420
- return async (_userText, opts) => {
421
- opts.onTextDelta(P);
422
- opts.onSteerMessage('steer one'); // seals P as its own item
423
- opts.onTextDelta(TAIL2);
424
- opts.onSteerMessage('steer two'); // seals TAIL2 as its own item
425
- return { result: { content: finalContent }, session: { messages: [] } };
426
- };
427
- }
428
-
429
- test('per-segment strip: final content = TAIL only (P omitted) → no new item', async () => {
430
- const { bag, getState } = makeTurnBag(makeTwoSegmentAsk(TAIL2));
431
- await createRunTurn(bag)('do a thing');
432
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
433
- assert.deepEqual(assistants.map((it) => it.text), [P, TAIL2], 'only the two committed segments exist');
434
- });
435
-
436
- test('per-segment strip: final content = P + newline + TAIL → no new item', async () => {
437
- const { bag, getState } = makeTurnBag(makeTwoSegmentAsk(`${P}\n${TAIL2}`));
438
- await createRunTurn(bag)('do a thing');
439
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
440
- assert.deepEqual(assistants.map((it) => it.text), [P, TAIL2], 'both segments stripped; nothing re-emitted');
441
- });
442
-
443
- test('per-segment strip: final content extends past both committed segments → only remainder', async () => {
444
- const { bag, getState } = makeTurnBag(makeTwoSegmentAsk(`${P}\n${TAIL2}\nmore`));
445
- await createRunTurn(bag)('do a thing');
446
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
447
- assert.equal(assistants.length, 3, 'committed P + TAIL + the new remainder');
448
- assert.equal(assistants[2].text, '\nmore', 'only the uncommitted remainder is appended');
449
- assert.equal(assistants[2].streaming, false);
450
- });
451
-
452
- test('per-segment strip: segment sealed WITH a leading newline still peels', async () => {
453
- // The committed segment carries its own leading '\n' ('\nTAIL'); the provider
454
- // returns the trimmed 'TAIL'. Trimming the segment before the compare lets it
455
- // peel so it is not duplicated after the steering row.
456
- const LEAD_TAIL = '\nterminal tail sealed with a leading newline';
457
- const ask = async (_userText, opts) => {
458
- opts.onTextDelta(LEAD_TAIL);
459
- opts.onSteerMessage('steer now');
460
- return { result: { content: LEAD_TAIL.replace(/^\s+/, '') }, session: { messages: [] } };
461
- };
462
- const { bag, getState } = makeTurnBag(ask);
463
- await createRunTurn(bag)('do a thing');
464
- const assistants = getState().items.filter((it) => it.kind === 'assistant');
465
- assert.equal(assistants.length, 1, 'leading-newline segment must not be duplicated at finalize');
466
- assert.equal(assistants[0].text, LEAD_TAIL);
467
- });
@@ -1,71 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { foldUserTextIntoToolResultTail } from '../src/runtime/agent/orchestrator/session/context-utils.mjs';
4
- import { _buildRequestBodyForCacheSmoke } from '../src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs';
5
-
6
- // A tool_result turn followed by a plain user text turn.
7
- function baseTranscript(userMeta) {
8
- return [
9
- { role: 'user', content: 'do the thing' },
10
- { role: 'assistant', content: '', toolCalls: [{ id: 't1', name: 'read', arguments: {} }] },
11
- { role: 'tool', toolCallId: 't1', content: 'file contents here' },
12
- { role: 'user', content: 'actually stop and do X instead', ...(userMeta ? { meta: userMeta } : {}) },
13
- ];
14
- }
15
-
16
- test('plain user text after tool_result is folded into the tool_result turn', () => {
17
- const body = _buildRequestBodyForCacheSmoke(baseTranscript(null), 'claude-sonnet-4');
18
- const msgs = body.messages;
19
- const lastUser = msgs[msgs.length - 1];
20
- assert.equal(lastUser.role, 'user');
21
- const hasToolResult = lastUser.content.some((b) => b.type === 'tool_result');
22
- assert.ok(hasToolResult, 'trailing user turn carries the tool_result');
23
- assert.ok(JSON.stringify(lastUser.content).includes('do X instead'), 'plain text folded into tool_result');
24
- });
25
-
26
- test('steering-tagged user text after tool_result stays a separate user turn', () => {
27
- const body = _buildRequestBodyForCacheSmoke(baseTranscript({ source: 'steering' }), 'claude-sonnet-4');
28
- const msgs = body.messages;
29
- const lastUser = msgs[msgs.length - 1];
30
- assert.equal(lastUser.role, 'user', 'steering message is its own user turn');
31
- const isToolResultTurn = Array.isArray(lastUser.content)
32
- && lastUser.content.some((b) => b.type === 'tool_result');
33
- assert.equal(isToolResultTurn, false, 'steering turn is not a tool_result turn');
34
- assert.ok(JSON.stringify(lastUser.content).includes('do X instead'), 'steering text preserved as user input');
35
- const prev = msgs[msgs.length - 2];
36
- assert.ok(
37
- Array.isArray(prev.content) && prev.content.some((b) => b.type === 'tool_result'),
38
- 'tool_result stays on its own preceding turn',
39
- );
40
- });
41
-
42
- test('foldUserTextIntoToolResultTail unit: folds plain text tail', () => {
43
- const result = [{ role: 'user', content: [{ type: 'tool_result', tool_use_id: 't1', content: 'r' }] }];
44
- assert.equal(foldUserTextIntoToolResultTail(result, 'hi'), true);
45
- });
46
-
47
- // --- API-key Anthropic provider (anthropic.mjs) path ---
48
- import { _toAnthropicMessagesForTest } from '../src/runtime/agent/orchestrator/providers/anthropic.mjs';
49
-
50
- test('anthropic.mjs: plain user text after tool_result is folded', () => {
51
- const msgs = _toAnthropicMessagesForTest(baseTranscript(null));
52
- const lastUser = msgs[msgs.length - 1];
53
- assert.equal(lastUser.role, 'user');
54
- assert.ok(lastUser.content.some((b) => b.type === 'tool_result'), 'trailing turn carries tool_result');
55
- assert.ok(JSON.stringify(lastUser.content).includes('do X instead'), 'plain text folded into tool_result');
56
- });
57
-
58
- test('anthropic.mjs: steering-tagged user text stays a separate user turn', () => {
59
- const msgs = _toAnthropicMessagesForTest(baseTranscript({ source: 'steering' }));
60
- const lastUser = msgs[msgs.length - 1];
61
- assert.equal(lastUser.role, 'user', 'steering message is its own user turn');
62
- const isToolResultTurn = Array.isArray(lastUser.content)
63
- && lastUser.content.some((b) => b.type === 'tool_result');
64
- assert.equal(isToolResultTurn, false, 'steering turn is not a tool_result turn');
65
- assert.ok(JSON.stringify(lastUser.content).includes('do X instead'), 'steering text preserved as user input');
66
- const prev = msgs[msgs.length - 2];
67
- assert.ok(
68
- Array.isArray(prev.content) && prev.content.some((b) => b.type === 'tool_result'),
69
- 'tool_result stays on its own preceding turn',
70
- );
71
- });