mixdog 1.2.3 → 1.2.5

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 (469) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/mobile-release.mjs +5 -4
  5. package/scripts/probe-provider-catalog.mjs +31 -0
  6. package/scripts/provider-pingpong-live.mjs +128 -38
  7. package/scripts/session-sweep.mjs +5 -25
  8. package/src/agents/maintainer/AGENT.md +1 -1
  9. package/src/agents/maintainer/agent.json +1 -1
  10. package/src/agents/reviewer/AGENT.md +2 -4
  11. package/src/agents/security/AGENT.md +1 -2
  12. package/src/agents/writer/AGENT.md +2 -3
  13. package/src/defaults/shipped-history.json +7 -1
  14. package/src/defaults/skills/image/SKILL.md +21 -23
  15. package/src/lib/keychain-cjs.cjs +13 -11
  16. package/src/output-styles/common.md +2 -1
  17. package/src/rules/agent/AGENT.md +7 -5
  18. package/src/rules/lead/LEAD.md +13 -13
  19. package/src/rules/shared/45-destructive.md +4 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  23. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  24. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  25. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  31. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  34. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  35. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  36. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  38. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  41. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  46. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  48. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  50. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  51. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  52. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  53. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  54. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  56. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  57. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  59. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  60. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  61. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  62. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  64. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  65. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  66. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  67. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  68. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  69. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  70. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  80. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  82. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  86. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  87. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  88. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  89. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  90. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  91. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  92. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  93. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  96. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  97. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  98. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  99. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  100. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  101. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  103. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  109. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  110. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  111. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  112. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  113. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  114. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  115. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  116. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  117. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  118. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  119. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  120. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  121. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  123. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  124. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  125. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  128. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  129. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  131. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  132. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  133. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  134. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  135. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  141. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  146. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  147. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  148. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  149. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  150. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  151. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  155. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  157. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  158. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  161. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  162. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  163. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  164. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  165. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  167. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  169. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  173. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  174. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  178. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  179. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  180. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  181. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +160 -212
  182. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  183. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  184. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  185. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  186. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  187. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  188. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  189. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  190. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  191. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  192. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  193. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  194. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  197. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  199. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  200. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  208. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  209. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  210. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  211. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  212. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  216. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  217. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  218. package/src/runtime/attachments/store.d.mts +9 -0
  219. package/src/runtime/attachments/store.mjs +80 -9
  220. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  221. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  222. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  223. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  224. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  225. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  226. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  227. package/src/runtime/github/tool.mjs +118 -17
  228. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  229. package/src/runtime/media/catalog.mjs +4 -4
  230. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  231. package/src/runtime/media/defaults.mjs +37 -0
  232. package/src/runtime/media/jobs.mjs +12 -2
  233. package/src/runtime/media/lanes.mjs +12 -0
  234. package/src/runtime/media/tool-defs.mjs +15 -4
  235. package/src/runtime/media/tool.mjs +41 -12
  236. package/src/runtime/media/upstream-error.mjs +72 -6
  237. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  238. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  240. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  242. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  243. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  244. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  245. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  246. package/src/runtime/office/index.mjs +2 -3
  247. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  248. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  249. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  250. package/src/runtime/shared/atomic-file.d.mts +1 -1
  251. package/src/runtime/shared/background-tasks.mjs +6 -1
  252. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  253. package/src/runtime/shared/browser-launch.mjs +15 -0
  254. package/src/runtime/shared/err-text.mjs +42 -4
  255. package/src/runtime/shared/file-lock.mjs +6 -5
  256. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  257. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  258. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  259. package/src/runtime/shared/model-route-chain.mjs +48 -0
  260. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  261. package/src/runtime/shared/pid-liveness.mjs +12 -0
  262. package/src/runtime/shared/provider-api-key.mjs +6 -0
  263. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  264. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  265. package/src/runtime/shared/quiet-progress.mjs +101 -0
  266. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  267. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  268. package/src/runtime/shared/remote-intent.mjs +2 -42
  269. package/src/runtime/shared/slow-span.mjs +8 -1
  270. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  271. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  272. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  273. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  274. package/src/runtime/shared/tool-status.mjs +3 -2
  275. package/src/runtime/shared/tool-surface.mjs +2 -3
  276. package/src/runtime/shared/tool-work-units.mjs +22 -15
  277. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  278. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  279. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  280. package/src/runtime/shared/transcript-writer.mjs +14 -4
  281. package/src/runtime/shared/turn-changes.mjs +2 -1
  282. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  283. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  284. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  285. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  286. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  287. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  288. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  289. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  290. package/src/runtime/shared/worktrees/include.mjs +125 -7
  291. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  292. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  293. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  294. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  295. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  296. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  297. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  298. package/src/runtime/terminal-bridge/client.mjs +14 -10
  299. package/src/session-runtime/boot/apis.mjs +7 -1
  300. package/src/session-runtime/boot/config.mjs +2 -0
  301. package/src/session-runtime/boot/facade.mjs +3 -0
  302. package/src/session-runtime/boot/session.mjs +3 -2
  303. package/src/session-runtime/boot/tools.mjs +3 -0
  304. package/src/session-runtime/conversation-import/service.mjs +1 -1
  305. package/src/session-runtime/goal-facade-api.mjs +2 -2
  306. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  307. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  308. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  309. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  310. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  311. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  312. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  313. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  314. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  315. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  316. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  317. package/src/session-runtime/media-api.mjs +8 -0
  318. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  319. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  320. package/src/session-runtime/model-route/set-route.mjs +114 -34
  321. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  322. package/src/session-runtime/model-route-api.mjs +4 -2
  323. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  324. package/src/session-runtime/native-web-search.mjs +9 -8
  325. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  326. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  327. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  328. package/src/session-runtime/provider-models.mjs +1 -0
  329. package/src/session-runtime/provider-usage.mjs +4 -0
  330. package/src/session-runtime/route-resolution.mjs +3 -1
  331. package/src/session-runtime/runtime-core.mjs +4 -0
  332. package/src/session-runtime/runtime-facade.mjs +6 -0
  333. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  334. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  335. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  336. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  337. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  338. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  339. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  340. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  341. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  343. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  344. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  345. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  346. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  347. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  348. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  349. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  350. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  351. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  352. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  353. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  354. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  355. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  356. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  357. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  358. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  359. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  360. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  361. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  362. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  363. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  364. package/src/session-runtime/services/agent-tool.mjs +4 -0
  365. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  366. package/src/session-runtime/services/provider-admin.mjs +77 -0
  367. package/src/session-runtime/session-create/session-options.mjs +7 -5
  368. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  369. package/src/session-runtime/session-transcript.mjs +4 -5
  370. package/src/session-runtime/session-turn-api.mjs +3 -9
  371. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  372. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  373. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  374. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  375. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  376. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  377. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  378. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  379. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  380. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  381. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  382. package/src/session-runtime/turn/turn-run.mjs +1 -36
  383. package/src/session-runtime/usage-stats-api.mjs +4 -1
  384. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  385. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  386. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  387. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  388. package/src/session-runtime/workspace/transitions.mjs +248 -68
  389. package/src/standalone/channel-transport.mjs +3 -4
  390. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  391. package/src/standalone/daemon-pg-stop.mjs +1 -3
  392. package/src/standalone/daemon-session-control.mjs +88 -0
  393. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  394. package/src/standalone/daemon.mjs +2 -0
  395. package/src/standalone/desktop-service-registry.mjs +32 -5
  396. package/src/standalone/local-session-runtime.mjs +11 -12
  397. package/src/standalone/session-protocol.mjs +7 -0
  398. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  399. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  400. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  401. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  402. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  403. package/src/standalone/session-service/agent-tree.mjs +28 -2
  404. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  405. package/src/standalone/session-service/entries.mjs +1 -0
  406. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  407. package/src/standalone/session-service/quota-resume.mjs +28 -13
  408. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  409. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  410. package/src/standalone/session-tool-output.mjs +82 -0
  411. package/src/tui/app/app-view.jsx +2 -1
  412. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  413. package/src/tui/app/provider-setup-picker.mjs +15 -3
  414. package/src/tui/app/slash-commands.mjs +6 -0
  415. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  416. package/src/tui/components/Spinner.jsx +4 -0
  417. package/src/tui/components/SpinnerByline.jsx +21 -2
  418. package/src/tui/components/status-line/local-line.mjs +10 -2
  419. package/src/tui/dist/index.mjs +529 -270
  420. package/src/tui/session/active-tool-summary.mjs +1 -1
  421. package/src/tui/session/context-state.mjs +0 -3
  422. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  423. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  424. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  425. package/src/tui/session/quota-resume.mjs +140 -61
  426. package/src/tui/session/session-api/commands.mjs +4 -10
  427. package/src/tui/session/session-api/controls.mjs +12 -0
  428. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  429. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  430. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  431. package/src/tui/session/session-api/media.mjs +2 -0
  432. package/src/tui/session/session-api/routes.mjs +81 -57
  433. package/src/tui/session/session-api/settings.mjs +3 -3
  434. package/src/tui/session/session-api/worktree.mjs +3 -2
  435. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  436. package/src/tui/session/session-flow/drain.mjs +6 -4
  437. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  438. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  439. package/src/tui/session/session-flow/queue.mjs +1 -1
  440. package/src/tui/session/tool-approval.mjs +17 -8
  441. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  442. package/src/tui/session/tui-steering-persist.mjs +3 -5
  443. package/src/tui/session/turn-quiet.mjs +53 -0
  444. package/src/tui/session/turn-stream.mjs +5 -1
  445. package/src/tui/session/turn.mjs +38 -31
  446. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  447. package/src/tui/spinner-meta.mjs +10 -0
  448. package/src/tui/spinner-verbs.mjs +3 -0
  449. package/src/ui/statusline.mjs +13 -3
  450. package/src/ui/streaming-markdown-heal.mjs +70 -18
  451. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  452. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  453. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  454. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  455. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  456. package/src/session-runtime/turn/share-ops.mjs +0 -77
  457. package/src/tui/session/cross-surface-share.mjs +0 -406
  458. package/src/tui/session/live-share/delta.mjs +0 -153
  459. package/src/tui/session/live-share/mirror.mjs +0 -106
  460. package/src/tui/session/live-share/owner.mjs +0 -254
  461. package/src/tui/session/live-share/retry.mjs +0 -54
  462. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  463. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  464. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  465. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  466. package/src/tui/session/live-share/viewer.mjs +0 -99
  467. package/src/tui/session/live-share/wire.mjs +0 -44
  468. package/src/tui/session/live-share.mjs +0 -154
  469. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -75,10 +75,11 @@ function parseAnchor(line, { workDir, rgSpawnCwd, grepResolvedPath, searchPath,
75
75
  content: String(line).slice(split.markerEnd),
76
76
  };
77
77
  }
