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
@@ -6,6 +6,7 @@
6
6
  // converge on one runtime.
7
7
 
8
8
  export function createSessionLoads({
9
+ pendingViewers,
9
10
  sessionExists,
10
11
  log,
11
12
  assertAvailable,
@@ -20,12 +21,21 @@ export function createSessionLoads({
20
21
  const sessionLoads = new Map(); // sessionId -> Promise<entry>
21
22
 
22
23
  function getOrCreateSessionEntry(sessionId, create) {
23
- const owner = sessionOwner(sessionId);
24
- if (owner) return Promise.resolve(owner);
25
24
  const inFlight = sessionLoads.get(sessionId);
26
25
  if (inFlight) return inFlight;
27
26
  const loading = Promise.resolve()
28
- .then(create)
27
+ .then(async () => {
28
+ const owner = sessionOwner(sessionId);
29
+ if (owner) {
30
+ if ((await owner.runtime.isSessionTerminal?.()) !== true) return owner;
31
+ if (owner.subscribers?.size) {
32
+ if (!pendingViewers.has(sessionId)) pendingViewers.set(sessionId, new Set());
33
+ for (const subscriber of owner.subscribers) pendingViewers.get(sessionId).add(subscriber);
34
+ }
35
+ await destroy(owner, 'replace terminal runtime', { keepBackgroundWork: true, announce: false });
36
+ }
37
+ return create();
38
+ })
29
39
  .finally(() => {
30
40
  if (sessionLoads.get(sessionId) === loading) sessionLoads.delete(sessionId);
31
41
  });
@@ -84,11 +94,12 @@ export function createSessionLoads({
84
94
  * flight), or null where answering would take a fresh load. */
85
95
  async function hostedEntryForSession(sessionId) {
86
96
  assertAvailable();
97
+ if (sessionLoads.has(sessionId)) return sessionLoads.get(sessionId);
87
98
  const owner = sessionOwner(sessionId);
88
- if (owner) return owner;
99
+ if (owner) return (await owner.runtime.isSessionTerminal?.()) === true ? null : owner;
89
100
  const external = await bindExternalSessionView(sessionId);
90
101
  const acquiredOwner = sessionOwner(sessionId);
91
- if (acquiredOwner) return acquiredOwner;
102
+ if (acquiredOwner) return (await acquiredOwner.runtime.isSessionTerminal?.()) === true ? null : acquiredOwner;
92
103
  if (external?.runtime?.externalAction === true) return external;
93
104
  return sessionLoads.get(sessionId) || null;
94
105
  }
@@ -70,6 +70,7 @@ export function createSessionEntries({
70
70
  publishExternalSessionState,
71
71
  });
72
72
  const loads = createSessionLoads({
73
+ pendingViewers,
73
74
  sessionExists,
74
75
  log,
75
76
  assertAvailable: lifecycle.assertAvailable,
@@ -386,3 +386,42 @@ export async function loadLiveTranscriptHead(entry, sessionId, readStoredSession
386
386
  view.headOlder = from > 0 || stored.transcriptHasOlder === true;
387
387
  return true;
388
388
  }
389
+
390
+ /** A one-shot range must not grow or rebudget an attached desktop's window. */
391
+ export async function readTranscriptRange(entry, sessionId, readStoredSession, { offset, limit }) {
392
+ if (
393
+ !Number.isSafeInteger(offset) ||
394
+ offset < 0 ||
395
+ !Number.isInteger(limit) ||
396
+ limit < 1 ||
397
+ limit > 100 ||
398
+ !Number.isSafeInteger(offset + limit)
399
+ )
400
+ throw new TypeError('invalid transcript range');
401
+ const requested = offset + limit;
402
+ let snapshot;
403
+ if (entry) {
404
+ const state = entry.runtime.getState();
405
+ const isolated = { runtime: { getState: () => state } };
406
+ requestLiveTranscriptWindow(isolated, { limit: requested, byteBudget: null, pageBase: 0 });
407
+ windowTranscriptSnapshot(isolated, state);
408
+ isolated.transcriptView.paged = true;
409
+ await loadLiveTranscriptHead(isolated, sessionId, readStoredSession);
410
+ snapshot = windowTranscriptSnapshot(isolated, state);
411
+ } else {
412
+ snapshot = await readStoredSession(sessionId, { transcriptItemLimit: requested });
413
+ }
414
+ if (!snapshot) throw new Error(`session ${sessionId} is not available`);
415
+ const source = snapshot.items || [];
416
+ if (offset >= source.length && snapshot.transcriptHasOlder) {
417
+ throw new Error('session: requested history is not available in the retained projection');
418
+ }
419
+ const end = Math.max(0, source.length - offset);
420
+ const start = Math.max(0, end - limit);
421
+ return {
422
+ sessionId,
423
+ offset,
424
+ items: sanitizeForWire(source.slice(start, end)),
425
+ hasOlder: start > 0 || snapshot.transcriptHasOlder === true,
426
+ };
427
+ }
@@ -8,8 +8,6 @@
8
8
  // again, and the session's own lane runs the retry under the entry's
9
9
  // generation. Overdue entries run as soon as the scheduler starts.
10
10
 
11
- import { isAgentOnlySession } from '../../runtime/shared/quota-resume-store.mjs';
12
-
13
11
  /** A still-exhausted reading is re-taken once, this much later. */
14
12
  export const QUOTA_RESUME_RETRY_MS = 5 * 60_000;
15
13
  /** A busy session is re-checked this much later, without spending an attempt. */
@@ -32,19 +30,33 @@ async function resumeDueEntry(entry, { store, materializeSession, readQuotaUsabl
32
30
  return 'unavailable';
33
31
  }
34
32
  if (!isCurrent(store, entry)) return 'cancelled';
35
- // A delegated agent child never owns a resume (the Lead decides retries), so
36
- // an entry persisted for one never fires, whatever wrote it.
37
- if (isAgentOnlySession({ id: sessionId, ...runtime?.session })) {
38
- store.cancelQuotaResumeEntry(sessionId, { generation });
39
- return 'agent-session';
40
- }
41
33
  let usable = false;
34
+ const currentRoute = () => {
35
+ const snapshot = runtime.getState?.() ?? {};
36
+ return {
37
+ // The selected route is what the next turn runs on; sessionProvider /
38
+ // sessionModel still hold the previous turn's after a deferred selection.
39
+ provider: snapshot.provider || snapshot.sessionProvider || runtime.session?.provider || entry.provider,
40
+ model: snapshot.model || snapshot.sessionModel || runtime.session?.model || entry.model,
41
+ };
42
+ };
43
+ const { provider, model } = currentRoute();
42
44
  try {
43
- usable = await readQuotaUsable(entry.provider, entry.model);
45
+ usable = await readQuotaUsable(provider, model);
44
46
  } catch (error) {
45
- log(`quota resume usage reading failed provider=${entry.provider}: ${error?.message || error}`);
47
+ log(`quota resume usage reading failed provider=${provider}: ${error?.message || error}`);
46
48
  }
47
49
  if (!isCurrent(store, entry)) return 'cancelled';
50
+ const selected = currentRoute();
51
+ if (selected.provider !== provider || selected.model !== model) {
52
+ // The reading belongs to the old route. Preserve the reservation and
53
+ // check the selected route on the normal busy-recheck cadence.
54
+ store.deferQuotaResumeEntry(sessionId, generation, {
55
+ resetAt: now() + QUOTA_RESUME_BUSY_RECHECK_MS,
56
+ attempts: entry.attempts,
57
+ });
58
+ return 'route-changed';
59
+ }
48
60
  if (!usable) {
49
61
  // One more reading five minutes on; after that the session falls back
50
62
  // to the user's own choice.
@@ -59,8 +71,8 @@ async function resumeDueEntry(entry, { store, materializeSession, readQuotaUsabl
59
71
  return 'exhausted';
60
72
  }
61
73
  const outcome =
62
- typeof runtime?.fireQuotaResume === 'function' ? runtime.fireQuotaResume({ generation }) : 'unsupported';
63
- if (outcome === 'busy') {
74
+ typeof runtime?.fireQuotaResume === 'function' ? await runtime.fireQuotaResume({ generation }) : 'unsupported';
75
+ if (outcome === 'busy' || outcome === 'rejected') {
64
76
  store.deferQuotaResumeEntry(sessionId, generation, {
65
77
  resetAt: now() + QUOTA_RESUME_BUSY_RECHECK_MS,
66
78
  attempts: entry.attempts,
@@ -117,7 +129,10 @@ export function createQuotaResumeScheduler({
117
129
  log(`quota resume session=${entry.sessionId} outcome=${outcome}`);
118
130
  } catch (error) {
119
131
  log(`quota resume failed session=${entry.sessionId}: ${error?.message || error}`);
120
- store.cancelQuotaResumeEntry(entry.sessionId, { generation: entry.generation });
132
+ store.deferQuotaResumeEntry(entry.sessionId, entry.generation, {
133
+ resetAt: now() + QUOTA_RESUME_RETRY_MS,
134
+ attempts: entry.attempts,
135
+ });
121
136
  }
122
137
  }
123
138
  })().finally(() => {
@@ -5,6 +5,7 @@
5
5
  // ./goal-recovery.mjs.
6
6
  import { createGoalRecovery } from './goal-recovery.mjs';
7
7
  import {
8
+ attachmentsForIntake,
8
9
  materializePromptSubmission,
9
10
  preparePromptSubmissionForProvider,
10
11
  } from '../../../runtime/attachments/store.mjs';
@@ -52,18 +53,28 @@ export function createSessionTurnCalls(ctx) {
52
53
  const entry = await entryForSession(id, openHints || {});
53
54
  const target = entry.runtime.submitAsync;
54
55
  if (typeof target !== 'function') throw new TypeError('session runtime must implement submitAsync');
55
- const intake = await preparePromptSubmissionForProvider(
56
- materializePromptSubmission(prompt, options || {}),
57
- { nativePdf: providerNativePdf(entry.runtime.provider || entry.runtime.session?.provider || '') }
58
- );
56
+ const intake = await preparePromptSubmissionForProvider(materializePromptSubmission(prompt, options || {}), {
57
+ nativePdf: providerNativePdf(entry.runtime.provider || entry.runtime.session?.provider || ''),
58
+ });
59
59
  // Await intake only: submitAsync resolves once the prompt is represented by
60
60
  // the queue/user row, while provider execution remains daemon-owned and
61
61
  // detached.
62
62
  let submissionOptions = intake.options;
63
- if (entry.runtime.externalAction === true) {
63
+ // Every send (desktop or paired device) reaches the engine here: the
64
+ // message records its attachments by content hash, so any device can open
65
+ // or download them by id later (and the store's census keeps the blobs).
66
+ const attachments = attachmentsForIntake(intake.prompt, intake.options);
67
+ if (attachments.length || entry.runtime.externalAction === true) {
64
68
  const transcriptMeta = intake.options?.transcriptMeta;
65
69
  const baseMeta = transcriptMeta && typeof transcriptMeta === 'object' ? transcriptMeta : {};
66
- submissionOptions = { ...intake.options, transcriptMeta: { ...baseMeta, sender: 'user' } };
70
+ submissionOptions = {
71
+ ...intake.options,
72
+ transcriptMeta: {
73
+ ...baseMeta,
74
+ ...(attachments.length ? { attachments } : {}),
75
+ ...(entry.runtime.externalAction === true ? { sender: 'user' } : {}),
76
+ },
77
+ };
67
78
  }
68
79
  const accepted = await Promise.resolve(target.call(entry.runtime, intake.prompt, submissionOptions));
69
80
  if (accepted === true) {
@@ -6,6 +6,7 @@ import { SESSION_READ_ACTION_SET } from '../../session-protocol.mjs';
6
6
  import { SESSION_ID_PATTERN } from '../agent-tree.mjs';
7
7
  import {
8
8
  loadLiveTranscriptHead,
9
+ readTranscriptRange,
9
10
  rebudgetTranscriptWindow,
10
11
  requestedHeldTranscript,
11
12
  requestedTranscriptWindow,
@@ -132,6 +133,11 @@ export function createSessionViewCalls(ctx) {
132
133
  if (!id) throw new TypeError('sessionId is required');
133
134
  const live = await liveEntryFor(id);
134
135
  assertAvailable(live);
136
+ if (params.transcriptRange) {
137
+ const range = await readTranscriptRange(live, id, readStoredSession, params.transcriptRange);
138
+ assertAvailable(live);
139
+ return { range };
140
+ }
135
141
  if (live) {
136
142
  return liveSessionReadResult(live, params, id, baseRevision);
137
143
  }
@@ -0,0 +1,82 @@
1
+ import { encodeCatalogCursor } from '../runtime/agent/orchestrator/session/catalog-db/schema.mjs';
2
+ import { truncateToolText } from '../runtime/shared/tool-primitives.mjs';
3
+ import { summarizeToolResult } from '../runtime/shared/tool-result-summary.mjs';
4
+
5
+ import { TOOL_OUTPUT_PAGE_BYTES, TOOL_OUTPUT_TAIL_BYTES } from '../runtime/shared/tool-output-budget.mjs';
6
+
7
+ export const SESSION_TOOL_OUTPUT_BYTES = TOOL_OUTPUT_TAIL_BYTES;
8
+ const MESSAGE_TEXT_LIMIT = 2000;
9
+ const outputBytes = (value) => Buffer.byteLength(JSON.stringify(value), 'utf8');
10
+
11
+ /** Keep catalog paging authoritative even when the output budget shortens a page. */
12
+ export function sessionListOutput(page, limit) {
13
+ const raw = page.sessions;
14
+ const sessions = raw.slice(0, limit).map((row) => ({
15
+ id: row.id,
16
+ ...(row.title || row.preview ? { title: truncateToolText(row.title || row.preview, 160) } : {}),
17
+ ...(row.status ? { status: row.status } : {}),
18
+ ...(row.cwd ? { cwd: row.cwd } : {}),
19
+ }));
20
+ const result = { sessions, nextCursor: page.nextCursor ?? null };
21
+ for (;;) {
22
+ if (sessions.length < raw.length && sessions.length) {
23
+ result.nextCursor = encodeCatalogCursor(raw[sessions.length - 1]);
24
+ }
25
+ if (outputBytes(result) <= TOOL_OUTPUT_PAGE_BYTES) return result;
26
+ if (sessions.length <= 1) throw new Error('session: one catalog row exceeds the output budget');
27
+ sessions.pop();
28
+ }
29
+ }
30
+
31
+ function conversationItem(item, offset, textOffset) {
32
+ const row = { kind: item.kind, offset, ...(item.id != null ? { id: item.id } : {}) };
33
+ if (item.name) row.name = item.name;
34
+ if (item.status) row.status = item.status;
35
+ if (item.isError === true) row.isError = true;
36
+ let text = item.text || item.detail || item.label || '';
37
+ if (item.kind === 'tool') {
38
+ const value = item.rawResult ?? item.result ?? '';
39
+ const raw = typeof value === 'string' ? value : JSON.stringify(value);
40
+ text = raw || summarizeToolResult(item.name, item.args, raw, item.isError === true) || text;
41
+ }
42
+ if (text) {
43
+ // Conversation indentation is significant; do not trim code/prose like a title.
44
+ if (
45
+ textOffset > 0 &&
46
+ /[\uDC00-\uDFFF]/.test(text[textOffset] || '') &&
47
+ /[\uD800-\uDBFF]/.test(text[textOffset - 1])
48
+ )
49
+ throw new Error('session: text_offset splits a surrogate pair');
50
+ let end = Math.min(text.length, textOffset + MESSAGE_TEXT_LIMIT);
51
+ if (end < text.length && /[\uD800-\uDBFF]/.test(text[end - 1])) end--;
52
+ row.text = text.slice(textOffset, end);
53
+ if (end < text.length) row.nextTextOffset = end;
54
+ }
55
+ const attachments = item.attachments ?? item.imageRefs ?? item.images;
56
+ if (attachments?.length) row.attachments = attachments.length;
57
+ return row;
58
+ }
59
+
60
+ /** Service windows can be sticky/shared; enforce this tool call's limits locally. */
61
+ export function sessionReadOutput(sessionId, range, { textOffset = 0, itemId } = {}) {
62
+ const byteLimit = range.offset === 0 && textOffset === 0 ? TOOL_OUTPUT_TAIL_BYTES : TOOL_OUTPUT_PAGE_BYTES;
63
+ const source = range.items;
64
+ if (itemId !== undefined && (source.length !== 1 || source[0].id !== itemId)) {
65
+ throw new Error('session: stale item position; read the tail again');
66
+ }
67
+ const items = source.map((item, index) =>
68
+ conversationItem(item, range.offset + source.length - index - 1, textOffset)
69
+ );
70
+ const result = {
71
+ sessionId,
72
+ offset: range.offset,
73
+ items,
74
+ nextOffset: null,
75
+ };
76
+ for (;;) {
77
+ result.nextOffset = range.hasOlder || items.length < source.length ? range.offset + items.length : null;
78
+ if (outputBytes(result) <= byteLimit) return result;
79
+ if (items.length <= 1) throw new Error('session: one transcript item exceeds the output budget');
80
+ items.shift();
81
+ }
82
+ }
@@ -138,6 +138,7 @@ export function renderAppView(ctx) {
138
138
  thinkingActiveSince={liveSpinner?.thinkingSegmentStartedAt ?? 0}
139
139
  thinkingMs={liveSpinner?.thinkingAccumulatedMs ?? 0}
140
140
  effort={state.effort || ''}
141
+ quiet={liveSpinner?.quiet ?? null}
141
142
  hasActiveTools={!!activeTools?.web_search?.count}
142
143
  paused={!!toolApproval}
143
144
  interruptible={!!(state.busy && state.spinner?.active)}
@@ -230,7 +231,7 @@ export function renderAppView(ctx) {
230
231
  ) : null}
231
232
  <StatusLine
232
233
  sessionId={state.sessionId}
233
- clientHostPid={state.ownerClientHostPid || state.clientHostPid}
234
+ clientHostPid={state.clientHostPid}
234
235
  provider={state.provider}
235
236
  model={state.model}
236
237
  effort={state.effort}
@@ -77,6 +77,14 @@ export function buildProviderItems(setup) {
77
77
  _authenticated: p.authenticated,
78
78
  });
79
79
  }
80
+ if (setup.managedByClient === true) {
81
+ for (const p of setup.local || []) {
82
+ providerItems.push({
83
+ value: `local:${p.id}`, label: p.name, meta: providerStatusLabel(p), description: '',
84
+ _type: 'local', _providerId: p.id, _providerName: p.name, _provider: p,
85
+ });
86
+ }
87
+ }
80
88
  providerItems.sort((a, b) => {
81
89
  const rank = providerItemRank(a) - providerItemRank(b);
82
90
  if (rank !== 0) return rank;
@@ -54,7 +54,7 @@ export function createProviderSetupPicker({
54
54
  });
55
55
  try {
56
56
  await new Promise((resolve) => setTimeout(resolve, 0));
57
- return await store.getProviderSetup();
57
+ return await store.getProviderSetup(options.forceRefresh ? { force: true } : undefined);
58
58
  } catch (e) {
59
59
  store.pushNotice(`providers failed: ${e?.message || e}`, 'error');
60
60
  return null;
@@ -98,6 +98,9 @@ export function createProviderSetupPicker({
98
98
  });
99
99
  }
100
100
  items.push(...buildProviderItems(setup));
101
+ if (setup.managedByClient === true) {
102
+ items.push({ value: 'refresh-client-providers', label: 'Refresh', _type: 'refresh-client-providers' });
103
+ }
101
104
 
102
105
  const flow = {
103
106
  store,
@@ -127,10 +130,13 @@ export function createProviderSetupPicker({
127
130
 
128
131
  flow.paint({
129
132
  title: options.title || 'Providers',
130
- description: options.description || 'Choose a provider. Enter opens provider actions.',
133
+ description: setup.managedByClient === true
134
+ ? 'Manage provider sign-ins and accounts in the local desktop window. Refresh to update availability.'
135
+ : options.description || 'Choose a provider. Enter opens provider actions.',
131
136
  footer: providerFooter,
132
137
  footerGapRows: 1,
133
- help: options.confirmBar ? undefined : '↑/↓ Select · Enter Open · Esc Back',
138
+ help: options.confirmBar ? undefined : setup.managedByClient === true
139
+ ? '↑/↓ Select · Enter Refresh · Esc Back' : '↑/↓ Select · Enter Open · Esc Back',
134
140
  indexMode: 'always',
135
141
  labelWidth: 18,
136
142
  metaWidth: 12,
@@ -142,6 +148,12 @@ export function createProviderSetupPicker({
142
148
  if (item?._providerId) flow.rememberProviderSelection(item);
143
149
  },
144
150
  onSelect: (_value, item) => {
151
+ if (setup.managedByClient === true && item._type !== 'continue') {
152
+ if (item._type === 'refresh-client-providers') {
153
+ void openProviderSetupPicker({ ...options, preloadedSetup: null, forceRefresh: true });
154
+ }
155
+ return;
156
+ }
145
157
  // In-flow navigation (Enter on a provider row): the flow keeps the
146
158
  // surface, so clear-and-continue — own.close() would supersede this
147
159
  // very flow and paintProviders would then reject the action panel the
@@ -35,6 +35,12 @@ export const SLASH_COMMANDS = [
35
35
  },
36
36
  { name: 'resume', usage: '/resume', params: '[id]', description: 'Resume a saved chat' },
37
37
  { name: 'context', usage: '/context', description: 'Show current context surface' },
38
+ {
39
+ name: 'shells',
40
+ usage: '/shells',
41
+ params: '[release <task_id> [--confirm]]',
42
+ description: 'List shells; release one whose termination is unconfirmed',
43
+ },
38
44
  {
39
45
  name: 'fork',
40
46
  usage: '/fork',
@@ -119,6 +119,55 @@ export const sessionCommands = {
119
119
  return true;
120
120
  },
121
121
 
122
+ // /shells — this session's shells, each marked running or "termination
123
+ // unconfirmed". `/shells release <id>` explains what releasing means and
124
+ // asks for `--confirm`; only then is the workspace hold dropped.
125
+ shells(ctx, arg) {
126
+ const { store } = ctx;
127
+ const [action, taskId, flag] = String(arg || '')
128
+ .trim()
129
+ .split(/\s+/)
130
+ .filter(Boolean);
131
+ if (!action) {
132
+ void Promise.resolve(store.listShellJobs?.())
133
+ .then((result) => {
134
+ const jobs = Array.isArray(result?.jobs) ? result.jobs : [];
135
+ if (!jobs.length) {
136
+ store.pushNotice('No shells are running.', 'info');
137
+ return;
138
+ }
139
+ const lines = jobs.map(
140
+ (job) =>
141
+ `${job.taskId} ${job.terminationUnconfirmed ? 'termination unconfirmed' : 'running'} ${job.command}`
142
+ );
143
+ const held = jobs.some((job) => job.terminationUnconfirmed);
144
+ if (held) lines.push('Release a held shell with /shells release <task_id>.');
145
+ store.pushNotice(lines.join('\n'), held ? 'warn' : 'info');
146
+ })
147
+ .catch((error) => store.pushNotice(`Couldn’t list shells: ${error?.message || error}`, 'error'));
148
+ return true;
149
+ }
150
+ if (action !== 'release' || !taskId) {
151
+ store.pushNotice('usage: /shells [release <task_id> [--confirm]]', 'warn');
152
+ return true;
153
+ }
154
+ if (flag !== '--confirm') {
155
+ store.pushNotice(
156
+ `Releasing ${taskId} unlocks its folder although its processes may still be running. To proceed, run /shells release ${taskId} --confirm`,
157
+ 'warn'
158
+ );
159
+ return true;
160
+ }
161
+ void Promise.resolve(store.releaseShellJob?.(taskId))
162
+ .then((result) =>
163
+ result?.released
164
+ ? store.pushNotice(`Released ${taskId}.`, 'info')
165
+ : store.pushNotice(`Couldn’t release ${taskId}: ${result?.error || 'unavailable'}`, 'warn')
166
+ )
167
+ .catch((error) => store.pushNotice(`Couldn’t release ${taskId}: ${error?.message || error}`, 'error'));
168
+ return true;
169
+ },
170
+
122
171
  doctor(ctx) {
123
172
  const { state, store, deps } = ctx;
124
173
  if (state.commandBusy) {
@@ -66,6 +66,8 @@ export function Spinner({
66
66
  thinkingActiveSince = 0,
67
67
  thinkingMs = 0,
68
68
  effort = '',
69
+ // The turn's quiet state (`spinner.quiet`), shown as the quiet-time cue.
70
+ quiet = null,
69
71
  hasActiveTools = false,
70
72
  paused = false,
71
73
  interruptible = false,
@@ -126,6 +128,8 @@ export function Spinner({
126
128
  thinkingActiveSince={thinkingActiveSince}
127
129
  thinkingMs={thinkingMs}
128
130
  effort={effort}
131
+ quiet={quiet}
132
+ now={now}
129
133
  interruptible={interruptible}
130
134
  columns={columns}
131
135
  reducedMotion={reducedMotion}
@@ -20,6 +20,7 @@ const THINKING_DELAY_MS = 3000;
20
20
  // One-way shimmer. The tail runs past the final character before restarting.
21
21
  const GLIMMER_SPEED_MS = {
22
22
  requesting: 70,
23
+ 'model-thinking': 70,
23
24
  reconnecting: 70,
24
25
  compacting: 120,
25
26
  'auto-clear': 120,
@@ -91,7 +92,9 @@ function tokenModeGlyph(mode) {
91
92
  case 'responding':
92
93
  case 'thinking':
93
94
  return DOWN_ARROW;
95
+ // The request is sent and nothing has come back yet.
94
96
  case 'requesting':
97
+ case 'model-thinking':
95
98
  return UP_ARROW;
96
99
  default:
97
100
  return '';
@@ -108,6 +111,8 @@ export function SpinnerByline({
108
111
  thinkingActiveSince,
109
112
  thinkingMs,
110
113
  effort,
114
+ quiet = null,
115
+ now = Date.now(),
111
116
  interruptible,
112
117
  columns,
113
118
  reducedMotion,
@@ -151,6 +156,8 @@ export function SpinnerByline({
151
156
  thinkingSince: thinkingActiveSince,
152
157
  thinkingMs,
153
158
  effort,
159
+ quiet,
160
+ now,
154
161
  });
155
162
  const tokenGlyph = tokenModeGlyph(mode);
156
163
  let tokenText = '';
@@ -176,8 +183,13 @@ export function SpinnerByline({
176
183
  const interruptW = interruptText.length;
177
184
  const showInterrupt = Boolean(interruptText) && avail > interruptW;
178
185
  const usedAfterInterrupt = showInterrupt ? interruptW + SEP_WIDTH : 0;
179
- const showThinkingStatus = Boolean(thinkingStatusText) && avail > usedAfterInterrupt + thinkingStatusW;
180
- const usedAfterThinking = usedAfterInterrupt + (showThinkingStatus ? thinkingStatusW + SEP_WIDTH : 0);
186
+ // A quiet turn says so before any other stat: it is the one that explains a
187
+ // long silence (the interrupt hint beside it is how to stop it).
188
+ const quietText = meta.quietText;
189
+ const showQuiet = Boolean(quietText) && avail > usedAfterInterrupt + quietText.length;
190
+ const usedAfterQuiet = usedAfterInterrupt + (showQuiet ? quietText.length + SEP_WIDTH : 0);
191
+ const showThinkingStatus = Boolean(thinkingStatusText) && avail > usedAfterQuiet + thinkingStatusW;
192
+ const usedAfterThinking = usedAfterQuiet + (showThinkingStatus ? thinkingStatusW + SEP_WIDTH : 0);
181
193
  const showTimer = Boolean(timerLabel) && avail > usedAfterThinking + timerW;
182
194
  const usedAfterTimer = usedAfterThinking + (showTimer ? timerW + SEP_WIDTH : 0);
183
195
  const showTokens = wantsTokens && tokenText && avail > usedAfterTimer + tokenW;
@@ -223,6 +235,13 @@ export function SpinnerByline({
223
235
  )
224
236
  );
225
237
  }
238
+ if (showQuiet) {
239
+ segments.push(
240
+ <Text key="quiet" color={meta.quietParts?.escalated ? theme.warning : theme.statusSubtle}>
241
+ {quietText}
242
+ </Text>
243
+ );
244
+ }
226
245
  if (showInterrupt) {
227
246
  segments.push(
228
247
  <Text key="interrupt" color={theme.statusSubtle}>
@@ -10,7 +10,7 @@ import { normalizeStatuslineAnsi } from '../../statusline-ansi-bridge.mjs';
10
10
  // Cache-only synchronous read (never touches fs on the render path), so the
11
11
  // instant-local L2 can render the Shell segment itself instead of grafting it
12
12
  // out of the previous full line.
13
- import { shellJobsStatus } from '../../../runtime/shared/statusline/statusline-segments.mjs';
13
+ import { shellJobsStatus, unconfirmedShellCount } from '../../../runtime/shared/statusline/statusline-segments.mjs';
14
14
  import { formatElapsed, num, terminalColumns, timeMs } from '../../../runtime/shared/statusline/statusline-format.mjs';
15
15
  import { isTerminalStatus } from './boot-schedule.mjs';
16
16
 
@@ -137,11 +137,19 @@ export function localStatusLineL2(
137
137
  const shellStatus = shellScope
138
138
  ? shellJobsStatus({ clientHostPid, sessionId: shellScope })
139
139
  : shellJobsStatus({ clientHostPid });
140
- const shellCount = Number(shellStatus?.count) || 0;
140
+ // A shell whose termination is unconfirmed is not "running": it is held,
141
+ // with /shells as the way to inspect or release it.
142
+ const unconfirmed = unconfirmedShellCount(shellStatus);
143
+ const shellCount = (Number(shellStatus?.count) || 0) - unconfirmed;
141
144
  if (shellCount > 0) {
142
145
  const label = `Running ${shellCount} Shell${shellCount === 1 ? '' : 's'}`;
143
146
  l2Parts.push(`${spin} ${STATUS}${label}${RESET}${elapsedSuffix(shellStatus.elapsedLabel || '')}`);
144
147
  }
148
+ if (unconfirmed > 0) {
149
+ const { WARNING } = statusColors();
150
+ const label = `⚠ ${unconfirmed} Shell${unconfirmed === 1 ? '' : 's'} termination unconfirmed`;
151
+ l2Parts.push(`${WARNING}${label}${RESET} ${SUBTLE}·${RESET} /shells`);
152
+ }
145
153
  // `web_search` is the key the producers publish (session/active-tool-summary
146
154
  // and app/use-transcript-activity); the async full render reads the same key,
147
155
  // so both paths light the segment on the same snapshot.