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,256 +0,0 @@
1
- #!/usr/bin/env node
2
- import test from 'node:test';
3
- import assert from 'node:assert/strict';
4
- import { readFileSync } from 'node:fs';
5
- import { buildExplorerPrompt } from '../src/standalone/explore-tool.mjs';
6
- import { EXPLORE_TOOL } from '../src/standalone/explore-tool.mjs';
7
- import { BUILTIN_TOOLS } from '../src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs';
8
- import { CODE_GRAPH_TOOL_DEFS } from '../src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs';
9
- import { TOOL_SEARCH_TOOL } from '../src/session-runtime/tool-defs.mjs';
10
- import { createEagerDispatcher } from '../src/runtime/agent/orchestrator/session/eager-dispatch.mjs';
11
- import { crossTurnSignature } from '../src/runtime/agent/orchestrator/session/loop/completion-guards.mjs';
12
- import {
13
- isEagerDispatchable,
14
- isToolCallDedupEligible,
15
- } from '../src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs';
16
- import { assertCodeGraphDescriptionContract } from './code-graph-description-contract.mjs';
17
-
18
- test('explore per-query prompt contains only escaped query XML', () => {
19
- const prompt = buildExplorerPrompt('display model usage show usage model_usage provider_usage session cache usage state');
20
- assert.equal(prompt, '<query>display model usage show usage model_usage provider_usage session cache usage state</query>');
21
- assert.doesNotMatch(prompt, /Reminder:|BUDGET|TURN 1|STOP and answer|verdicts|ratings|recommendations/i);
22
- assert.equal(buildExplorerPrompt('where is <agent> & status?'), '<query>where is &lt;agent&gt; &amp; status?</query>');
23
- });
24
-
25
- test('builtin route descriptions carry the shortest verified-path policy', () => {
26
- const byName = Object.fromEntries(BUILTIN_TOOLS.map((tool) => [tool.name, tool]));
27
- // Conditional find-first: only genuinely guessed fragments route through
28
- // find, and in the SAME turn — project root itself is a verified scope.
29
- assert.match(byName.grep.description, /project root counts as verified/i);
30
- assert.match(byName.grep.description, /guessed path fragment → find first/i);
31
- assert.match(byName.grep.description, /no path "\." \+ guessed src\/\*\*/i);
32
- assert.match(byName.glob.description, /project root is verified/i);
33
- assert.match(byName.glob.description, /Guessed root\/name → find first/i);
34
- assert.match(byName.find.description, /output paths are verified/i);
35
- assert.match(byName.read.description, /guessed path\/name → find first/i);
36
- assert.match(byName.read.description, /Batch paths\/regions as real arrays.*path\[\]/i);
37
- assert.match(byName.shell.description, /Shell\/write calls are serial/i);
38
- assert.doesNotMatch(byName.shell.description, /parallel calls/i);
39
- assert.match(byName.find.description, /lookup only for unknown partial paths\/names/i);
40
- assert.match(byName.find.description, /project root or already-verified roots/i);
41
- assert.doesNotMatch(byName.grep.description, /find for that fragment before any grep\/glob/i);
42
- assert.match(byName.grep.description, /nonzero content_with_context result resolves that search concept/i);
43
- assert.match(byName.grep.description, /Only zero\/error results may change tokens or scope/i);
44
- assert.match(`${byName.code_graph?.description || ''} ${CODE_GRAPH_TOOL_DEFS[0]?.inputSchema?.properties?.symbols?.description || ''}`, /multiple exact symbols use one symbols\[\] call/i);
45
- assert.match(CODE_GRAPH_TOOL_DEFS[0]?.description || '', /verified source files only/i);
46
- });
47
-
48
- test('shared tool policy routes facets without duplicate content acquisition', () => {
49
- const rule = readFileSync(new URL('../src/rules/shared/01-tool.md', import.meta.url), 'utf8');
50
- const policy = rule.replace(/\s+/g, ' ');
51
- assert.match(policy, /for each facet choose exactly one shortest locator route/i);
52
- assert.match(policy, /broad\/uncertain→\s*`explore`.*partial path\/name→\s*`find`.*verified root\+wildcard→\s*`glob`.*quoted\/non-identifier literal or regex→\s*`grep`.*exact code identifier\/relation→\s*`code_graph` before grep/i);
53
- assert.match(policy, /grep only for a requested literal occurrence or after graph zero\/error/i);
54
- assert.match(policy, /batch compatible targets/i);
55
- assert.match(policy, /parallelize distinct facets only, never alternative routes for one facet/i);
56
- assert.match(policy, /put independent read-only calls in one turn; they may run concurrently regardless of tool/i);
57
- assert.match(policy, /after locator results, collect all known candidate files\/regions before inspection/i);
58
- assert.match(policy, /batch compatible reads.*same-file regions.*in one `path\[\]` call and graph targets in arrays/i);
59
- assert.match(policy, /parallelize independent incompatible read-only inspections/i);
60
- assert.match(policy, /do not start a singleton while a known compatible candidate remains/i);
61
- assert.match(policy, /shell\/write calls are serial/i);
62
- assert.match(policy, /later turns are only for targets dependent on prior results or unresolved facets/i);
63
- assert.match(policy, /stop when evidence covers the deliverable/i);
64
- assert.match(policy, /sufficient contextual grep means no overlapping `read`/i);
65
- assert.match(policy, /files_with_matches.*count.*capped.*insufficient context.*inspect only missing content/i);
66
- assert.match(policy, /known file\/span→`read` directly without `grep`/i);
67
- assert.doesNotMatch(policy, /retrieval[^.]{0,120}\b(?:one|at most \d+)\s+(?:lookup|inspection|turn|call)/i);
68
- assert.doesNotMatch(policy, /\bput independent calls in one turn\b/i);
69
- assert.doesNotMatch(policy, /\b(?:all|every)\s+(?:independent\s+)?(?:calls|tool calls)\b[^.]{0,80}\bone turn\b/i);
70
- assert.doesNotMatch(policy, /\bone lookup\b[^.]{0,80}\bat most\b[^.]{0,40}\binspection\b/i);
71
- assert.doesNotMatch(policy, /all (?:independent )?(?:calls|tool calls|targets)[^.]{0,100}\bone (?:tool )?(?:message|call)/i);
72
- });
73
-
74
- test('explorer locator policy retains its compact behavioral contract', () => {
75
- const rule = readFileSync(new URL('../src/rules/agent/30-explorer.md', import.meta.url), 'utf8');
76
- const policy = rule.replace(/\s+/g, ' ');
77
- const required = [
78
- /Return only WHERE \(`path:line`\), never WHY[\s\S]*You ARE `explore`; never call it/i,
79
- /only grep\/find\/glob\/code_graph[\s\S]*`read` and `list` are forbidden/i,
80
- /Turn 1 \(`turn 1\/3`\) is the whole search[\s\S]*Split broad\/uncertain input into every known facet[\s\S]*one batch under the shared one-route contract[\s\S]*upstream producer\/derivation layer[\s\S]*SAME batch[\s\S]*Follow-up turns batch every unresolved facet in parallel[\s\S]*single-tool turn is allowed only when exactly one pre-anchor\/zero-hit facet remains/i,
81
- /broad grep use `output_mode:"files_with_matches"`[\s\S]*`content_with_context` with `head_limit` only on paths returned this session/i,
82
- /Each pattern is one identifier, camel\/snake variant, or concept synonym[\s\S]*never a prose phrase[\s\S]*Spaces and non-ASCII are allowed only in verbatim quoted error\/log literals[\s\S]*Translate other non-English queries to English identifiers/i,
83
- /Scope is session cwd[\s\S]*For unverified `src` paths, use `find` first[\s\S]*never guess or invent directories[\s\S]*`path:"\."` with guessed `src\/\*\*`[\s\S]*exact find-returned path[\s\S]*no earlier than turn 2[\s\S]*After zero hits, change tokens or scope, never wording or guessed paths/i,
84
- /anchor is a `path:line` containing a query token or synonym[\s\S]*code_graph hit[\s\S]*Generic terms without query specificity are zero[\s\S]*Never re-locate, reconfirm, or upgrade an anchor[\s\S]*path without `:line` is a pre-anchor and counts as zero/i,
85
- /After every result, stop and answer on any specific-token anchor[\s\S]*mark a weak anchor `\?`/i,
86
- /code-location query left only with pre-anchors[\s\S]*sole anchor-minting follow-up[\s\S]*one scoped `content_with_context` grep with `head_limit`[\s\S]*If it returns zero[\s\S]*changed tokens or scope[\s\S]*Never make a second minting hop or fabricate\/estimate a line/i,
87
- /at most 3 turns[\s\S]*label every tool message `turn N\/3`[\s\S]*normally use one batch and one answer[\s\S]*Turns 2–3 are allowed only when turn 1 has zero anchors/i,
88
- /first matching entry\/definition anchors a concept, value, or default[\s\S]*never trace its chain[\s\S]*explicit flow or default-resolution query[\s\S]*entry anchor but no resolved value[\s\S]*turn 2 for one resolving hop/i,
89
- /Answer in at most 3 lines[\s\S]*`path:line — symbol — short reason`[\s\S]*Copy every cited `path:line` verbatim[\s\S]*tool result in this session[\s\S]*never estimate, adjust, or recall/i,
90
- /Every code-location line requires `:line`[\s\S]*never return a bare filename or vague prose/i,
91
- /file\/dir-location query may return an exact verified path without `:line`/i,
92
- /Return `EXPLORATION_FAILED` only after spending the budget with zero anchors[\s\S]*prefer a weak anchor to a false miss/i,
93
- ];
94
- for (const behavior of required) assert.match(policy, behavior);
95
- assert.doesNotMatch(policy, /grep[^.]{0,120}\band\b[^.]{0,120}code_graph[^.]{0,120}\band\b[^.]{0,120}find/i);
96
- });
97
-
98
- test('canonical schemas advertise safe batching without changing tool shapes', () => {
99
- const graph = CODE_GRAPH_TOOL_DEFS[0];
100
- const patch = readFileSync(new URL('../src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs', import.meta.url), 'utf8');
101
- const mode = graph?.inputSchema?.properties?.mode?.description || '';
102
- const description = graph?.description || '';
103
- const symbols = graph?.inputSchema?.properties?.symbols?.description || '';
104
- assert.doesNotThrow(() => assertCodeGraphDescriptionContract({
105
- description,
106
- modeDescription: mode,
107
- symbolsDescription: symbols,
108
- }));
109
- assert.deepEqual(graph?.inputSchema?.required, ['mode']);
110
- assert.equal(graph?.inputSchema?.properties?.file, undefined);
111
- assert.equal(graph?.inputSchema?.properties?.symbol, undefined);
112
- assert.match(patch, /one patch/i);
113
- assert.match(patch, /one file block per target/i);
114
- });
115
-
116
- test('code graph and eager-dispatch boundaries preserve runtime shape', () => {
117
- const schema = CODE_GRAPH_TOOL_DEFS[0]?.inputSchema?.properties || {};
118
- const fileModes = ['overview', 'imports', 'dependents', 'related', 'impact', 'symbols'];
119
- const symbolModes = ['symbols', 'find_symbol', 'symbol_search', 'search', 'references', 'callers', 'callees'];
120
- assert.deepEqual(new Set(schema.mode?.enum), new Set([...fileModes, ...symbolModes]));
121
- const assertBatchShape = (field) => {
122
- assert.equal(field?.anyOf?.length, 2);
123
- assert.deepEqual(field.anyOf.map((entry) => entry.type), ['string', 'array']);
124
- assert.equal(field.anyOf[1].items.type, 'string');
125
- assert.equal(field.anyOf[1].minItems, 1);
126
- };
127
- assertBatchShape(schema.files);
128
- assertBatchShape(schema.symbols);
129
- assert.equal(schema.file, undefined);
130
- assert.equal(schema.symbol, undefined);
131
- assert.equal(schema.language, undefined);
132
- const tools = [
133
- ...BUILTIN_TOOLS,
134
- { name: 'mcp_read', annotations: { readOnlyHint: true } },
135
- { name: 'mcp_write', annotations: { readOnlyHint: false } },
136
- ];
137
- assert.equal(isEagerDispatchable('read', tools), true);
138
- assert.equal(isEagerDispatchable('shell', tools), false);
139
- assert.equal(isEagerDispatchable('mcp_read', tools), true);
140
- assert.equal(isEagerDispatchable('mcp_write', tools), false);
141
- assert.equal(isToolCallDedupEligible('read', tools), true);
142
- assert.equal(isToolCallDedupEligible('mcp_read', tools), true);
143
- });
144
-
145
- test('same-batch load_tool and legacy tool_search repeats all execute eagerly', async () => {
146
- const tools = [...BUILTIN_TOOLS, TOOL_SEARCH_TOOL];
147
- assert.equal(isEagerDispatchable('load_tool', tools), true);
148
- assert.equal(isEagerDispatchable('tool_search', tools), true);
149
- assert.equal(isToolCallDedupEligible('load_tool', tools), false);
150
- assert.equal(isToolCallDedupEligible('tool_search', tools), false);
151
- assert.equal(isToolCallDedupEligible('read', tools), true);
152
-
153
- const args = { names: ['shell'] };
154
- const calls = [
155
- { id: 'load-1', name: 'load_tool', arguments: args },
156
- { id: 'load-2', name: 'load_tool', arguments: args },
157
- { id: 'legacy-1', name: 'tool_search', arguments: args },
158
- { id: 'legacy-2', name: 'tool_search', arguments: args },
159
- ];
160
- const executed = [];
161
- const crossTurnCalls = new Map([
162
- [crossTurnSignature('load_tool', args), { count: 1, firstIteration: 1 }],
163
- [crossTurnSignature('tool_search', args), { count: 1, firstIteration: 1 }],
164
- ]);
165
- const dispatcher = createEagerDispatcher({
166
- tools,
167
- cwd: process.cwd(),
168
- sessionId: null,
169
- sessionRef: {},
170
- signal: null,
171
- opts: {},
172
- crossTurnCalls,
173
- getIterations: () => 2,
174
- getNextIteration: () => 2,
175
- repeatFailLimit: 3,
176
- executeToolFn: async (name) => {
177
- executed.push(name);
178
- return '{}';
179
- },
180
- });
181
- dispatcher.startEagerRun(calls, 0, new Set());
182
- assert.equal(dispatcher.pending.size, 4);
183
- await Promise.all([...dispatcher.pending.values()].map((entry) => entry.promise));
184
- assert.deepEqual(executed, ['load_tool', 'load_tool', 'tool_search', 'tool_search']);
185
-
186
- const normalExecuted = [];
187
- const normalDispatcher = createEagerDispatcher({
188
- tools,
189
- cwd: process.cwd(),
190
- sessionId: null,
191
- sessionRef: {},
192
- signal: null,
193
- opts: {},
194
- crossTurnCalls: new Map(),
195
- getIterations: () => 1,
196
- getNextIteration: () => 1,
197
- repeatFailLimit: 3,
198
- executeToolFn: async (name) => {
199
- normalExecuted.push(name);
200
- return 'ok';
201
- },
202
- });
203
- normalDispatcher.startEagerRun([
204
- { id: 'read-1', name: 'read', arguments: { path: 'same.txt' } },
205
- { id: 'read-2', name: 'read', arguments: { path: 'same.txt' } },
206
- ], 0, new Set());
207
- await Promise.all([...normalDispatcher.pending.values()].map((entry) => entry.promise));
208
- assert.deepEqual(normalExecuted, ['read']);
209
- });
210
-
211
- test('code graph descriptions partition file and symbol targets', () => {
212
- const description = CODE_GRAPH_TOOL_DEFS[0]?.description || '';
213
- const mode = CODE_GRAPH_TOOL_DEFS[0]?.inputSchema?.properties?.mode?.description || '';
214
- assert.match(mode, /file modes=\{overview,imports,dependents,related,impact\}.*symbols with files.*files\[\].*file outline/i);
215
- assert.match(mode, /symbol modes=\{find_symbol,symbol_search,search,references,callers,callees\}.*fileless symbols.*symbol_search keywords/i);
216
- assert.match(description, /exact identifiers.*find_symbol\/references\/callers\/callees.*keywords.*symbol_search\/search/i);
217
- assert.match(description, /IDs→graph; literal\/zero→grep/i);
218
- assert.match(description, /unsupported target arrays.*omitted.*never silently mixed/i);
219
- assert.match(CODE_GRAPH_TOOL_DEFS[0]?.inputSchema?.properties?.files?.description || '', /supported targets only/i);
220
- assert.match(CODE_GRAPH_TOOL_DEFS[0]?.inputSchema?.properties?.symbols?.description || '', /exact identifiers.*keywords/i);
221
- const grep = Object.fromEntries(BUILTIN_TOOLS.map((tool) => [tool.name, tool])).grep;
222
- assert.match(grep.description, /quoted\/non-identifier literal or regex→grep/i);
223
- assert.doesNotMatch(grep.inputSchema.properties.pattern.description, /code_graph/i);
224
- });
225
-
226
- test('retrieval schemas require their primary arguments and preserve region paths', () => {
227
- const byName = Object.fromEntries(BUILTIN_TOOLS.map((tool) => [tool.name, tool]));
228
- const read = byName.read.inputSchema;
229
- assert.deepEqual(read.required, ['path']);
230
- const region = read.properties.path.anyOf[1].items.anyOf[1];
231
- assert.deepEqual(region.required, ['path']);
232
- assert.deepEqual(byName.grep.inputSchema.anyOf, [{ required: ['pattern'] }, { required: ['glob'] }]);
233
- assert.deepEqual(byName.grep.inputSchema.properties.output_mode.enum, ['content_with_context', 'files_with_matches', 'count']);
234
- const grepSchema = byName.grep.inputSchema;
235
- const valid = (value) => grepSchema.anyOf.some((branch) => branch.required.every((key) => Object.hasOwn(value, key)));
236
- assert.equal(valid({ pattern: 'x' }), true);
237
- assert.equal(valid({ glob: '*.mjs' }), true);
238
- assert.equal(valid({}), false);
239
- });
240
-
241
- test('grep scopes do not masquerade as read regions', () => {
242
- const pattern = Object.fromEntries(BUILTIN_TOOLS.map((tool) => [tool.name, tool])).grep.inputSchema.properties.pattern.description;
243
- assert.match(pattern, /path\[\] batches verified scopes only/i);
244
- assert.match(pattern, /file\/span reads use read path\[\] regions/i);
245
- assert.doesNotMatch(pattern, /known files\/spans use path\[\]/i);
246
- });
247
-
248
- test('explore freezes returned facets and avoids re-location', () => {
249
- assert.match(EXPLORE_TOOL.description, /freezes the LOCATION only/i);
250
- assert.match(EXPLORE_TOOL.description, /read\/code_graph detail inspection is valid when content was not returned/i);
251
- assert.match(EXPLORE_TOOL.description, /never re-locate it/i);
252
- assert.match(EXPLORE_TOOL.description, /search only unresolved facets/i);
253
- const rule = readFileSync(new URL('../src/rules/shared/01-tool.md', import.meta.url), 'utf8');
254
- assert.match(rule, /freezes the LOCATION only[\s\S]*read or[\s\S]*code_graph detail inspection is valid[\s\S]*never re-locate it/i);
255
- assert.match(rule, /nonzero[\s\S]*content_with_context[\s\S]*result resolves[\s\S]*act directly/i);
256
- });
@@ -1,345 +0,0 @@
1
- #!/usr/bin/env node
2
- // Regression test for explore-tool wall-clock timeout + cancellation cleanup:
3
- // - the default hard timeout is 60s (was 10min) and the
4
- // MIXDOG_EXPLORE_HARD_TIMEOUT_MS override (including 0 = disabled) is kept;
5
- // - a parent cancellation aborts every child compute AbortSignal immediately;
6
- // - the wall-clock hard timeout aborts the compute AbortSignal (not just the
7
- // race), so wedged compute tears down instead of running detached;
8
- // - a canceled/timed-out compute purges its poisoned cache entry so a later
9
- // call never awaits a dead promise and empties into "no tool output".
10
- import test from 'node:test';
11
- import assert from 'node:assert/strict';
12
- import { execFileSync } from 'node:child_process';
13
- import { readFileSync } from 'node:fs';
14
- import {
15
- EXPLORE_COMPUTE_HARD_TIMEOUT_MS,
16
- runExploreComputeWithAbort,
17
- runExploreCached,
18
- runExplore,
19
- exploreStaggerDelay,
20
- awaitExploreProviderReadyOrCancel,
21
- ensureExploreProviderReady,
22
- __exploreResultCacheForTest,
23
- } from '../src/standalone/explore-tool.mjs';
24
-
25
- const MODULE_URL = new URL('../src/standalone/explore-tool.mjs', import.meta.url).href;
26
-
27
- // A compute stub that rejects the moment its AbortSignal fires (mirrors a real
28
- // child dispatch tearing down on abort), and handles the already-aborted case.
29
- function abortAwareCompute(record) {
30
- return (signal) => new Promise((_resolve, reject) => {
31
- record?.(signal);
32
- if (signal.aborted) { reject(signal.reason || new Error('aborted')); return; }
33
- signal.addEventListener('abort', () => reject(signal.reason || new Error('aborted')), { once: true });
34
- });
35
- }
36
-
37
- function readHardTimeoutWithEnv(value) {
38
- const code = `import(${JSON.stringify(MODULE_URL)}).then((m) => process.stdout.write(String(m.EXPLORE_COMPUTE_HARD_TIMEOUT_MS)))`;
39
- return execFileSync(process.execPath, ['--input-type=module', '-e', code], {
40
- env: { ...process.env, MIXDOG_EXPLORE_HARD_TIMEOUT_MS: value },
41
- encoding: 'utf8',
42
- }).trim();
43
- }
44
-
45
- // The production hard-timeout timer is unref()'d (must never keep the host
46
- // process alive). In an otherwise-idle test that relies on that timer firing,
47
- // hold a ref'd timer so the event loop stays alive until the assertion settles.
48
- function keepEventLoopAlive(ms = 5_000) {
49
- const t = setTimeout(() => {}, ms);
50
- return () => clearTimeout(t);
51
- }
52
-
53
- test('default explore hard timeout is 60 seconds', () => {
54
- // Only meaningful when the override is unset (the CI/default environment).
55
- if (process.env.MIXDOG_EXPLORE_HARD_TIMEOUT_MS == null) {
56
- assert.equal(EXPLORE_COMPUTE_HARD_TIMEOUT_MS, 60_000);
57
- }
58
- });
59
-
60
- test('MIXDOG_EXPLORE_HARD_TIMEOUT_MS override is preserved (including 0 = disabled)', () => {
61
- assert.equal(readHardTimeoutWithEnv('1234'), '1234');
62
- assert.equal(readHardTimeoutWithEnv('0'), '0');
63
- assert.equal(readHardTimeoutWithEnv('90000'), '90000');
64
- });
65
-
66
- test('hard timeout aborts the compute AbortSignal', async () => {
67
- const release = keepEventLoopAlive();
68
- try {
69
- let received = null;
70
- const p = runExploreComputeWithAbort((signal) => {
71
- received = signal;
72
- return new Promise(() => {}); // never settles on its own
73
- }, null, 25);
74
- await assert.rejects(p, /timed out/);
75
- assert.ok(received, 'compute received a signal');
76
- assert.equal(received.aborted, true);
77
- } finally {
78
- release();
79
- }
80
- });
81
-
82
- test('parent cancellation aborts every child compute immediately', async () => {
83
- const parent = new AbortController();
84
- const seen = [];
85
- const p1 = runExploreComputeWithAbort(abortAwareCompute((s) => seen.push(s)), parent.signal, 10_000);
86
- const p2 = runExploreComputeWithAbort(abortAwareCompute((s) => seen.push(s)), parent.signal, 10_000);
87
- parent.abort(new Error('user pressed ESC'));
88
- await assert.rejects(p1);
89
- await assert.rejects(p2);
90
- assert.equal(seen.length, 2);
91
- assert.ok(seen.every((s) => s.aborted), 'both child signals aborted');
92
- });
93
-
94
- test('an already-aborted parent signal aborts the compute up front', async () => {
95
- const parent = new AbortController();
96
- parent.abort(new Error('already canceled'));
97
- let received = null;
98
- const p = runExploreComputeWithAbort(abortAwareCompute((s) => { received = s; }), parent.signal, 10_000);
99
- await assert.rejects(p);
100
- assert.ok(received.aborted, 'compute signal was already aborted');
101
- });
102
-
103
- test('a canceled compute purges its poisoned cache entry', async () => {
104
- // Force the result cache on so the in-flight promise is stored.
105
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
106
- const cache = __exploreResultCacheForTest();
107
- cache.clear();
108
- const key = 'explore-cancel-poison-key';
109
- const parent = new AbortController();
110
- const promise = runExploreCached(key, abortAwareCompute(), parent.signal);
111
- // Pending promise cached while the compute is in flight.
112
- assert.equal(cache.has(key), true);
113
- parent.abort(new Error('cancel'));
114
- await assert.rejects(promise);
115
- // The poisoned pending entry must be gone so a later call recomputes fresh
116
- // instead of awaiting the dead promise.
117
- assert.equal(cache.has(key), false);
118
- });
119
-
120
- test('a wedged never-resolving shared compute is purged by its real hard-timeout timer and recomputed', async () => {
121
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
122
- const cache = __exploreResultCacheForTest();
123
- cache.clear();
124
- const key = 'shared-hard-timeout-wedged';
125
- const seen = [];
126
- let calls = 0;
127
- const neverResolves = (signal) => { calls += 1; seen.push(signal); return new Promise(() => {}); };
128
- const release = keepEventLoopAlive();
129
- try {
130
- await assert.rejects(runExploreCached(key, neverResolves, null, 25), /timed out/);
131
- assert.equal(seen[0].aborted, true, 'the timer aborted the actual wedged compute');
132
- assert.equal(cache.has(key), false, 'timed-out pending entry was purged');
133
- assert.equal(await runExploreCached(key, () => { calls += 1; return Promise.resolve('fresh'); }, null, 100), 'fresh');
134
- assert.equal(calls, 2, 'a later caller recomputes rather than reusing the rejected promise');
135
- } finally {
136
- release();
137
- }
138
- });
139
-
140
- // A compute whose settlement the test controls, recording the AbortSignal it
141
- // received so shared-compute teardown can be asserted.
142
- function deferredCompute() {
143
- const handle = { signal: null, resolve: null, reject: null };
144
- handle.fn = (signal) => new Promise((resolve, reject) => {
145
- handle.signal = signal;
146
- handle.resolve = resolve;
147
- handle.reject = reject;
148
- });
149
- return handle;
150
- }
151
-
152
- test('parent cancellation rejects immediately even for a non-cooperative compute', async () => {
153
- // The compute IGNORES its AbortSignal (never settles on abort). Cancellation
154
- // must still reject the returned promise right away — not hang until the
155
- // wall-clock hard timeout.
156
- const parent = new AbortController();
157
- const p = runExploreComputeWithAbort(() => new Promise(() => {}), parent.signal, 10_000);
158
- const t0 = Date.now();
159
- parent.abort(new Error('user pressed ESC'));
160
- await assert.rejects(p);
161
- assert.ok(Date.now() - t0 < 500, 'rejected promptly, not after the hard timeout');
162
- });
163
-
164
- test('never-resolving compute is torn down by the hard timeout (real cleanup)', async () => {
165
- const release = keepEventLoopAlive();
166
- try {
167
- let received = null;
168
- const p = runExploreComputeWithAbort((signal) => {
169
- received = signal;
170
- return new Promise(() => {}); // never resolves, never observes abort
171
- }, null, 30);
172
- await assert.rejects(p, /timed out/);
173
- assert.equal(received.aborted, true, 'compute signal aborted on timeout');
174
- } finally {
175
- release();
176
- }
177
- });
178
-
179
- test('owner cancellation does not abort an unaffected later subscriber', async () => {
180
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
181
- const cache = __exploreResultCacheForTest();
182
- cache.clear();
183
- const key = 'shared-owner-cancel';
184
- const dc = deferredCompute();
185
- const owner = new AbortController();
186
- const later = new AbortController();
187
- const ownerP = runExploreCached(key, dc.fn, owner.signal); // starts shared compute
188
- const laterP = runExploreCached(key, dc.fn, later.signal); // subscribes to the same compute
189
- owner.abort(new Error('owner ESC'));
190
- await assert.rejects(ownerP);
191
- // The shared compute must keep running for the still-active later subscriber.
192
- assert.equal(dc.signal.aborted, false, 'shared compute not aborted by owner cancel');
193
- assert.equal(cache.has(key), true);
194
- dc.resolve('src/x.mjs:1 — shared hit');
195
- assert.match(String(await laterP), /src\/x\.mjs:1/);
196
- });
197
-
198
- test('later subscriber cancellation releases itself without aborting the shared compute', async () => {
199
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
200
- const cache = __exploreResultCacheForTest();
201
- cache.clear();
202
- const key = 'shared-later-cancel';
203
- const dc = deferredCompute();
204
- const owner = new AbortController();
205
- const later = new AbortController();
206
- const ownerP = runExploreCached(key, dc.fn, owner.signal);
207
- const laterP = runExploreCached(key, dc.fn, later.signal);
208
- later.abort(new Error('later ESC'));
209
- await assert.rejects(laterP);
210
- assert.equal(dc.signal.aborted, false, 'owner still waiting; compute alive');
211
- assert.equal(cache.has(key), true);
212
- dc.resolve('src/y.mjs:2 — owner result');
213
- assert.match(String(await ownerP), /src\/y\.mjs:2/);
214
- });
215
-
216
- test('all subscribers canceling aborts the shared compute and purges the cache', async () => {
217
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
218
- const cache = __exploreResultCacheForTest();
219
- cache.clear();
220
- const key = 'shared-all-cancel';
221
- const dc = deferredCompute();
222
- const a = new AbortController();
223
- const b = new AbortController();
224
- const pa = runExploreCached(key, dc.fn, a.signal);
225
- const pb = runExploreCached(key, dc.fn, b.signal);
226
- a.abort(new Error('a ESC'));
227
- await assert.rejects(pa);
228
- assert.equal(cache.has(key), true, 'b still subscribed; compute retained');
229
- assert.equal(dc.signal.aborted, false);
230
- b.abort(new Error('b ESC'));
231
- await assert.rejects(pb);
232
- assert.equal(dc.signal.aborted, true, 'last subscriber gone; shared compute torn down');
233
- assert.equal(cache.has(key), false, 'purged so a future call recomputes');
234
- });
235
-
236
- test('a failed shared compute is purged so future calls recompute', async () => {
237
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
238
- const cache = __exploreResultCacheForTest();
239
- cache.clear();
240
- const key = 'shared-recompute';
241
- let calls = 0;
242
- const compute = () => { calls += 1; return Promise.reject(new Error(`owner fail ${calls}`)); };
243
- await assert.rejects(runExploreCached(key, compute, null));
244
- assert.equal(cache.has(key), false);
245
- await assert.rejects(runExploreCached(key, compute, null));
246
- assert.equal(calls, 2, 'recomputed rather than deduping on the dead promise');
247
- });
248
-
249
- test('stagger delay is canceled before dispatch when the signal aborts', async () => {
250
- const ctrl = new AbortController();
251
- let dispatched = false;
252
- const p = exploreStaggerDelay(10_000, ctrl.signal).then(() => { dispatched = true; return 'DISPATCHED'; });
253
- ctrl.abort(new Error('cancel during stagger'));
254
- await assert.rejects(p);
255
- assert.equal(dispatched, false, 'child dispatch skipped after the stagger was canceled');
256
- });
257
-
258
- test('runtime-core forwards the caller signal into runExplore (plumbing)', () => {
259
- const src = readFileSync(new URL('../src/session-runtime/runtime-core.mjs', import.meta.url), 'utf8');
260
- const at = src.indexOf("name === 'explore'");
261
- assert.ok(at >= 0, 'explore branch present in runtime-core');
262
- const branch = src.slice(at, at + 1000);
263
- assert.match(branch, /return await runExplore\(/);
264
- assert.match(branch, /signal:\s*callerCtx\?\.signal\s*\|\|\s*session\?\.controller\?\.signal/);
265
- });
266
-
267
- test('runExplore short-circuits an already-canceled call without dispatching', async () => {
268
- const ctrl = new AbortController();
269
- ctrl.abort(new Error('canceled up front'));
270
- const res = await runExplore({ query: 'anything at all' }, { signal: ctrl.signal });
271
- assert.equal(res.isError, true);
272
- assert.match(res.content?.[0]?.text || '', /cancel/i);
273
- });
274
-
275
- test('explore provider warmup is canceled immediately when the caller aborts mid-init', async () => {
276
- const ctrl = new AbortController();
277
- const ready = new Promise(() => {}); // warmup wedged (never resolves)
278
- const t0 = Date.now();
279
- const p = awaitExploreProviderReadyOrCancel(ready, ctrl.signal);
280
- ctrl.abort(new Error('ESC during warmup'));
281
- const canceled = await p;
282
- assert.equal(canceled, true, 'reported canceled without waiting for warmup');
283
- assert.ok(Date.now() - t0 < 500, 'returned promptly, did not block on warmup');
284
- });
285
-
286
- test('explore provider warmup aborts the real pending initializer work', async () => {
287
- const ctrl = new AbortController();
288
- let initializerAborted = false;
289
- const pendingInit = (_providers, { signal }) => new Promise((_resolve, reject) => {
290
- signal.addEventListener('abort', () => {
291
- initializerAborted = true;
292
- reject(signal.reason);
293
- }, { once: true });
294
- });
295
- const ready = ensureExploreProviderReady({ providers: { fake: { enabled: true } } }, { provider: 'fake' }, ctrl.signal, pendingInit);
296
- const waited = awaitExploreProviderReadyOrCancel(ready, ctrl.signal);
297
- ctrl.abort(new Error('ESC during provider init'));
298
- assert.equal(await waited, true);
299
- assert.equal(initializerAborted, true, 'AbortSignal reached the pending initializer');
300
- });
301
-
302
- test('explore provider warmup resolves to not-canceled when the caller stays active', async () => {
303
- const canceled = await awaitExploreProviderReadyOrCancel(Promise.resolve(), new AbortController().signal);
304
- assert.equal(canceled, false, 'ready to dispatch');
305
- });
306
-
307
- test('explore provider warmup short-circuits an already-aborted caller before awaiting init', async () => {
308
- const ctrl = new AbortController();
309
- ctrl.abort(new Error('already canceled'));
310
- let awaited = false;
311
- const ready = Promise.resolve().then(() => { awaited = true; });
312
- const canceled = await awaitExploreProviderReadyOrCancel(ready, ctrl.signal);
313
- assert.equal(canceled, true, 'canceled without blocking on the warmup promise');
314
- });
315
-
316
- test('explore provider warmup propagates a genuine init failure (not a cancel)', async () => {
317
- const ctrl = new AbortController(); // never aborted
318
- const ready = Promise.reject(new Error('provider init failed'));
319
- await assert.rejects(awaitExploreProviderReadyOrCancel(ready, ctrl.signal), /init failed/);
320
- });
321
-
322
- test('a retired shared compute success write is identity-guarded (no stale overwrite)', async () => {
323
- process.env.MIXDOG_EXPLORE_RESULT_CACHE = '1';
324
- const cache = __exploreResultCacheForTest();
325
- cache.clear();
326
- const key = 'identity-guard-success';
327
- const dc = deferredCompute();
328
- const p = runExploreCached(key, dc.fn, null);
329
- p.catch(() => {});
330
- // The compute executor runs on a microtask; let it install dc.resolve.
331
- await new Promise((r) => setTimeout(r, 0));
332
- const oldEntry = cache.get(key);
333
- assert.ok(oldEntry?.promise, 'pending entry stored');
334
- assert.equal(typeof dc.resolve, 'function', 'compute executor ran');
335
- // Simulate the entry having been retired + replaced by a fresh one (e.g. a
336
- // TTL eviction/recompute) WITHOUT touching the old in-flight compute.
337
- const freshEntry = { ts: Date.now(), value: undefined, sentinel: true };
338
- cache.set(key, freshEntry);
339
- // The OLD compute now resolves successfully — its success handler must NOT
340
- // overwrite the fresh entry (identity guard on every eventual write).
341
- dc.resolve('src/stale.mjs:1 — retired winner');
342
- await p;
343
- await new Promise((r) => setTimeout(r, 0)); // flush the success microtask
344
- assert.equal(cache.get(key), freshEntry, 'retired success write was identity-guarded');
345
- });