78
- const raw = splitGrepLinePrefix(line);
78
+ // The anchor pass runs rg without context lines: every line is a match.
79
+ const raw = splitGrepLinePrefix(line, true);
79
80
  if (raw?.delimiter !== ':') return null;
80
- const normalized = relativeGrepLine(line, workDir, false, outputMode, false);
81
- const display = splitGrepLinePrefix(normalized);
81
+ const normalized = relativeGrepLine(line, workDir, false, outputMode, false, true);
82
+ const display = splitGrepLinePrefix(normalized, true);
82
83
  if (display?.delimiter !== ':') return null;
83
84
  return {
84
85
  path: display.path,
@@ -301,8 +301,9 @@ export function formatGrepOutput({
301
301
  includeMatchCount = false,
302
302
  }) {
303
303
  const lines = headLimit === Infinity ? windowed : windowed.slice(0, headLimit);
304
+ const matchOnly = outputMode === 'content' && !(beforeN > 0 || afterN > 0 || contextN > 0);
304
305
  const normalized = lines.map((line) =>
305
- relativeGrepLine(line, workDir, outputMode === 'files_with_matches', outputMode, filenameOmitted)
306
+ relativeGrepLine(line, workDir, outputMode === 'files_with_matches', outputMode, filenameOmitted, matchOnly)
306
307
  );
307
308
  const remaining = Math.max(0, totalWindowed - lines.length);
308
309
  const shown = lines.length;
@@ -82,23 +82,25 @@ function combinedPathRequest(args, defaultHeadLimit) {
82
82
  // order; null when one is a glob, unreachable, or on a share — the legacy
83
83
  // fan-out owns those diagnostics.
84
84
  async function resolvePathRoots(list, workDir) {
85
- const roots = await Promise.all(list.map(async (p) => {
86
- const cleaned = normalizeInputPath(p);
87
- if (hasGlobMagic(cleaned)) return null;
88
- const resolved = resolveSearchScope(cleaned, workDir);
89
- if (isUncOrSmbPath(cleaned) || isUncOrSmbPath(resolved)) return null;
90
- let st;
91
- try {
92
- st = await statReachable(resolved);
93
- } catch {
94
- return null;
95
- }
96
- return {
97
- arg: p,
98
- abs: normalizeOutputPath(isAbsolute(resolved) ? await trueCasePath(resolved) : resolved),
99
- isDir: st.isDirectory(),
100
- };
101
- }));
85
+ const roots = await Promise.all(
86
+ list.map(async (p) => {
87
+ const cleaned = normalizeInputPath(p);
88
+ if (hasGlobMagic(cleaned)) return null;
89
+ const resolved = resolveSearchScope(cleaned, workDir);
90
+ if (isUncOrSmbPath(cleaned) || isUncOrSmbPath(resolved)) return null;
91
+ let st;
92
+ try {
93
+ st = await statReachable(resolved);
94
+ } catch {
95
+ return null;
96
+ }
97
+ return {
98
+ arg: p,
99
+ abs: normalizeOutputPath(isAbsolute(resolved) ? await trueCasePath(resolved) : resolved),
100
+ isDir: st.isDirectory(),
101
+ };
102
+ })
103
+ );
102
104
  if (roots.some((root) => !root)) return null;
103
105
  // An overlapping operand would scan a file twice and then attribute both
104
106
  // copies to each containing section. Keep independent scope semantics.
@@ -123,7 +125,7 @@ function attributeLinesToRoots(lines, roots, wdFwd) {
123
125
  for (const line of lines) {
124
126
  const norm = line.replace(/\\/g, '/');
125
127
  const lineKey = pathKey(norm);
126
- const rel = lineKey.startsWith(workDirKey) ? norm.slice(wdFwd.length + 1) : line;
128
+ const rel = lineKey.startsWith(workDirKey) ? line.slice(wdFwd.length + 1) : line;
127
129
  for (let i = 0; i < roots.length; i++) {
128
130
  const rootKey = pathKey(roots[i].abs);
129
131
  if (lineKey === rootKey || lineKey.startsWith(`${rootKey}/`) || lineKey.startsWith(`${rootKey}:`)) {
@@ -143,7 +145,7 @@ function renderedRootLines(root, windowedLines, wdFwd) {
143
145
  const rootPrefix = `${pathKey(rootRel)}:`;
144
146
  return windowedLines.map((l) => {
145
147
  const lineFwd = l.replace(/\\/g, '/');
146
- return pathKey(lineFwd).startsWith(rootPrefix) ? lineFwd.slice(rootRel.length + 1) : l;
148
+ return pathKey(lineFwd).startsWith(rootPrefix) ? l.slice(rootRel.length + 1) : l;
147
149
  });
148
150
  }
149
151
 
@@ -151,13 +153,13 @@ async function combinedPathBody(request, root, linesFor, { workDir, wdFwd, optio
151
153
  const { pattern, outMode, headLimit, offset, contextN, autoContext, caseInsensitive } = request;
152
154
  if (outMode === 'content' && (autoContext || contextN > 0)) {
153
155
  const ctx = await expandGrepAnchorContextOutput({
154
- allLines: linesFor,
156
+ allLines: renderedRootLines(root, linesFor, wdFwd),
155
157
  workDir,
156
158
  rgSpawnCwd: wdFwd,
157
159
  grepResolvedPath: root.abs,
158
160
  searchPath: root.arg,
159
161
  outputMode: outMode,
160
- filenameOmitted: false,
162
+ filenameOmitted: !root.isDir,
161
163
  headLimit,
162
164
  offset,
163
165
  totalKnown: true,
@@ -229,29 +231,31 @@ async function runCombinedPathFanout({ args, list, workDir, options, callContext
229
231
 
230
232
  // One rg invocation over every root's files at once.
231
233
  async function combinedPathRgArgs(args, request, roots, workDir) {
232
- const perRoot = await Promise.all(roots.map(async (root) => {
233
- const scope = await resolveGrepScope({ ...args, path: root.arg }, workDir);
234
- if (scope.result !== undefined) return null;
235
- return buildGrepRgArgs({
236
- patterns: [request.pattern],
237
- includeNoise: args.include_noise === true,
238
- text: args.text === true,
239
- searchPath: isAbsolute(scope.searchPath) ? root.abs : scope.searchPath,
240
- // Match buildGrepScope: an explicit file operand wins over its glob.
241
- globPatterns: root.isDir ? scope.normalizedGlobPatterns : [],
242
- outputMode: request.outMode,
243
- caseInsensitive: request.caseInsensitive,
244
- showLineNumbers: true,
245
- beforeN: null,
246
- afterN: null,
247
- contextN: null,
248
- multilineMode: false,
249
- fileType: '',
250
- onlyMatching: false,
251
- pcre2: false,
252
- withFilename: true,
253
- }).slice(0, -1);
254
- }));
234
+ const perRoot = await Promise.all(
235
+ roots.map(async (root) => {
236
+ const scope = await resolveGrepScope({ ...args, path: root.arg }, workDir);
237
+ if (scope.result !== undefined) return null;
238
+ return buildGrepRgArgs({
239
+ patterns: [request.pattern],
240
+ includeNoise: args.include_noise === true,
241
+ text: args.text === true,
242
+ searchPath: isAbsolute(scope.searchPath) ? root.abs : scope.searchPath,
243
+ // Match buildGrepScope: an explicit file operand wins over its glob.
244
+ globPatterns: root.isDir ? scope.normalizedGlobPatterns : [],
245
+ outputMode: request.outMode,
246
+ caseInsensitive: request.caseInsensitive,
247
+ showLineNumbers: true,
248
+ beforeN: null,
249
+ afterN: null,
250
+ contextN: null,
251
+ multilineMode: false,
252
+ fileType: '',
253
+ onlyMatching: false,
254
+ pcre2: false,
255
+ withFilename: true,
256
+ }).slice(0, -1);
257
+ })
258
+ );
255
259
  if (perRoot.some((argv) => !argv)) return null;
256
260
  const first = JSON.stringify(perRoot[0]);
257
261
  // Native filters are rooted at each operand. Only identical normalized
@@ -3,12 +3,19 @@ import { canonicalizeGlobSlashes } from '../path-utils.mjs';
3
3
  import { relativePathPrefix } from '../search-path-diagnostics.mjs';
4
4
  import { normalizeGrepLine, splitGrepCountPrefix, splitGrepLinePrefix } from '../grep-formatting.mjs';
5
5
 
6
- export function relativeGrepLine(line, workDir, pathOnly = false, outputMode = 'content', filenameOmitted = false) {
7
- const normalized = normalizeGrepLine(line, pathOnly, outputMode, filenameOmitted);
6
+ export function relativeGrepLine(
7
+ line,
8
+ workDir,
9
+ pathOnly = false,
10
+ outputMode = 'content',
11
+ filenameOmitted = false,
12
+ matchOnly = false
13
+ ) {
14
+ const normalized = normalizeGrepLine(line, pathOnly, outputMode, filenameOmitted, matchOnly);
8
15
  if (!workDir) return normalized;
9
16
  if (pathOnly) return relativePathPrefix(normalized, workDir);
10
17
  if (filenameOmitted) return normalized;
11
- const split = splitGrepLinePrefix(normalized);
18
+ const split = splitGrepLinePrefix(normalized, matchOnly);
12
19
  if (split) {
13
20
  return relativePathPrefix(normalized.slice(0, split.pathEnd), workDir) + normalized.slice(split.pathEnd);
14
21
  }
@@ -21,11 +21,31 @@
21
21
  * partial `.json`, and a marker never exists without its `.json`.
22
22
  */
23
23
  import { access, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
24
+ import { unlinkSync } from 'node:fs';
24
25
  import { homedir } from 'node:os';
25
26
  import { join } from 'node:path';
26
27
  import { writeJsonAtomicAsync } from '../../../../../shared/atomic-file.mjs';
27
28
  import { isPidAlive } from '../../../../../shared/pid-liveness.mjs';
28
29
  import { displayShellCommand } from '../../../../../shared/shell-display.mjs';
30
+ import { tryAdmitWorkspaceActivity } from '../../../../../shared/worktrees/activity-gate.mjs';
31
+ import { worktreeError } from '../../../../../shared/worktrees/contract.mjs';
32
+ import { workspaceSees } from '../../../../../shared/worktrees/workspace-visibility.mjs';
33
+
34
+ const workspaceLeases = new Map();
35
+
36
+ /** Transfer a foreground reader to a surviving-descendant task without a lease gap. */
37
+ export function adoptShellWorkspaceAdmission(jobId, leave) {
38
+ const prior = workspaceLeases.get(jobId);
39
+ workspaceLeases.set(
40
+ jobId,
41
+ prior
42
+ ? () => {
43
+ prior();
44
+ leave();
45
+ }
46
+ : leave
47
+ );
48
+ }
29
49
 
30
50
  // A record whose owner process is gone is garbage. Age alone never deletes a
31
51
  // live owner's job, so an unlimited long-running shell keeps its record.
@@ -155,10 +175,26 @@ async function sweepOrphanRecords(dir) {
155
175
  /** Publish the record for a job that has just started or been promoted. */
156
176
  export function publishShellJobRecord(
157
177
  task,
158
- { ownerSessionId = null, clientHostPid = null, startedAtMs = 0, foreground = false } = {}
178
+ {
179
+ ownerSessionId = null,
180
+ clientHostPid = null,
181
+ startedAtMs = 0,
182
+ foreground = false,
183
+ terminationUnconfirmed = false,
184
+ } = {}
159
185
  ) {
160
186
  const jobId = String(task?.jobId || '').trim();
161
187
  if (!jobId) return;
188
+ if (task?.cwd) {
189
+ const leave = tryAdmitWorkspaceActivity(ownerSessionId || jobId, task.cwd, {
190
+ pid: Number(task.pid) || process.pid,
191
+ kind: 'command',
192
+ });
193
+ if (!leave) throw worktreeError('WORKTREE_BUSY_JOBS', 'The command folder is being managed.');
194
+ // A descendant handoff can reuse the job id with a different live pid.
195
+ // Acquire its reader before releasing any predecessor's admission.
196
+ adoptShellWorkspaceAdmission(jobId, leave);
197
+ }
162
198
  const ownerPid = Number(clientHostPid ?? task?.clientHostPid) || process.pid;
163
199
  const dir = jobsDir();
164
200
  const jsonPath = join(dir, `${jobId}.json`);
@@ -192,6 +228,12 @@ export function publishShellJobRecord(
192
228
  // removed on settle. Restart recovery skips them: there is no
193
229
  // task id to report and no waiter to notify.
194
230
  ...(foreground ? { foreground: true } : {}),
231
+ // A killed command whose processes are not confirmed gone: its
232
+ // result is reported, but the readouts keep listing it.
233
+ ...(terminationUnconfirmed ? { terminationUnconfirmed: true } : {}),
234
+ // Its owner session closed: any session that sees the folder lists
235
+ // (and may release) it.
236
+ ...(closedOwnerSessions.has(String(ownerSessionId || '')) ? { ownerSessionClosed: true } : {}),
195
237
  };
196
238
  records.set(jobId, record);
197
239
  incarnations.delete(jobId);
@@ -205,26 +247,146 @@ export function publishShellJobRecord(
205
247
  }
206
248
 
207
249
  /** Preserve a bounded terminal snapshot for manual recovery after restart. */
250
+ // jobId -> { ownerSessionId, release } for every job whose result is reported
251
+ // but whose processes are not confirmed gone. The hold is dropped by the
252
+ // termination watch on confirmation, or by an explicit user force-release —
253
+ // never by a timer.
254
+ const terminationHolds = new Map();
255
+ // Owner sessions that closed in this runtime: their holds have no owner left
256
+ // to release them, so any session that sees the workspace may.
257
+ const closedOwnerSessions = new Set();
258
+
259
+ export function registerTerminationHold(jobId, { ownerSessionId = null, cwd = null, release }) {
260
+ const key = String(jobId || '').trim();
261
+ if (key && typeof release === 'function') terminationHolds.set(key, { ownerSessionId, cwd, release });
262
+ }
263
+
264
+ export function clearTerminationHold(jobId) {
265
+ terminationHolds.delete(String(jobId || '').trim());
266
+ }
267
+
268
+ /** The owner session closed: its live records are flagged so every surface
269
+ * can list them for the sessions that see their folder. */
270
+ export function noteShellOwnerSessionClosed(sessionId) {
271
+ const id = String(sessionId || '').trim();
272
+ if (!id) return;
273
+ closedOwnerSessions.add(id);
274
+ for (const [jobId, record] of records) {
275
+ if (record?.ownerSessionId === id) void patchRunningShellJobRecord(jobId, { ownerSessionClosed: true });
276
+ }
277
+ }
278
+
279
+ const releaseRequestPath = (key) => join(jobsDir(), `${key}.release-requested`);
280
+
281
+ async function requestRemoteRelease(key, caller) {
282
+ const id = validJobId(key);
283
+ const record = id ? JSON.parse(await readFile(join(jobsDir(), `${id}.json`), 'utf8')) : null;
284
+ if (record?.status !== 'running' || record.terminationUnconfirmed !== true) throw new Error('not held');
285
+ const hold = { ownerSessionId: record.ownerSessionId || null, cwd: record.cwd };
286
+ if (!mayRelease(hold, { ...caller, ownerClosed: record.ownerSessionClosed === true })) {
287
+ throw new Error('not permitted');
288
+ }
289
+ await writeFile(releaseRequestPath(id), '', 'utf8');
290
+ return { released: true, deferred: true };
291
+ }
292
+
293
+ /** Consume a cross-process release request for `jobId`; true when one existed. */
294
+ export function takeReleaseRequest(jobId) {
295
+ const key = validJobId(jobId);
296
+ if (!key) return false;
297
+ try {
298
+ unlinkSync(releaseRequestPath(key));
299
+ return true;
300
+ } catch {
301
+ return false;
302
+ }
303
+ }
304
+
305
+ function mayRelease(hold, { callerSessionId, callerCwd, ownerClosed = closedOwnerSessions.has(hold.ownerSessionId) }) {
306
+ if (!hold.ownerSessionId || hold.ownerSessionId === callerSessionId) return true;
307
+ return ownerClosed && workspaceSees(hold.cwd, callerCwd);
308
+ }
309
+
310
+ /** Explicit user action ("Release anyway"): drop the workspace hold of a job
311
+ * whose termination is unconfirmed. The surface asks for confirmation first.
312
+ * The owning session may release it; once that session has closed, any
313
+ * session whose workspace (`callerCwd`) sees the hold's folder may. */
314
+ export async function forceReleaseTerminationHold(jobId, { callerSessionId = null, callerCwd = null } = {}) {
315
+ const key = String(jobId || '').trim();
316
+ const refused = { released: false, error: `no unconfirmed termination hold: ${key}` };
317
+ const hold = terminationHolds.get(key);
318
+ // The hold lives in another runtime process (a desktop pane's session shard):
319
+ // its own observation loop honours the request at its next pass.
320
+ if (!hold) return requestRemoteRelease(key, { callerSessionId, callerCwd }).catch(() => refused);
321
+ if (!mayRelease(hold, { callerSessionId, callerCwd })) return refused;
322
+ try {
323
+ await hold.release();
324
+ } catch (error) {
325
+ return { released: false, error: error instanceof Error ? error.message : String(error) };
326
+ }
327
+ if (terminationHolds.get(key) === hold) terminationHolds.delete(key);
328
+ return { released: true };
329
+ }
330
+
331
+ /** Release the workspace admission adopted under `jobId` without a record of
332
+ * its own (survivors a finished foreground command left behind), once they
333
+ * are confirmed gone. */
334
+ export function releaseShellJobWorkspace(jobId) {
335
+ const key = String(jobId || '').trim();
336
+ const leave = workspaceLeases.get(key);
337
+ if (!leave) return;
338
+ leave();
339
+ if (workspaceLeases.get(key) === leave) workspaceLeases.delete(key);
340
+ }
341
+
342
+ /** Flag a still-open job record as "termination unconfirmed" for the readouts. */
343
+ export function markShellJobTerminationUnconfirmed(jobId) {
344
+ return patchRunningShellJobRecord(jobId, { terminationUnconfirmed: true });
345
+ }
346
+
347
+ function patchRunningShellJobRecord(jobId, patch) {
348
+ const key = validJobId(jobId);
349
+ if (!key) return Promise.resolve(false);
350
+ const jsonPath = join(jobsDir(), `${key}.json`);
351
+ return enqueue(key, async () => {
352
+ const record = records.get(key);
353
+ if (record?.status !== 'running') return false;
354
+ const next = { ...record, ...patch };
355
+ records.set(key, next);
356
+ await writeJsonAtomicAsync(jsonPath, next, { fsync: false });
357
+ return true;
358
+ }).catch(() => false);
359
+ }
360
+
208
361
  export function completeShellJobRecord(jobId, detail) {
209
362
  const key = validJobId(jobId);
210
363
  if (!key) return Promise.resolve(false);
364
+ const leaveWorkspace = workspaceLeases.get(key);
211
365
  const dir = jobsDir();
212
366
  const jsonPath = join(dir, `${key}.json`);
213
367
  const donePath = join(dir, `${key}.done`);
214
368
  const list = paths.get(key) || [jsonPath];
215
369
  const ownerPaths = list.filter((path) => path.includes('.owner-'));
216
370
  return enqueue(key, async () => {
217
- await mkdir(dir, { recursive: true });
218
- let base = records.get(key) || null;
219
- if (!base) {
220
- try {
221
- base = JSON.parse(await readFile(jsonPath, 'utf8'));
222
- } catch {}
371
+ try {
372
+ await mkdir(dir, { recursive: true });
373
+ let base = records.get(key) || null;
374
+ if (!base) {
375
+ try {
376
+ base = JSON.parse(await readFile(jsonPath, 'utf8'));
377
+ } catch {}
378
+ }
379
+ const record = terminalRecord(base, { ...detail, jobId: key });
380
+ records.set(key, record);
381
+ await writeJsonAtomicAsync(jsonPath, record, { fsync: false });
382
+ await writeFile(donePath, '', 'utf8');
383
+ } finally {
384
+ // Callers complete a record only once its processes are confirmed gone
385
+ // (or explicitly released): the workspace admission follows that, not
386
+ // the record write, so a failed write cannot strand it with no handle.
387
+ leaveWorkspace?.();
388
+ if (workspaceLeases.get(key) === leaveWorkspace) workspaceLeases.delete(key);
223
389
  }
224
- const record = terminalRecord(base, { ...detail, jobId: key });
225
- records.set(key, record);
226
- await writeJsonAtomicAsync(jsonPath, record, { fsync: false });
227
- await writeFile(donePath, '', 'utf8');
228
390
  await removePaths(ownerPaths);
229
391
  paths.set(key, [jsonPath, donePath]);
230
392
  const incarnation = ++incarnationSeq;
@@ -245,33 +407,51 @@ export function completeShellJobRecord(jobId, detail) {
245
407
  }).catch(() => false);
246
408
  }
247
409
 
248
- export async function readShellJobRecord(jobId) {
410
+ /** Test support: settles once every record write queued so far for `jobId`
411
+ * has finished, so a test waits on the write itself, not on elapsed turns. */
412
+ export function _shellJobRecordWritesSettledForTest(jobId) {
413
+ return chains.get(String(jobId || '').trim()) || Promise.resolve();
414
+ }
415
+
416
+ export async function readShellJobRecord(jobId, { strict = false } = {}) {
249
417
  const key = validJobId(jobId);
250
418
  if (!key) return null;
251
419
  const dir = jobsDir();
252
420
  const jsonPath = join(dir, `${key}.json`);
253
421
  try {
254
422
  const record = JSON.parse(await readFile(jsonPath, 'utf8'));
423
+ if (
424
+ strict &&
425
+ (!record ||
426
+ !['running', 'completed', 'failed', 'cancelled'].includes(record.status) ||
427
+ (record.status === 'running' && !record.cwd))
428
+ )
429
+ throw new Error(`Cannot inspect shell job ${key}.`);
255
430
  const done = await access(join(dir, `${key}.done`)).then(
256
431
  () => true,
257
- () => false
432
+ (error) => {
433
+ if (strict && error?.code !== 'ENOENT') throw error;
434
+ return false;
435
+ }
258
436
  );
259
437
  return { ...record, recovered: true, terminal: done || record?.status !== 'running' };
260
- } catch {
438
+ } catch (error) {
439
+ if (strict && error?.code !== 'ENOENT') throw error;
261
440
  return null;
262
441
  }
263
442
  }
264
443
 
265
- export async function listShellJobRecords() {
444
+ export async function listShellJobRecords({ strict = false } = {}) {
266
445
  const dir = jobsDir();
267
446
  let names;
268
447
  try {
269
448
  names = await readdir(dir);
270
- } catch {
449
+ } catch (error) {
450
+ if (strict && error?.code !== 'ENOENT') throw error;
271
451
  return [];
272
452
  }
273
453
  const rows = await Promise.all(
274
- names.filter((name) => name.endsWith('.json')).map((name) => readShellJobRecord(name.slice(0, -5)))
454
+ names.filter((name) => name.endsWith('.json')).map((name) => readShellJobRecord(name.slice(0, -5), { strict }))
275
455
  );
276
456
  const cutoff = Date.now() - COMPLETED_SHELL_JOB_TTL_MS;
277
457
  return rows.filter((record) => {
@@ -286,6 +466,8 @@ export async function listShellJobRecords() {
286
466
  export function retireShellJobRecord(jobId) {
287
467
  const key = String(jobId || '').trim();
288
468
  if (!key) return;
469
+ const leaveWorkspace = workspaceLeases.get(key);
470
+ workspaceLeases.delete(key);
289
471
  const list = paths.get(key);
290
472
  paths.delete(key);
291
473
  // Queued behind every earlier publish and ahead of any later one: a promoted
@@ -294,5 +476,6 @@ export function retireShellJobRecord(jobId) {
294
476
  records.delete(key);
295
477
  incarnations.delete(key);
296
478
  await removePaths(list);
479
+ leaveWorkspace?.();
297
480
  });
298
481
  }
@@ -2,9 +2,10 @@ import { closeSync, fstatSync, openSync, readSync } from 'node:fs';
2
2
  import { stripAnsi } from '../../shell-exec-output.mjs';
3
3
  import { FileEncodingSniffer, ShellStreamDecoder, alignedOffset, oemTextEncoding } from '../../shell-codepage.mjs';
4
4
 
5
- const PAGE_BYTES = 8 * 1024;
6
- const COMPLETION_BYTES = 12 * 1024;
5
+ import { TOOL_OUTPUT_PAGE_BYTES, TOOL_OUTPUT_TAIL_BYTES } from '../../../../../shared/tool-output-budget.mjs';
7
6
  const cursors = new WeakMap();
7
+ const ESC = '\x1b';
8
+ const PENDING_ANSI_INTRODUCER = new RegExp(`${ESC}(?:\\[[0-?]*[ -/]*|\\](?:(?!${ESC}\\\\)[^\x07\x9c])*)?$`);
8
9
 
9
10
  function newCursor() {
10
11
  return {
@@ -36,7 +37,7 @@ function visibleText(cursor, text) {
36
37
  const value = cursor.ansi + text;
37
38
  // Retain only the introducer of an unfinished CSI/OSC, not its potentially
38
39
  // unbounded payload. It will be stripped with the following chunk.
39
- const pending = /\x1b(?:\[[0-?]*[ -/]*|\](?:(?!\x1b\\)[^\x07\x9c])*)?$/.exec(value);
40
+ const pending = PENDING_ANSI_INTRODUCER.exec(value);
40
41
  cursor.ansi = '';
41
42
  if (!pending) return stripAnsi(value);
42
43
  const sequence = pending[0];
@@ -53,7 +54,7 @@ function visibleText(cursor, text) {
53
54
  export function readShellTaskOutput(task, result = {}, { output = 'new' } = {}) {
54
55
  const replay = output === 'tail';
55
56
  const terminal = task.status !== 'running';
56
- const limit = terminal || replay ? COMPLETION_BYTES : PAGE_BYTES;
57
+ const limit = terminal || replay ? TOOL_OUTPUT_TAIL_BYTES : TOOL_OUTPUT_PAGE_BYTES;
57
58
  let streams = replay ? new Map() : cursors.get(task);
58
59
  if (!streams) {
59
60
  streams = new Map();
@@ -79,10 +79,15 @@ export function parseLineLimitArg(value, defaultValue) {
79
79
  return Math.max(1, Math.trunc(n));
80
80
  }
81
81
 
82
+ /** Whether the read display clips this source line. */
83
+ export function isReadLineClipped(line, { truncateLongLine = true } = {}) {
84
+ return truncateLongLine && String(line ?? '').length > READ_MAX_RENDERED_LINE_CHARS;
85
+ }
86
+
82
87
  export function truncateReadLineText(line, { truncateLongLine = true } = {}) {
83
88
  let text = String(line ?? '');
84
89
  const originalLength = text.length;
85
- if (truncateLongLine && text.length > READ_MAX_RENDERED_LINE_CHARS) {
90
+ if (isReadLineClipped(text, { truncateLongLine })) {
86
91
  const cps = [...text];
87
92
  const head = cps.slice(0, 1_500).join('');
88
93
  const tail = cps.slice(-300).join('');
@@ -3,6 +3,7 @@ import { assertPathReachable, assertPathsReachable } from './fs-reachability.mjs
3
3
  import { isAbsolute, join, resolve } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { randomUUID } from 'node:crypto';
6
+ import { isBuiltin } from 'node:module';
6
7
  import { cwdRelativePath, normalizeInputPath, normalizeOutputPath, resolveAgainstCwd } from './path-utils.mjs';
7
8
 
8
9
  // Hard-block patterns live exclusively in ../shell-policy.mjs (BLOCKED_PATTERNS /
@@ -661,6 +662,24 @@ const LONG_INLINE_SCRIPT_RE =
661
662
  /\b(node(?:\.exe)?|python3?|py)((?:\s+--?[\w-]+(?:=[^\s"']+)?)*)\s+(-e|--eval|-c)\s+(?:"((?:[^"\\]|\\.)*)"|'((?:[^']|'')*)')/i;
662
663
  const POWERSHELL_FILE_SEMANTIC_RE = /\$(?:PSScriptRoot|PSCommandPath|MyInvocation)\b|^\s*(?:param\s*\(|#requires\b)/im;
663
664
 
665
+ // `node -e` resolves a package name from the working directory; a hoisted file
666
+ // resolves it from the temp directory it lives in, where no node_modules is.
667
+ // Any package specifier (not a Node builtin), require.resolve, or a specifier
668
+ // that is not a string literal therefore keeps the -e form.
669
+ const NODE_SPECIFIER_RE = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s+|\bimport\s+)['"]([^'"]+)['"]/g;
670
+ const NODE_LOADER_CALL_RE = /\b(?:require|import)\s*\(/g;
671
+ const NODE_LITERAL_LOADER_CALL_RE = /\b(?:require|import)\s*\(\s*['"][^'"]+['"]/g;
672
+ function importsPackage(body) {
673
+ if (/\brequire\s*\.\s*resolve\b/.test(body)) return true;
674
+ if ((body.match(NODE_LOADER_CALL_RE) || []).length !== (body.match(NODE_LITERAL_LOADER_CALL_RE) || []).length) {
675
+ return true;
676
+ }
677
+ for (const [, specifier] of body.matchAll(NODE_SPECIFIER_RE)) {
678
+ if (!isBuiltin(specifier)) return true;
679
+ }
680
+ return false;
681
+ }
682
+
664
683
  /** File extension a hoisted inline script gets: Python, or the Node module kind the flags declare. */
665
684
  function inlineScriptExtension(isNode, esm) {
666
685
  if (!isNode) return '.py';
@@ -684,6 +703,7 @@ export function planInlineScriptHoist(command) {
684
703
  const [whole, exe, rawFlags, , body] = match;
685
704
  if (INLINE_UNSAFE_BODY.test(body)) return null;
686
705
  if (INLINE_FILE_RELATIVE.test(body)) return null;
706
+ if (/^node/i.test(exe) && importsPackage(body)) return null;
687
707
  const { extension, keptFlags } = inlineScriptFileShape(exe, rawFlags);
688
708
  return {
689
709
  exe,