mixdog 0.9.63 → 0.9.65

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 (408) 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/turn-snapshot.mjs +184 -0
  187. package/src/runtime/shared/update-checker.mjs +1 -1
  188. package/src/runtime/shared/user-cwd.mjs +3 -3
  189. package/src/runtime/shared/user-data-guard.mjs +4 -4
  190. package/src/runtime/shared/workspace-router.mjs +5 -5
  191. package/src/session-runtime/config-helpers.mjs +2 -3
  192. package/src/session-runtime/config-lifecycle.mjs +1 -1
  193. package/src/session-runtime/hitch-profile.mjs +1 -1
  194. package/src/session-runtime/lifecycle-api.mjs +6 -4
  195. package/src/session-runtime/model-recency.mjs +5 -5
  196. package/src/session-runtime/plugin-mcp.mjs +1 -1
  197. package/src/session-runtime/provider-request-snapshot.mjs +338 -0
  198. package/src/session-runtime/runtime-core.mjs +4 -0
  199. package/src/session-runtime/session-text.mjs +4 -4
  200. package/src/session-runtime/session-turn-api.mjs +7 -0
  201. package/src/session-runtime/tool-catalog-schema.mjs +197 -0
  202. package/src/session-runtime/tool-catalog.mjs +6 -483
  203. package/src/session-runtime/tool-defs.mjs +1 -1
  204. package/src/standalone/agent-task-status.mjs +1 -1
  205. package/src/standalone/agent-tool/helpers.mjs +2 -2
  206. package/src/standalone/channel-admin.mjs +4 -4
  207. package/src/standalone/explore-tool.mjs +3 -3
  208. package/src/standalone/hook-bus/config.mjs +1 -1
  209. package/src/standalone/hook-bus/constants.mjs +1 -1
  210. package/src/standalone/hook-bus/rules.mjs +1 -1
  211. package/src/standalone/projects.mjs +1 -1
  212. package/src/standalone/provider-admin.mjs +2 -2
  213. package/src/tui/App.jsx +1 -1
  214. package/src/tui/app/app-format.mjs +2 -2
  215. package/src/tui/app/clipboard.mjs +3 -3
  216. package/src/tui/app/input-parsers.mjs +1 -1
  217. package/src/tui/app/text-layout.mjs +1 -1
  218. package/src/tui/app/transcript-row-estimate.mjs +360 -0
  219. package/src/tui/app/transcript-window.mjs +5 -347
  220. package/src/tui/app/use-mouse-input.mjs +1 -1
  221. package/src/tui/app/use-prompt-handlers.mjs +1 -1
  222. package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
  223. package/src/tui/components/tool-execution/text-format.mjs +2 -2
  224. package/src/tui/components/tool-output-format.mjs +7 -7
  225. package/src/tui/display-width.mjs +4 -4
  226. package/src/tui/dist/index.mjs +187 -99
  227. package/src/tui/engine/agent-envelope.mjs +5 -5
  228. package/src/tui/engine/live-share.mjs +100 -0
  229. package/src/tui/engine/notification-plan.mjs +1 -1
  230. package/src/tui/engine/queue-helpers.mjs +3 -3
  231. package/src/tui/engine/render-timing.mjs +4 -5
  232. package/src/tui/engine/session-api-ext.mjs +6 -3
  233. package/src/tui/engine/tool-result-status.mjs +5 -5
  234. package/src/tui/engine/tool-result-text.mjs +1 -1
  235. package/src/tui/engine/transcript-spill.mjs +585 -0
  236. package/src/tui/engine/tui-steering-persist.mjs +1 -1
  237. package/src/tui/engine.mjs +29 -474
  238. package/src/tui/lib/voice-setup.mjs +1 -1
  239. package/src/tui/markdown/format-token.mjs +1 -1
  240. package/src/tui/markdown/render-ansi.mjs +1 -1
  241. package/src/tui/markdown/table-layout.mjs +4 -4
  242. package/src/tui/paste-attachments.mjs +1 -1
  243. package/src/tui/prompt-history-store.mjs +3 -3
  244. package/src/tui/statusline-ansi-bridge.mjs +2 -2
  245. package/src/tui/theme.mjs +1 -1
  246. package/src/tui/transcript-tool-failures.mjs +2 -2
  247. package/src/ui/ansi.mjs +0 -9
  248. package/src/ui/statusline-format.mjs +2 -4
  249. package/scripts/_bench-cwc.json +0 -20
  250. package/scripts/_jitter-fuzz.jsx +0 -42
  251. package/scripts/_jitter-fuzz2.jsx +0 -30
  252. package/scripts/_jitter-probe.jsx +0 -35
  253. package/scripts/_jp2.jsx +0 -16
  254. package/scripts/_smoke_wd.mjs +0 -7
  255. package/scripts/abort-recovery-test.mjs +0 -175
  256. package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
  257. package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
  258. package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
  259. package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
  260. package/scripts/agent-loop-policy-test.mjs +0 -37
  261. package/scripts/agent-model-liveness-test.mjs +0 -763
  262. package/scripts/agent-parallel-smoke.mjs +0 -435
  263. package/scripts/agent-route-batch-test.mjs +0 -40
  264. package/scripts/agent-tag-reuse-smoke.mjs +0 -441
  265. package/scripts/agent-terminal-reap-test.mjs +0 -252
  266. package/scripts/agent-trace-io-test.mjs +0 -133
  267. package/scripts/ansi-color-capability-test.mjs +0 -90
  268. package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
  269. package/scripts/anthropic-maxtokens-test.mjs +0 -119
  270. package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
  271. package/scripts/anthropic-transport-policy-test.mjs +0 -726
  272. package/scripts/apply-patch-edit-smoke.mjs +0 -71
  273. package/scripts/arg-guard-test.mjs +0 -93
  274. package/scripts/async-notify-settlement-test.mjs +0 -99
  275. package/scripts/atomic-lock-tryonce-test.mjs +0 -125
  276. package/scripts/background-task-meta-smoke.mjs +0 -38
  277. package/scripts/bench-run.mjs +0 -508
  278. package/scripts/boot-smoke.mjs +0 -137
  279. package/scripts/build-runtime-windows.ps1 +0 -242
  280. package/scripts/channel-daemon-smoke.mjs +0 -1103
  281. package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
  282. package/scripts/code-graph-disk-hit-test.mjs +0 -290
  283. package/scripts/code-graph-dispatch-test.mjs +0 -96
  284. package/scripts/code-graph-root-federation-test.mjs +0 -273
  285. package/scripts/compact-active-turn-test.mjs +0 -68
  286. package/scripts/compact-file-reattach-test.mjs +0 -88
  287. package/scripts/compact-pressure-test.mjs +0 -788
  288. package/scripts/compact-prior-context-flatten-test.mjs +0 -252
  289. package/scripts/compact-recall-digest-test.mjs +0 -57
  290. package/scripts/compact-smoke.mjs +0 -1017
  291. package/scripts/compact-trigger-migration-smoke.mjs +0 -261
  292. package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
  293. package/scripts/context-mcp-metering-test.mjs +0 -1406
  294. package/scripts/debounced-skills-async-save-test.mjs +0 -57
  295. package/scripts/deferred-tool-loading-test.mjs +0 -250
  296. package/scripts/desktop-session-bridge-test.mjs +0 -1013
  297. package/scripts/dispatch-persist-recovery-test.mjs +0 -141
  298. package/scripts/embedding-worker-exit-test.mjs +0 -76
  299. package/scripts/execution-completion-dedup-test.mjs +0 -205
  300. package/scripts/execution-pending-resume-kick-test.mjs +0 -151
  301. package/scripts/execution-resume-esc-integration-test.mjs +0 -176
  302. package/scripts/explore-bench-tmp.mjs +0 -36
  303. package/scripts/explore-bench.mjs +0 -248
  304. package/scripts/explore-prompt-policy-test.mjs +0 -256
  305. package/scripts/explore-timeout-cancel-test.mjs +0 -345
  306. package/scripts/find-fuzzy-hidden-test.mjs +0 -267
  307. package/scripts/forwarder-rebind-tail-test.mjs +0 -67
  308. package/scripts/freevar-smoke.mjs +0 -98
  309. package/scripts/gemini-provider-test.mjs +0 -1448
  310. package/scripts/generate-oc-icons.mjs +0 -11
  311. package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
  312. package/scripts/headless-pristine-execution-test.mjs +0 -614
  313. package/scripts/hook-bus-test.mjs +0 -552
  314. package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
  315. package/scripts/internal-comms-bench-test.mjs +0 -226
  316. package/scripts/internal-comms-bench.mjs +0 -853
  317. package/scripts/internal-comms-smoke.mjs +0 -277
  318. package/scripts/internal-tools-normalization-test.mjs +0 -62
  319. package/scripts/interrupted-turn-history-test.mjs +0 -399
  320. package/scripts/lead-workflow-smoke.mjs +0 -592
  321. package/scripts/lifecycle-api-test.mjs +0 -137
  322. package/scripts/live-share-test.mjs +0 -148
  323. package/scripts/live-worker-smoke.mjs +0 -333
  324. package/scripts/log-writer-guard-smoke.mjs +0 -131
  325. package/scripts/maintenance-default-routes-test.mjs +0 -164
  326. package/scripts/max-output-recovery-persist-test.mjs +0 -81
  327. package/scripts/max-output-recovery-test.mjs +0 -368
  328. package/scripts/mcp-client-normalization-test.mjs +0 -45
  329. package/scripts/mcp-grace-deferred-test.mjs +0 -225
  330. package/scripts/memory-core-input-test.mjs +0 -167
  331. package/scripts/memory-cycle-routing-test.mjs +0 -111
  332. package/scripts/memory-meta-concurrency-test.mjs +0 -20
  333. package/scripts/memory-pg-recovery-test.mjs +0 -59
  334. package/scripts/memory-rule-contract-test.mjs +0 -93
  335. package/scripts/model-list-sanitize-test.mjs +0 -37
  336. package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
  337. package/scripts/native-edit-wire-test.mjs +0 -152
  338. package/scripts/notify-completion-mirror-test.mjs +0 -73
  339. package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
  340. package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
  341. package/scripts/openai-ws-early-settle-test.mjs +0 -216
  342. package/scripts/output-style-bench.mjs +0 -292
  343. package/scripts/output-style-smoke.mjs +0 -145
  344. package/scripts/parent-abort-link-test.mjs +0 -90
  345. package/scripts/patch-binary-cache-test.mjs +0 -181
  346. package/scripts/path-suffix-test.mjs +0 -57
  347. package/scripts/pending-completion-drop-test.mjs +0 -239
  348. package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
  349. package/scripts/pretool-ask-runtime-test.mjs +0 -54
  350. package/scripts/process-lifecycle-test.mjs +0 -461
  351. package/scripts/prompt-immediate-render-test.mjs +0 -89
  352. package/scripts/prompt-input-parity-test.mjs +0 -145
  353. package/scripts/provider-admission-scheduler-test.mjs +0 -681
  354. package/scripts/provider-contract-test.mjs +0 -632
  355. package/scripts/provider-stream-stall-test.mjs +0 -276
  356. package/scripts/provider-toolcall-test.mjs +0 -3063
  357. package/scripts/reactive-compact-persist-smoke.mjs +0 -203
  358. package/scripts/recall-bench.mjs +0 -359
  359. package/scripts/resource-admission-test.mjs +0 -792
  360. package/scripts/result-classification-test.mjs +0 -75
  361. package/scripts/rg-runner-test.mjs +0 -240
  362. package/scripts/routing-corpus-test.mjs +0 -349
  363. package/scripts/sanitize-tool-pairs-test.mjs +0 -260
  364. package/scripts/session-bench-cache-break-test.mjs +0 -102
  365. package/scripts/session-bench.mjs +0 -1708
  366. package/scripts/session-context-bench.mjs +0 -344
  367. package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
  368. package/scripts/session-ingest-compaction-smoke.mjs +0 -279
  369. package/scripts/session-ingest-smoke.mjs +0 -177
  370. package/scripts/session-orphan-sweep-test.mjs +0 -109
  371. package/scripts/set-effort-config-test.mjs +0 -41
  372. package/scripts/shell-failure-diagnostics-test.mjs +0 -280
  373. package/scripts/shell-hardening-test.mjs +0 -209
  374. package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
  375. package/scripts/ship-mode-test.mjs +0 -98
  376. package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
  377. package/scripts/smoke-loop-failure-summary.mjs +0 -16
  378. package/scripts/smoke-loop-report.mjs +0 -221
  379. package/scripts/smoke-loop.mjs +0 -202
  380. package/scripts/smoke-runtime-negative.ps1 +0 -106
  381. package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
  382. package/scripts/steering-drain-buckets-test.mjs +0 -467
  383. package/scripts/steering-fold-provenance-test.mjs +0 -71
  384. package/scripts/streaming-tail-window-test.mjs +0 -175
  385. package/scripts/submit-commandbusy-race-test.mjs +0 -257
  386. package/scripts/task-bench.mjs +0 -205
  387. package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
  388. package/scripts/termio-input-smoke.mjs +0 -199
  389. package/scripts/tool-result-hook-test.mjs +0 -48
  390. package/scripts/tool-smoke.mjs +0 -2453
  391. package/scripts/tool-tui-presentation-test.mjs +0 -286
  392. package/scripts/toolcall-args-test.mjs +0 -239
  393. package/scripts/tui-ambiguous-width-test.mjs +0 -113
  394. package/scripts/tui-background-failure-smoke.mjs +0 -73
  395. package/scripts/tui-perf-run.ps1 +0 -26
  396. package/scripts/tui-render-smoke.mjs +0 -90
  397. package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
  398. package/scripts/tui-runtime-load-bench.mjs +0 -50
  399. package/scripts/tui-store-frame-batch-test.mjs +0 -99
  400. package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
  401. package/scripts/tui-transcript-perf-test.mjs +0 -934
  402. package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
  403. package/scripts/verify-release-assets-test.mjs +0 -281
  404. package/scripts/web-fetch-routing-test.mjs +0 -158
  405. package/scripts/webhook-smoke.mjs +0 -201
  406. package/scripts/windows-hide-spawn-options-test.mjs +0 -19
  407. package/scripts/worker-notify-rejection-test.mjs +0 -51
  408. 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
- });