mixdog 1.1.2 → 1.2.1

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 (424) hide show
  1. package/README.md +8 -5
  2. package/package.json +2 -1
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/test-direct.mjs +4 -2
  6. package/src/defaults/shipped-history.json +10 -0
  7. package/src/defaults/skills/pptx/SKILL.md +6 -6
  8. package/src/defaults/skills/pptx/references/review.md +6 -4
  9. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  10. package/src/defaults/skills/setup/references/actions.md +18 -5
  11. package/src/defaults/skills/visualize/SKILL.md +160 -0
  12. package/src/defaults/skills/visualize/references/craft.md +88 -0
  13. package/src/headless-command.mjs +56 -3
  14. package/src/headless-exec.mjs +17 -1
  15. package/src/help.mjs +2 -0
  16. package/src/rules/shared/60-verification.md +3 -1
  17. package/src/rules/shared/70-delivery.md +4 -2
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
  21. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  22. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  24. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +19 -5
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
  33. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  35. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  36. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  43. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  45. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  50. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
  51. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  52. package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
  53. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  55. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  56. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  57. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  59. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  60. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  61. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
  62. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  64. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  66. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
  67. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  68. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
  78. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  79. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  80. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
  81. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  82. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  83. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
  85. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  86. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  87. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  88. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  90. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  91. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  92. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  93. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
  94. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  95. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  96. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  97. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
  98. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  99. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  100. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  101. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  102. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  103. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  104. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  105. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  106. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  107. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  108. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  109. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  112. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  114. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
  115. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
  116. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
  124. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  125. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  126. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
  128. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  131. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  133. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  136. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  137. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  138. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
  139. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  140. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  141. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  142. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  143. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  144. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
  145. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
  146. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
  147. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  148. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
  149. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  150. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  151. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
  153. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  157. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  158. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  159. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  160. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  161. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  162. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
  163. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  164. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  165. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  166. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  167. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
  168. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  169. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  175. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  176. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  177. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  179. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  180. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  181. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  182. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  184. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  185. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  186. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  187. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  188. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  189. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  190. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  191. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  192. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  193. package/src/runtime/attachments/store.d.mts +3 -0
  194. package/src/runtime/attachments/store.mjs +38 -0
  195. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  196. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  197. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  199. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  200. package/src/runtime/memory/lib/memory.mjs +1 -1
  201. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  202. package/src/runtime/shared/atomic-file.mjs +33 -6
  203. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  204. package/src/runtime/shared/code-graph-state.mjs +12 -0
  205. package/src/runtime/shared/defaults-separation.mjs +2 -0
  206. package/src/runtime/shared/extension-scopes.mjs +5 -3
  207. package/src/runtime/shared/file-lock.mjs +37 -11
  208. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  209. package/src/runtime/shared/git-runner.mjs +4 -3
  210. package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
  211. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  212. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  213. package/src/runtime/shared/llm/model-catalog.mjs +240 -130
  214. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  215. package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
  216. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  217. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  218. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  219. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  220. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  221. package/src/runtime/shared/pristine-execution.mjs +28 -4
  222. package/src/runtime/shared/provider-accounts.mjs +125 -21
  223. package/src/runtime/shared/provider-api-key.mjs +33 -1
  224. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  225. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  226. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  227. package/src/runtime/shared/shell-display.mjs +9 -3
  228. package/src/runtime/shared/singleton-owner.mjs +36 -6
  229. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  230. package/src/runtime/shared/skill-mentions.mjs +133 -0
  231. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  232. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  233. package/src/runtime/shared/tool-primitives.mjs +35 -2
  234. package/src/runtime/shared/tool-surface.mjs +7 -0
  235. package/src/runtime/shared/tool-work-units.mjs +14 -0
  236. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  237. package/src/runtime/shared/turn-changes.mjs +791 -0
  238. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  239. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  240. package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
  241. package/src/runtime/shared/worker-requests.mjs +15 -2
  242. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  243. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  244. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  245. package/src/runtime/shared/worktrees/git.mjs +247 -0
  246. package/src/runtime/shared/worktrees/include.mjs +480 -0
  247. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  248. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  249. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  250. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  251. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  252. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  253. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  254. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  255. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  256. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  257. package/src/runtime/visualize-bridge/client.mjs +181 -0
  258. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  259. package/src/session-runtime/automation-agents.mjs +8 -2
  260. package/src/session-runtime/boot/apis.mjs +90 -0
  261. package/src/session-runtime/boot/config.mjs +7 -2
  262. package/src/session-runtime/boot/facade.mjs +21 -0
  263. package/src/session-runtime/boot/infrastructure.mjs +30 -18
  264. package/src/session-runtime/boot/session.mjs +1 -0
  265. package/src/session-runtime/boot/tools.mjs +4 -0
  266. package/src/session-runtime/config-lifecycle.mjs +10 -1
  267. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  268. package/src/session-runtime/conversation-import/service.mjs +369 -0
  269. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
  270. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  271. package/src/session-runtime/global-extensions.mjs +2 -2
  272. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  273. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  274. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  275. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  276. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  277. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  278. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  279. package/src/session-runtime/mcp-glue/connect.mjs +68 -16
  280. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  281. package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
  282. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  283. package/src/session-runtime/mcp-glue.mjs +22 -2
  284. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  285. package/src/session-runtime/model-route/set-route.mjs +15 -2
  286. package/src/session-runtime/new-session-config.mjs +3 -2
  287. package/src/session-runtime/notification-bus.mjs +18 -3
  288. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  289. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  290. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  291. package/src/session-runtime/provider-readiness.mjs +16 -10
  292. package/src/session-runtime/resource-extension-sync.mjs +45 -7
  293. package/src/session-runtime/resource-mcp-api.mjs +121 -84
  294. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  295. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  296. package/src/session-runtime/resource-skills-api.mjs +23 -2
  297. package/src/session-runtime/route-resolution.mjs +10 -1
  298. package/src/session-runtime/route-state.mjs +16 -2
  299. package/src/session-runtime/runtime-core.mjs +5 -0
  300. package/src/session-runtime/runtime-facade.mjs +8 -0
  301. package/src/session-runtime/runtime-review-api.mjs +9 -0
  302. package/src/session-runtime/schedule-session-run.mjs +1 -1
  303. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  304. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  305. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  306. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  307. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  308. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  310. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  311. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  312. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  313. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  314. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  315. package/src/session-runtime/services/agent-tool.mjs +1 -0
  316. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  317. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  318. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  319. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  320. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  321. package/src/session-runtime/services/provider-admin.mjs +64 -18
  322. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  323. package/src/session-runtime/session-create/session-options.mjs +10 -1
  324. package/src/session-runtime/session-turn-api.mjs +1 -0
  325. package/src/session-runtime/settings-api.mjs +4 -2
  326. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  327. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  328. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  329. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  330. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
  331. package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
  332. package/src/session-runtime/skills-api.mjs +7 -3
  333. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  334. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
  335. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  336. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  337. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  338. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  339. package/src/session-runtime/turn/turn-run.mjs +6 -7
  340. package/src/session-runtime/webhook-session-run.mjs +16 -4
  341. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  342. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  343. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  344. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  345. package/src/session-runtime/workspace/transitions.mjs +651 -0
  346. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  347. package/src/skill-script-entry.mjs +36 -0
  348. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  349. package/src/standalone/channel-transport.mjs +13 -1
  350. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  351. package/src/standalone/daemon-owner.mjs +372 -0
  352. package/src/standalone/daemon-stored-session-views.mjs +15 -4
  353. package/src/standalone/daemon.mjs +179 -8
  354. package/src/standalone/desktop-service-registry.mjs +20 -0
  355. package/src/standalone/local-session-runtime.mjs +11 -0
  356. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  357. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  358. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  359. package/src/standalone/session-client.mjs +104 -13
  360. package/src/standalone/session-protocol.mjs +21 -0
  361. package/src/standalone/session-proxy.mjs +3 -0
  362. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  363. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  364. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  365. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  366. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  367. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  368. package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
  369. package/src/standalone/session-transport/call-route.mjs +13 -0
  370. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  371. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  372. package/src/standalone/session-transport/client-registry.mjs +20 -3
  373. package/src/standalone/session-transport/routes.mjs +79 -2
  374. package/src/standalone/session-transport/stack.mjs +2 -0
  375. package/src/standalone/session-transport.mjs +24 -1
  376. package/src/tui/App.jsx +9 -1
  377. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  378. package/src/tui/app/app-view.jsx +6 -0
  379. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  380. package/src/tui/app/message-selector.mjs +2 -1
  381. package/src/tui/app/model-options.mjs +37 -5
  382. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  383. package/src/tui/app/model-picker.mjs +4 -2
  384. package/src/tui/app/skill-mention-palette.mjs +88 -0
  385. package/src/tui/app/slash-palette.mjs +23 -7
  386. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  387. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  388. package/src/tui/app/use-prompt-state.mjs +5 -0
  389. package/src/tui/components/Message.jsx +22 -1
  390. package/src/tui/components/PromptInput.jsx +21 -3
  391. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  392. package/src/tui/components/TranscriptItem.jsx +9 -1
  393. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  394. package/src/tui/components/user-message-skills.mjs +38 -0
  395. package/src/tui/dist/index.mjs +839 -207
  396. package/src/tui/index.jsx +10 -2
  397. package/src/tui/session/compaction-view.mjs +147 -0
  398. package/src/tui/session/context-state.mjs +6 -0
  399. package/src/tui/session/initial-state.mjs +3 -0
  400. package/src/tui/session/queue-helpers.mjs +7 -2
  401. package/src/tui/session/quota-resume.mjs +2 -1
  402. package/src/tui/session/session-api/commands.mjs +10 -2
  403. package/src/tui/session/session-api/controls.mjs +4 -0
  404. package/src/tui/session/session-api/extensions.mjs +4 -2
  405. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  406. package/src/tui/session/session-api/integrations.mjs +17 -0
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  408. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  409. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  410. package/src/tui/session/session-api/routes.mjs +10 -1
  411. package/src/tui/session/session-api/settings.mjs +9 -0
  412. package/src/tui/session/session-api/worktree.mjs +60 -0
  413. package/src/tui/session/session-api.mjs +2 -0
  414. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  415. package/src/tui/session/session-flow/drain.mjs +27 -3
  416. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  417. package/src/tui/session/session-flow.mjs +2 -0
  418. package/src/tui/session/transcript-intake.mjs +14 -5
  419. package/src/tui/session/tui-steering-persist.mjs +25 -9
  420. package/src/tui/session/turn-stream.mjs +32 -4
  421. package/src/tui/session/turn-tool-cards.mjs +10 -2
  422. package/src/tui/session/turn.mjs +56 -10
  423. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  424. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
@@ -18,9 +18,13 @@ import { readSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
18
18
  import { isPidAlive } from '../runtime/shared/pid-liveness.mjs';
19
19
  import { resolveRuntimeRoot } from '../runtime/shared/runtime-root.mjs';
20
20
  import { withHeapCap } from '../runtime/shared/heap-cap.mjs';
21
- import { daemonDataDir } from '../session-runtime/services/daemon-crash-capture.mjs';
21
+ import { daemonCrashCaptureDir, daemonDataDir } from '../session-runtime/services/daemon-crash-capture.mjs';
22
22
  import { forkDaemonCandidate } from '../session-runtime/services/daemon-candidate.mjs';
23
+ import { daemonOwnerIsStale, proveDaemonOwnerRecord } from './daemon-owner.mjs';
23
24
  import { createAttachmentCalls } from './session-client/attachment-calls.mjs';
25
+ import { daemonUnavailableError } from './session-client/daemon-unavailable.mjs';
26
+ import { createLeaseHeartbeat } from './session-client/lease-heartbeat.mjs';
27
+ import { clampLeaseMs } from './session-transport/client-registry/client-grace.mjs';
24
28
  import {
25
29
  EVENT_STREAM_LIVENESS_TIMEOUT_MS,
26
30
  EVENT_STREAM_RECONNECT_BASE_MS,
@@ -90,8 +94,20 @@ function request({
90
94
  timeoutMs = 120_000,
91
95
  control = false,
92
96
  urgent = false,
97
+ // Total-duration bound (timeoutMs only measures socket inactivity, which a
98
+ // trickling response keeps resetting). Probes set it; calls do not.
99
+ deadlineMs = 0,
93
100
  }) {
94
- return new Promise((resolve, reject) => {
101
+ return new Promise((settleResolve, settleReject) => {
102
+ let deadlineTimer = null;
103
+ const resolve = (value) => {
104
+ clearTimeout(deadlineTimer);
105
+ settleResolve(value);
106
+ };
107
+ const reject = (error) => {
108
+ clearTimeout(deadlineTimer);
109
+ settleReject(error);
110
+ };
95
111
  const payload = body ? JSON.stringify(body) : null;
96
112
  const req = http.request(
97
113
  {
@@ -112,6 +128,7 @@ function request({
112
128
  res.on('data', (chunk) => {
113
129
  data += chunk;
114
130
  });
131
+ res.on('error', reject);
115
132
  res.on('end', () => {
116
133
  let parsed = null;
117
134
  try {
@@ -133,20 +150,43 @@ function request({
133
150
  req.destroy(new Error(`session timeout: ${method} ${urlPath}`));
134
151
  });
135
152
  req.on('error', reject);
153
+ if (deadlineMs > 0) {
154
+ deadlineTimer = setTimeout(() => {
155
+ const error = new Error(`session timeout: ${method} ${urlPath}`);
156
+ reject(error);
157
+ req.destroy(error);
158
+ }, deadlineMs);
159
+ }
136
160
  if (payload) req.write(payload);
137
161
  req.end();
138
162
  });
139
163
  }
140
164
 
165
+ /** /health within `timeoutMs` of total duration; null when it does not answer. */
141
166
  export async function probeSessionHealth({ port, token, timeoutMs = 800 } = {}) {
142
167
  try {
143
- const health = await request({ port, token, path: '/health', timeoutMs, control: true });
168
+ const health = await request({
169
+ port,
170
+ token,
171
+ path: '/health',
172
+ timeoutMs,
173
+ deadlineMs: Math.max(1, timeoutMs),
174
+ control: true,
175
+ });
144
176
  return health?.status === 'ok' ? health : null;
145
177
  } catch {
146
178
  return null;
147
179
  }
148
180
  }
149
181
 
182
+ /** Health probe + protocol compatibility against an explicit discovery,
183
+ * without reading daemon.json or spawning anything. `health` is null when
184
+ * the daemon does not answer (then `compatibility` is null too). */
185
+ export async function probeSessionCompatibility(discovery, { timeoutMs = 800, probeHealth = probeSessionHealth } = {}) {
186
+ const health = await probeHealth({ port: discovery?.port, token: discovery?.token, timeoutMs });
187
+ return { health, compatibility: health ? sessionDaemonCompatibility(health) : null };
188
+ }
189
+
150
190
  export function readSessionDiscovery(discoveryPath = sessionDiscoveryPath()) {
151
191
  try {
152
192
  const parsed = JSON.parse(readFileSync(discoveryPath, 'utf8'));
@@ -319,13 +359,15 @@ export function spawnDaemonCandidate({ cwd, log, timeoutMs = 30_000, entry = dae
319
359
  });
320
360
  }
321
361
 
322
- /** Spawn-or-attach discovery for the machine-global daemon. */
362
+ /** Spawn-or-attach discovery for the machine-global daemon.
363
+ * `entry` is a test seam; production always forks the real daemon. */
323
364
  export async function ensureDaemon({
324
365
  cwd = process.cwd(),
325
366
  log = () => {},
326
367
  attempts = 5,
327
368
  readyTimeoutMs = null,
328
369
  onUpgradeWait = null,
370
+ entry = daemonEntry(),
329
371
  } = {}) {
330
372
  const configuredTimeoutMs = Number(process.env.MIXDOG_DAEMON_READY_TIMEOUT_MS);
331
373
  let requestedTimeoutMs = DEFAULT_DAEMON_READY_TIMEOUT_MS;
@@ -338,6 +380,11 @@ export async function ensureDaemon({
338
380
  let spawnAttempts = 0;
339
381
  let waitingOwnerPid = 0;
340
382
  let waitingDrainPid = 0;
383
+ let staleOwnerPid = 0;
384
+ // What the passes observed, for the error when no endpoint ever appears.
385
+ let unansweredDiscovery = null;
386
+ let waitedOwner = null;
387
+ let lastCandidate = null;
341
388
  const startedAt = Date.now();
342
389
  const elapsed = () => Date.now() - startedAt;
343
390
  while (Date.now() < deadline) {
@@ -352,7 +399,7 @@ export async function ensureDaemon({
352
399
  const health = await probeSessionHealth({
353
400
  port: discovery.port,
354
401
  token: discovery.token,
355
- timeoutMs: DAEMON_BOOT_PROBE_TIMEOUT_MS,
402
+ timeoutMs: Math.max(1, Math.min(DAEMON_BOOT_PROBE_TIMEOUT_MS, deadline - Date.now())),
356
403
  });
357
404
  log(
358
405
  `discovery pid=${discovery.pid} port=${discovery.port}` +
@@ -385,13 +432,35 @@ export async function ensureDaemon({
385
432
  err.sessionProtocolMismatch = true;
386
433
  throw err;
387
434
  }
435
+ unansweredDiscovery = { pid: discovery.pid, port: discovery.port };
388
436
  }
389
437
  // A concurrent launcher can win the owner lock before it publishes
390
438
  // daemon.json. That is a healthy singleton boot, not a reason to spawn five
391
- // doomed contenders and report "endpoint unavailable" after ~500 ms.
392
- const ownerState = readSingletonOwner(daemonOwnerPath());
439
+ // doomed contenders and report "endpoint unavailable" after ~500 ms. A
440
+ // live pid whose heartbeat stopped is a reused pid (daemon-owner.mjs):
441
+ // spawn, and the candidate reclaims it.
442
+ // A live-pid record whose heartbeat looks stale (or that has none: v1.1.2
443
+ // and older) is reclaimable only when its holder's endpoint does not
444
+ // answer with its pid — a crashed daemon leaves its files behind, naming a
445
+ // pid the OS may have reused. A probe cut short by this deadline proves
446
+ // nothing either way.
447
+ const proof = await proveDaemonOwnerRecord(daemonOwnerPath(), {
448
+ discoveryPath: sessionDiscoveryPath(),
449
+ budgetMs: Math.max(1, deadline - Date.now()),
450
+ });
451
+ const ownerState = readSingletonOwner(daemonOwnerPath(), {
452
+ isStale: (owner) => daemonOwnerIsStale(owner, { proof }),
453
+ });
454
+ if (ownerState.stale && Number(ownerState.owner?.pid) !== staleOwnerPid) {
455
+ staleOwnerPid = Number(ownerState.owner?.pid) || 0;
456
+ log(
457
+ `daemon owner record pid=${staleOwnerPid} heartbeatAt=${ownerState.owner?.heartbeatAt ?? '-'}` +
458
+ ` probe=${proof?.verdict ?? '-'} is stale; spawning a daemon to reclaim it`
459
+ );
460
+ }
393
461
  const ownerPid = ownerState.alive ? Number(ownerState.owner?.pid) || 0 : 0;
394
462
  if (ownerPid) {
463
+ waitedOwner = { pid: ownerPid, claimedAt: ownerState.owner?.claimedAt ?? null };
395
464
  if (ownerPid !== waitingOwnerPid) {
396
465
  waitingOwnerPid = ownerPid;
397
466
  log(`waiting for daemon owner pid=${ownerPid} to publish its session endpoint`);
@@ -400,16 +469,25 @@ export async function ensureDaemon({
400
469
  continue;
401
470
  }
402
471
  waitingOwnerPid = 0;
472
+ waitedOwner = null;
403
473
  if (spawnAttempts >= maxSpawnAttempts) break;
404
474
  spawnAttempts += 1;
405
475
  log(`spawning daemon candidate attempt=${spawnAttempts} at=${elapsed()}ms`);
406
- await spawnDaemonCandidate({
476
+ lastCandidate = await spawnDaemonCandidate({
407
477
  cwd,
408
478
  log,
409
479
  timeoutMs: Math.max(1, deadline - Date.now()),
480
+ entry,
410
481
  });
411
482
  }
412
- throw new Error('daemon session endpoint is unavailable');
483
+ throw daemonUnavailableError({
484
+ discovery: unansweredDiscovery,
485
+ owner: waitedOwner,
486
+ candidate: lastCandidate?.summary?.() ?? null,
487
+ spawnAttempts,
488
+ maxSpawnAttempts,
489
+ captureDir: daemonCrashCaptureDir(),
490
+ });
413
491
  }
414
492
 
415
493
  /** Registers this client with the daemon. Transport failures are retried
@@ -454,6 +532,12 @@ export async function attachSession({
454
532
  streamReconnectMaxMs = EVENT_STREAM_RECONNECT_MAX_MS,
455
533
  streamLivenessTimeoutMs = EVENT_STREAM_LIVENESS_TIMEOUT_MS,
456
534
  streamReconnectBudgetMs = EVENT_STREAM_RECONNECT_BUDGET_MS,
535
+ remote = false,
536
+ leaseMs = 30_000,
537
+ heartbeatMs = 10_000,
538
+ readDiscovery = remote ? () => discovery : () => readSessionDiscovery(),
539
+ pidAlive = remote ? () => true : isPidAlive,
540
+ probeHealth = probeSessionHealth,
457
541
  log = () => {},
458
542
  } = {}) {
459
543
  if (!discovery?.port || !discovery?.token) throw new Error('daemon discovery {port, token} required');
@@ -462,13 +546,15 @@ export async function attachSession({
462
546
  port,
463
547
  serverToken,
464
548
  body: {
465
- leadPid,
549
+ // A remote client's process lives on another machine: no local pid.
550
+ leadPid: remote ? 0 : leadPid,
466
551
  cwd,
467
552
  lifecycle: lifecycle !== false,
468
553
  clientKind: clientKind === 'desktop' ? 'desktop' : 'session',
469
554
  registrationId: String(registrationId || randomUUID()),
470
555
  protocol: SESSION_PROTOCOL,
471
556
  revision: SESSION_REVISION,
557
+ ...(remote ? { remote: true, leaseMs: clampLeaseMs(leaseMs) } : {}),
472
558
  },
473
559
  });
474
560
  const clientToken = reg?.token;
@@ -487,18 +573,23 @@ export async function attachSession({
487
573
  reconnectMaxMs: streamReconnectMaxMs,
488
574
  reconnectBudgetMs: streamReconnectBudgetMs,
489
575
  livenessTimeoutMs: streamLivenessTimeoutMs,
490
- readDiscovery: () => readSessionDiscovery(),
491
- probeHealth: probeSessionHealth,
492
- pidAlive: isPidAlive,
576
+ readDiscovery,
577
+ probeHealth,
578
+ pidAlive,
493
579
  log,
494
580
  });
495
581
  link.open();
582
+ const heartbeat = remote
583
+ ? createLeaseHeartbeat({ request, port, serverToken, clientToken, heartbeatMs, leaseMs, log })
584
+ : null;
585
+ heartbeat?.start();
496
586
  const { call, close } = createAttachmentCalls({
497
587
  request,
498
588
  port,
499
589
  serverToken,
500
590
  clientToken,
501
591
  stopStream: link.stop,
592
+ stopHeartbeat: heartbeat?.stop,
502
593
  log,
503
594
  });
504
595
 
@@ -13,6 +13,7 @@ export const SESSION_READ_ACTIONS = Object.freeze([
13
13
  'getChannelSetup',
14
14
  'getChannelWorkerStatus',
15
15
  'getCompactionSettings',
16
+ 'getConversationImportJobs',
16
17
  'getDeveloperSettings',
17
18
  'getDisabledSkills',
18
19
  'getMediaDefault',
@@ -40,6 +41,7 @@ export const SESSION_READ_ACTIONS = Object.freeze([
40
41
  'getLocalProviderModelDetails',
41
42
  'getSessionReviewDiff',
42
43
  'getSessionUsage',
44
+ 'getTurnChanges',
43
45
  'getTurnReviewDiff',
44
46
  'getTurnLiveness',
45
47
  'getUpdateSettings',
@@ -49,9 +51,14 @@ export const SESSION_READ_ACTIONS = Object.freeze([
49
51
  'getVoiceStatus',
50
52
  'isSetupRequestActive',
51
53
  'getWorkflowPack',
54
+ 'getWorktreeSettings',
55
+ 'getBranchMenu',
56
+ 'getProjectBranchMenu',
57
+ 'listWorktrees',
52
58
  'hooksStatus',
53
59
  'inheritancePreflight',
54
60
  'listAgents',
61
+ 'listImportableConversations',
55
62
  'listMediaAssets',
56
63
  'listMediaLanes',
57
64
  'listOutputStyles',
@@ -82,6 +89,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
82
89
  'cacheMediaThumbnail',
83
90
  'cancelMediaJob',
84
91
  'cancelOAuthProviderLogin',
92
+ 'cancelConversationImport',
85
93
  'checkForUpdate',
86
94
  'clear',
87
95
  'compact',
@@ -113,6 +121,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
113
121
  'pushNotice',
114
122
  'recall',
115
123
  'reconnectMcp',
124
+ 'registerConversationImportFile',
116
125
  'reloadPlugins',
117
126
  'reloadSkills',
118
127
  'rememberPromptHistory',
@@ -132,6 +141,8 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
132
141
  'restoreQueued',
133
142
  'revertTurnReview',
134
143
  'revertTurnReviewFile',
144
+ 'revertTurnChanges',
145
+ 'reapplyTurnChanges',
135
146
  'rewindToItem',
136
147
  'runDoctor',
137
148
  'runScheduleNow',
@@ -191,7 +202,9 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
191
202
  'setOrchestrationMode',
192
203
  'saveStarterGuideState',
193
204
  'skipOnboarding',
205
+ 'startConversationImport',
194
206
  'startMediaJob',
207
+ 'stopShellJob',
195
208
  'switchContext',
196
209
  'taskControl',
197
210
  'testWebhook',
@@ -202,6 +215,14 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
202
215
  'transcribeAudio',
203
216
  'prepareTranscription',
204
217
  'updatePlugin',
218
+ 'setWorktreeSettings',
219
+ 'selectBranch',
220
+ 'selectProjectBranch',
221
+ 'mergeProjectBranch',
222
+ 'requestWorktree',
223
+ 'mergeWorkspace',
224
+ 'removeWorktree',
225
+ 'pruneWorktrees',
205
226
  ]);
206
227
 
207
228
  export const SESSION_READ_ACTION_SET = new Set(SESSION_READ_ACTIONS);
@@ -35,6 +35,9 @@ export function createSessionProxyFactory({ attachSession, ensureDaemon, closeId
35
35
  toolMode: options.toolMode || 'full',
36
36
  remote: options.remote === true,
37
37
  desktopSession: options.desktopSession ?? null,
38
+ // Worktree intent: `--worktree [name]` / `--no-worktree`, or `{ mode: 'default' }`
39
+ // (the TUI's interactive Lead follows the settings default); absent: none.
40
+ ...(options.worktree ? { worktree: options.worktree } : {}),
38
41
  };
39
42
  const attachment = await pool.ensure({ cwd, log });
40
43
  const created = await attachment.client.create(openParams, {
@@ -36,8 +36,11 @@ export function createAgentChildFactory({ registry, rehydrateAgentSessions, crea
36
36
  model,
37
37
  effort: preset.effort,
38
38
  fast: preset.fast === true,
39
+ // Explicit, so Main's modelSettings bucket cannot decide the agent's Auto effort.
40
+ autoEffort: preset.autoEffort !== false,
39
41
  modelParameters: preset.modelParameters,
40
42
  contextPercent: spec.contextPercent,
43
+ contextTokens: spec.contextTokens,
41
44
  selectedContextWindow: spec.selectedContextWindow,
42
45
  toolMode: 'full',
43
46
  sessionProfile,
@@ -51,6 +54,7 @@ export function createAgentChildFactory({ registry, rehydrateAgentSessions, crea
51
54
  presetName: preset.id || preset.name || null,
52
55
  effort: preset.effort || null,
53
56
  fast: preset.fast === true,
57
+ ...(preset.autoEffort === false ? { autoEffort: false } : {}),
54
58
  createdAt: Date.now(),
55
59
  updatedAt: Date.now(),
56
60
  status: 'idle',
@@ -79,6 +79,7 @@ function rehydratedDescriptor(candidate, ownerSessionId) {
79
79
  presetName: row.presetName || row.preset || row.profileId || null,
80
80
  effort: row.effort || null,
81
81
  fast: row.fast === true,
82
+ ...(row.autoEffort === false ? { autoEffort: false } : {}),
82
83
  modelParameters: row.modelParameters || null,
83
84
  taskType: row.taskType || null,
84
85
  permission: row.permission || null,
@@ -1,6 +1,7 @@
1
1
  // Creation of one daemon-owned execution entry around an injected runtime;
2
2
  // disposal lives in ./disposal.mjs. Entries are never addressed by clients;
3
3
  // sessionId is the only identity outside the service.
4
+ import { syncEntryWorkspace } from '../projection/workspace.mjs';
4
5
  import { createEntryDisposal } from './disposal.mjs';
5
6
 
6
7
  export function createEntryLifecycle({
@@ -46,17 +47,21 @@ export function createEntryLifecycle({
46
47
  model: params.model,
47
48
  effort: params.effort,
48
49
  fast: params.fast,
50
+ autoEffort: params.autoEffort,
49
51
  modelParameters: params.modelParameters,
50
52
  contextPercent: params.contextPercent,
53
+ contextTokens: params.contextTokens,
51
54
  selectedContextWindow: params.selectedContextWindow,
52
55
  toolMode: params.toolMode || 'full',
53
56
  remote: params.remote === true,
54
57
  desktopSession: params.desktopSession ?? null,
55
58
  sessionProfile: params.sessionProfile ?? null,
59
+ worktree: params.worktree ?? null,
56
60
  });
57
61
  const entry = {
58
62
  runtime,
59
63
  cwd: params.cwd || process.cwd(),
64
+ workspaceRevision: null,
60
65
  timer: null,
61
66
  disposed: false,
62
67
  unsubscribe: null,
@@ -79,6 +84,7 @@ export function createEntryLifecycle({
79
84
  } catch {
80
85
  initialState = { busy: true };
81
86
  }
87
+ syncEntryWorkspace(entry, initialState);
82
88
  indexSessionEntry(entry, initialState.sessionId);
83
89
  updateEntryBusy(entry, initialState);
84
90
  addSubscriber(entry, ctx);
@@ -5,6 +5,7 @@
5
5
  import { diffSessionState } from '../../session-state-patch.mjs';
6
6
  import { projectSessionState } from '../../session-state-projection.mjs';
7
7
  import { windowTranscriptSnapshot } from './transcript-window.mjs';
8
+ import { syncEntryWorkspace } from './workspace.mjs';
8
9
 
9
10
  function snapshotOf(entry) {
10
11
  const raw = entry.runtime.getState?.() ?? null;
@@ -127,6 +128,7 @@ export function createRevisionSteps({ revisionEpoch, index, updateEntryBusy, log
127
128
  };
128
129
  }
129
130
  const snapshot = current;
131
+ syncEntryWorkspace(entry, snapshot);
130
132
  if (!addressedSessionId && projectedSessionId) {
131
133
  entry.addressedSessionId = projectedSessionId;
132
134
  }
@@ -97,10 +97,7 @@ function budgetedStart(items, end, maxItems, byteBudget, minItems) {
97
97
  * goes below TRANSCRIPT_WINDOW_MIN_ITEMS. */
98
98
  function tailWindowStart(items, { limit, byteBudget = null, pageBase = 0 }) {
99
99
  const held = Math.min(pageBase || 0, limit, items.length);
100
- if (held > 0) {
101
- return budgetedStart(items, items.length - held, limit - held, byteBudget, TRANSCRIPT_WINDOW_MIN_ITEMS);
102
- }
103
- return budgetedStart(items, items.length, limit, byteBudget, TRANSCRIPT_WINDOW_MIN_ITEMS);
100
+ return budgetedStart(items, items.length - held, limit - held, byteBudget, TRANSCRIPT_WINDOW_MIN_ITEMS);
104
101
  }
105
102
 
106
103
  /** A stored projection already holds at most `window.limit` items; the byte
@@ -122,6 +119,22 @@ function restoredAfterFirstMessage(item) {
122
119
  return Boolean(match) && Number(match[1]) > 0;
123
120
  }
124
121
 
122
+ /** The compaction-archive segments (`{ after, before }`) a resumed runtime
123
+ * holds none of, or null. */
124
+ function runtimeArchiveRange(state) {
125
+ const range = state?.transcriptArchive;
126
+ const after = Number(range?.after);
127
+ const before = Number(range?.before);
128
+ return Number.isInteger(after) && Number.isInteger(before) && before - 1 > after ? { after, before } : null;
129
+ }
130
+
131
+ function resetLoadedHead(view) {
132
+ view.head = [];
133
+ view.headOlder = null;
134
+ view.headSource = null;
135
+ view.archiveHeld = 0;
136
+ }
137
+
125
138
  /** Fold a read/subscribe request into a live entry's window. */
126
139
  export function requestLiveTranscriptWindow(entry, request) {
127
140
  const view = entry.transcriptView;
@@ -143,6 +156,10 @@ export function requestLiveTranscriptWindow(entry, request) {
143
156
  paged: false,
144
157
  head: [],
145
158
  headOlder: null,
159
+ // Where the loaded head came from: 'stored' (durable history aligned by
160
+ // restore ids) or 'archive' (compaction-archive pages).
161
+ headSource: null,
162
+ archiveHeld: 0,
146
163
  cache: null,
147
164
  };
148
165
  return;
@@ -183,14 +200,12 @@ export function windowTranscriptSnapshot(entry, wire) {
183
200
  // rewind, clear): follow the anchor, or restart from the tail.
184
201
  const moved = items.findIndex((item) => item?.id === view.anchorId);
185
202
  start = moved < 0 ? null : moved;
186
- view.head = [];
187
- view.headOlder = null;
203
+ resetLoadedHead(view);
188
204
  }
189
205
  if (start !== null && start > items.length) start = null;
190
206
  if (start === null) {
191
207
  start = tailWindowStart(items, view);
192
- view.head = [];
193
- view.headOlder = null;
208
+ resetLoadedHead(view);
194
209
  }
195
210
  // The sticky start lets appends travel as suffix patches, so an unpaged
196
211
  // tail grows past its byte budget while views stay attached (a worker
@@ -210,7 +225,8 @@ export function windowTranscriptSnapshot(entry, wire) {
210
225
  }
211
226
  view.start = start;
212
227
  view.anchorId = items[start]?.id;
213
- const older = start > 0 || (view.headOlder ?? restoredAfterFirstMessage(view.head[0] ?? items[0]));
228
+ const older =
229
+ start > 0 || (view.headOlder ?? headSourceFor(view.head[0] ?? items[0], runtimeArchiveRange(wire)) !== null);
214
230
  const cache = view.cache;
215
231
  if (cache && cache.wire === wire && cache.start === start && cache.head === view.head && cache.older === older) {
216
232
  return cache.value;
@@ -223,10 +239,96 @@ export function windowTranscriptSnapshot(entry, wire) {
223
239
  return value;
224
240
  }
225
241
 
226
- /** Load durable history OLDER than the runtime's first item when a grown
227
- * window reaches past what the runtime holds (a resumed runtime restores
228
- * only a bounded tail). Items align by their deterministic restore ids.
229
- * Returns whether durable history was read. */
242
+ /** Where older rows above `first` come from: durable history aligned by
243
+ * restore ids (which continues into the compaction archive), or — once the
244
+ * runtime's rows are not restored ones (a live compaction archived them, or
245
+ * nothing was restored) — compaction-archive pages alone. */
246
+ function headSourceFor(first, archive) {
247
+ const restoredId = RESTORED_ITEM_ID.test(String(first?.id ?? ''));
248
+ if (restoredId && first?.archived !== true) return restoredAfterFirstMessage(first) || archive ? 'stored' : null;
249
+ if (archive) return 'archive';
250
+ // A compaction archived the first loaded restored row, but the runtime holds
251
+ // no archive range: older rows still align through durable history.
252
+ return restoredId && restoredAfterFirstMessage(first) ? 'stored' : null;
253
+ }
254
+
255
+ /** Whether a live row shows that a compaction rewrote this runtime's rows. */
256
+ function isCompactionMark(item) {
257
+ return item?.archived === true || (item?.kind === 'statusdone' && item.status === 'compacted');
258
+ }
259
+
260
+ /** The runtime's compaction generation: it only grows, because every
261
+ * compaction adds a divider and archives rows. A loaded head is valid only
262
+ * for the generation it was loaded in. */
263
+ function compactionGeneration(items) {
264
+ return items.reduce((count, item) => count + (isCompactionMark(item) ? 1 : 0), 0);
265
+ }
266
+
267
+ /** Where `first` sits in durable history, or -1. Restore ids are positional
268
+ * and only comparable while no compaction touched the live rows (or when
269
+ * `first` itself came from this durable history). Across a compaction the
270
+ * live `hist_…` ids and the durable `arch…`/renumbered ids describe
271
+ * different positions, so nothing but one verified stable identity aligns. */
272
+ function alignedHistoryIndex(history, first, live, fromHistory) {
273
+ if (!first) return -1;
274
+ if (fromHistory || !live.some(isCompactionMark)) return history.findIndex((item) => item?.id === first.id);
275
+ const index = verifiedIdentityIndex(history, first, live);
276
+ return index >= 0 && !prefixRepeatsLiveRow(history.slice(0, index), live) ? index : -1;
277
+ }
278
+
279
+ /** Whether a proposed older prefix holds a row whose stable identity (kind +
280
+ * `messageId`, or a tool call id) the live window already shows: joining it
281
+ * would show that row twice. */
282
+ function prefixRepeatsLiveRow(prefix, live) {
283
+ const messages = new Set();
284
+ const calls = new Set();
285
+ for (const item of live) {
286
+ if (item?.messageId) messages.add(`${item.kind}\0${item.messageId}`);
287
+ for (const callId of toolCallIds(item)) calls.add(callId);
288
+ }
289
+ return prefix.some(
290
+ (item) =>
291
+ (item?.messageId && messages.has(`${item.kind}\0${item.messageId}`)) ||
292
+ toolCallIds(item).some((callId) => calls.has(callId))
293
+ );
294
+ }
295
+
296
+ /** The tool call ids a tool row carries (single card or aggregate). */
297
+ function toolCallIds(item) {
298
+ const ids = Array.isArray(item?.callIds) ? item.callIds : [];
299
+ return item?.callId ? [item.callId, ...ids] : ids;
300
+ }
301
+
302
+ /** The one durable row that is the copy of live row `first`, by stable
303
+ * identity only: same kind and `messageId` for message rows; for tool rows
304
+ * every call id of `first` is in the durable row, which holds no call the
305
+ * live window lacks (an archive aggregate merging a call that is not live
306
+ * would otherwise drop it). No identity, no match, or more than one match:
307
+ * -1 — nothing is guessed. */
308
+ function verifiedIdentityIndex(history, first, live) {
309
+ const calls = toolCallIds(first);
310
+ const liveCalls = new Set(live.flatMap(toolCallIds));
311
+ const matches = [];
312
+ history.forEach((item, index) => {
313
+ if (item?.kind !== first.kind) return;
314
+ if (calls.length) {
315
+ const own = toolCallIds(item);
316
+ if (calls.every((callId) => own.includes(callId)) && own.every((callId) => liveCalls.has(callId))) {
317
+ matches.push(index);
318
+ }
319
+ } else if (first.kind !== 'tool' && first.messageId && item.messageId === first.messageId) {
320
+ matches.push(index);
321
+ }
322
+ });
323
+ return matches.length === 1 ? matches[0] : -1;
324
+ }
325
+
326
+ /** Load history OLDER than the runtime's first item when a grown window
327
+ * reaches past what the runtime holds (a resumed runtime restores only a
328
+ * bounded tail; a compaction moved earlier rows to the display-only
329
+ * archive). Durable items align by their deterministic restore ids; archive
330
+ * pages continue below the archived rows already held. Returns whether
331
+ * durable history was read. */
230
332
  export async function loadLiveTranscriptHead(entry, sessionId, readStoredSession) {
231
333
  const view = entry.transcriptView;
232
334
  if (!view || view.full || !view.paged || typeof readStoredSession !== 'function' || view.headOlder === false) {
@@ -235,24 +337,52 @@ export async function loadLiveTranscriptHead(entry, sessionId, readStoredSession
235
337
  // Durable history continues the window only once it shows the runtime's
236
338
  // first item; a budgeted page may still be revealing runtime rows.
237
339
  if (view.start !== 0) return false;
238
- const items = entry.runtime?.getState?.()?.items;
239
- const runtimeItems = Array.isArray(items) ? items : [];
340
+ const state = entry.runtime?.getState?.();
341
+ const runtimeItems = Array.isArray(state?.items) ? state.items : [];
240
342
  const missing = view.limit - runtimeItems.length - view.head.length;
241
343
  const first = view.head[0] ?? runtimeItems[0];
242
- if (missing <= 0 || !restoredAfterFirstMessage(first)) return false;
344
+ const archive = runtimeArchiveRange(state);
345
+ const source = view.headSource || headSourceFor(first, archive);
346
+ if (missing <= 0 || !source) return false;
347
+ const generation = compactionGeneration(runtimeItems);
348
+ if (view.head.length && view.headGeneration !== generation) {
349
+ // A compaction since the head was loaded: keep the rows as they are, and
350
+ // never join older rows across the generation.
351
+ view.headOlder = false;
352
+ return true;
353
+ }
243
354
  const pageBudget = view.grow ? view.grow.byteBudget : null;
244
- const stored = await readStoredSession(sessionId, { transcriptItemLimit: view.limit + runtimeItems.length });
245
- if (entry.transcriptView !== view || (view.head[0] ?? entry.runtime?.getState?.()?.items?.[0]) !== first) {
355
+ const currentItems = () => entry.runtime?.getState?.()?.items;
356
+ const stillCurrent = () =>
357
+ entry.transcriptView === view &&
358
+ (view.head[0] ?? currentItems()?.[0]) === first &&
359
+ compactionGeneration(Array.isArray(currentItems()) ? currentItems() : []) === generation;
360
+ if (source === 'archive') {
361
+ const page = await readStoredSession(sessionId, {
362
+ compactionArchive: { ...archive, skip: view.archiveHeld || 0, limit: missing },
363
+ });
364
+ if (!stillCurrent()) return true;
365
+ const rows = Array.isArray(page?.items) ? page.items : [];
366
+ const from = budgetedStart(rows, rows.length, missing, pageBudget, TRANSCRIPT_WINDOW_MIN_ITEMS);
367
+ view.head = sanitizeForWire(rows.slice(from)).concat(view.head);
368
+ view.archiveHeld = (view.archiveHeld || 0) + rows.length - from;
369
+ view.headSource = 'archive';
370
+ view.headGeneration = generation;
371
+ view.headOlder = from > 0 || page?.hasOlder === true;
246
372
  return true;
247
373
  }
374
+ const stored = await readStoredSession(sessionId, { transcriptItemLimit: view.limit + runtimeItems.length });
375
+ if (!stillCurrent()) return true;
248
376
  const history = Array.isArray(stored?.items) ? stored.items : [];
249
- const overlap = history.findIndex((item) => item?.id === first.id);
377
+ const overlap = alignedHistoryIndex(history, first, view.head.concat(runtimeItems), view.head.length > 0);
250
378
  if (overlap < 0) {
251
379
  view.headOlder = false;
252
380
  return true;
253
381
  }
254
382
  const from = budgetedStart(history, overlap, missing, pageBudget, TRANSCRIPT_WINDOW_MIN_ITEMS);
255
383
  view.head = sanitizeForWire(history.slice(from, overlap)).concat(view.head);
384
+ view.headSource = 'stored';
385
+ view.headGeneration = generation;
256
386
  view.headOlder = from > 0 || stored.transcriptHasOlder === true;
257
387
  return true;
258
388
  }
@@ -0,0 +1,14 @@
1
+ // Live execution root of a daemon entry. `entry.cwd` is captured at creation;
2
+ // a workspace transition moves the session's cwd without recreating the entry,
3
+ // so the projected `state.workspace` (cwd + revision) is the authority and the
4
+ // entry mirrors it every time a state is projected.
5
+
6
+ /** Mirror `state.workspace` onto the entry; states without one change nothing. */
7
+ export function syncEntryWorkspace(entry, state) {
8
+ const workspace = state?.workspace;
9
+ if (!entry || !workspace || typeof workspace !== 'object') return;
10
+ if (typeof workspace.cwd === 'string' && workspace.cwd) entry.cwd = workspace.cwd;
11
+ if (Number.isSafeInteger(workspace.revision) && workspace.revision >= 0) {
12
+ entry.workspaceRevision = workspace.revision;
13
+ }
14
+ }