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
@@ -5,7 +5,7 @@ import { SESSION_CONFIGURE_ACTION_SET, SESSION_READ_ACTION_SET } from '../../ses
5
5
  import { sanitizeForWire } from '../../session-wire-values.mjs';
6
6
 
7
7
  /** Reads a session without a live runtime answers from disk (readStoredReview). */
8
- const STORED_REVIEW_ACTIONS = new Set(['getTurnReviewDiff', 'getSessionReviewDiff']);
8
+ const STORED_REVIEW_ACTIONS = new Set(['getTurnReviewDiff', 'getSessionReviewDiff', 'getTurnChanges']);
9
9
 
10
10
  /** One compact description of an action result for the daemon log, never the payload itself. */
11
11
  function resultSummary(value) {
@@ -22,8 +22,16 @@ function requireSessionAction(action, allowed) {
22
22
  }
23
23
 
24
24
  export function createSessionActionCalls(ctx) {
25
- const { isClosed, log, listSessions, getRemoteSessionState, loadProjectStore, advanceForCaller, readStoredReview, responseSessionId } =
26
- ctx;
25
+ const {
26
+ isClosed,
27
+ log,
28
+ listSessions,
29
+ getRemoteSessionState,
30
+ loadProjectStore,
31
+ advanceForCaller,
32
+ readStoredReview,
33
+ responseSessionId,
34
+ } = ctx;
27
35
  const { bodyForClient } = ctx.projection;
28
36
  const { retainUnwatched } = ctx.retention;
29
37
  const { assertAvailable, hostedEntryForSession, entryForSession } = ctx.entries;
@@ -93,15 +101,20 @@ export function createSessionActionCalls(ctx) {
93
101
  if (typeof listSessions !== 'function') {
94
102
  throw new Error('session catalog is unavailable');
95
103
  }
96
- const sessions = await listSessions({
104
+ const listed = await listSessions({
97
105
  ...(options || {}),
98
106
  // Agent-only records are an internal ancestry/reuse source, never part
99
107
  // of the ordinary session catalog returned over the public transport.
100
108
  includeAgentOnly: false,
101
109
  });
110
+ // A `page: { limit, cursor }` request answers one page: { sessions, nextCursor }.
111
+ const page = listed && !Array.isArray(listed) && Array.isArray(listed.sessions) ? listed : null;
112
+ const sessions = page ? page.sessions : listed;
102
113
  const remoteSession = typeof getRemoteSessionState === 'function' ? await getRemoteSessionState() : null;
114
+ const nextCursor = typeof page?.nextCursor === 'string' ? page.nextCursor : null;
103
115
  return {
104
116
  sessions: sanitizeForWire(Array.isArray(sessions) ? sessions : []),
117
+ ...(page ? { nextCursor } : {}),
105
118
  remoteSession: sanitizeForWire(remoteSession) ?? null,
106
119
  };
107
120
  }
@@ -91,14 +91,27 @@ export function createCallRoute({ handleCall, clients, lanes, callCache, nowMs }
91
91
  // tell a failed CALL from a dead TRANSPORT (which must re-attach). The
92
92
  // machine-readable `code` (ECALLIDCONFLICT / ECALLRESULTDROPPED) travels
93
93
  // with it: a client must be able to branch on it, not parse prose.
94
+ // Typed (worktree) failures also carry their plain JSON `details`.
94
95
  sendJson(
95
96
  res,
96
97
  {
97
98
  error: err?.message || String(err),
98
99
  ...(err?.code ? { code: String(err.code) } : {}),
100
+ ...jsonDetails(err),
99
101
  },
100
102
  200
101
103
  );
102
104
  }
103
105
  };
104
106
  }
107
+
108
+ /** `{ details }` when the error's details are a JSON-safe object, else {}. */
109
+ function jsonDetails(err) {
110
+ const details = err?.details;
111
+ if (!details || typeof details !== 'object') return {};
112
+ try {
113
+ return { details: JSON.parse(JSON.stringify(details)) };
114
+ } catch {
115
+ return {};
116
+ }
117
+ }
@@ -5,11 +5,44 @@
5
5
  */
6
6
  import { isPidAlive } from '../../../runtime/shared/pid-liveness.mjs';
7
7
 
8
- export function createClientGrace({ clients, log, clientGraceMs, sweepMs, onClientsEmpty, isClosed, dropClient }) {
8
+ const LEASE_MIN_MS = 3_000;
9
+ const LEASE_MAX_MS = 600_000;
10
+ const LEASE_DEFAULT_MS = 30_000;
11
+ const REMOTE_CLIENT_GRACE_DEFAULT_MS = 1_800_000;
12
+
13
+ /** A remote client's lease: the requested `leaseMs`, clamped to 3s..10min. */
14
+ export function clampLeaseMs(value) {
15
+ const leaseMs = Number(value);
16
+ if (!Number.isFinite(leaseMs) || leaseMs <= 0) return LEASE_DEFAULT_MS;
17
+ return Math.min(LEASE_MAX_MS, Math.max(LEASE_MIN_MS, leaseMs));
18
+ }
19
+
20
+ // A remote desktop behind a flaky tunnel needs far longer to come back than a
21
+ // local terminal restarting. Read at arm time so the env can change per run.
22
+ function remoteClientGraceMs() {
23
+ const configured = Number(process.env.MIXDOG_REMOTE_CLIENT_GRACE_MS);
24
+ return Number.isFinite(configured) && configured >= 0 && process.env.MIXDOG_REMOTE_CLIENT_GRACE_MS
25
+ ? configured
26
+ : REMOTE_CLIENT_GRACE_DEFAULT_MS;
27
+ }
28
+
29
+ export function createClientGrace({
30
+ clients,
31
+ log,
32
+ clientGraceMs,
33
+ sweepMs,
34
+ nowMs = () => Date.now(),
35
+ onClientsEmpty,
36
+ isClosed,
37
+ dropClient,
38
+ }) {
9
39
  let graceTimer = null;
10
40
  let sweepTimer = null;
11
41
  let everHadLifecycleClient = false;
12
42
  let signalled = false;
43
+ // Whether the most recently departed lifecycle client was remote; it picks
44
+ // the empty-clients grace.
45
+ let lastLifecycleWasRemote = false;
13
46
 
14
47
  function lifecycleClientCount() {
15
48
  let count = 0;
@@ -45,10 +78,14 @@ export function createClientGrace({ clients, log, clientGraceMs, sweepMs, onClie
45
78
  try {
46
79
  onClientsEmpty();
47
80
  } catch {}
48
- }, clientGraceMs);
81
+ }, lastLifecycleWasRemote ? remoteClientGraceMs() : clientGraceMs);
49
82
  graceTimer.unref?.();
50
83
  }
51
84
 
85
+ function noteLifecycleDropped(client) {
86
+ lastLifecycleWasRemote = client.remote === true;
87
+ }
88
+
52
89
  /** A lifecycle client holds the daemon up: remember one ever attached and
53
90
  * cancel any pending grace. */
54
91
  function noteLifecycleClient() {
@@ -61,6 +98,11 @@ export function createClientGrace({ clients, log, clientGraceMs, sweepMs, onClie
61
98
  if (sweepTimer || isClosed()) return;
62
99
  sweepTimer = setInterval(() => {
63
100
  for (const [token, c] of [...clients]) {
101
+ if (c.remote) {
102
+ // Its owning process is on another machine: liveness is the lease.
103
+ if (nowMs() - c.lastSeen > c.leaseMs) dropClient(token, 'lease expired');
104
+ continue;
105
+ }
64
106
  // A client whose owning process died can never read its stream again.
65
107
  if (c.leadPid && !isPidAlive(c.leadPid)) dropClient(token, 'lead pid gone');
66
108
  }
@@ -77,5 +119,5 @@ export function createClientGrace({ clients, log, clientGraceMs, sweepMs, onClie
77
119
  }
78
120
  }
79
121
 
80
- return { lifecycleClientCount, cancel, maybeArm, noteLifecycleClient, startSweep, stopTimers };
122
+ return { lifecycleClientCount, cancel, maybeArm, noteLifecycleClient, noteLifecycleDropped, startSweep, stopTimers };
81
123
  }
@@ -31,6 +31,7 @@ export function createRegistrationReplays({ clients, log }) {
31
31
  replay.leadPid === identity.leadPid &&
32
32
  replay.cwd === identity.cwd &&
33
33
  replay.lifecycle === identity.lifecycle &&
34
+ replay.remote === identity.remote &&
34
35
  replay.clientKind === identity.clientKind &&
35
36
  replay.revision === identity.revision;
36
37
  if (sameIdentity && clients.has(replay.token)) {
@@ -7,7 +7,7 @@
7
7
  import { randomUUID } from 'node:crypto';
8
8
  import { parsePid } from '../../runtime/shared/pid-liveness.mjs';
9
9
  import { createRegistrationReplays } from './client-registry/registration-replays.mjs';
10
- import { createClientGrace } from './client-registry/client-grace.mjs';
10
+ import { createClientGrace, clampLeaseMs } from './client-registry/client-grace.mjs';
11
11
 
12
12
  export function createClientRegistry({
13
13
  log,
@@ -18,7 +18,7 @@ export function createClientRegistry({
18
18
  onClientRegistered,
19
19
  onClientDropped,
20
20
  }) {
21
- // token -> { token, leadPid, cwd, lifecycle, sse, pending, lastSeen }
21
+ // token -> { token, leadPid, cwd, lifecycle, remote, leaseMs, sse, pending, lastSeen }
22
22
  const clients = new Map();
23
23
  let closed = false;
24
24
  const replays = createRegistrationReplays({ clients, log });
@@ -27,6 +27,7 @@ export function createClientRegistry({
27
27
  log,
28
28
  clientGraceMs,
29
29
  sweepMs,
30
+ nowMs,
30
31
  onClientsEmpty,
31
32
  isClosed: () => closed,
32
33
  dropClient: (token, reason) => dropClient(token, reason),
@@ -35,6 +36,7 @@ export function createClientRegistry({
35
36
  function removeClientRecord(token) {
36
37
  const c = clients.get(token);
37
38
  if (!c) return;
39
+ if (c.lifecycle) grace.noteLifecycleDropped(c);
38
40
  try {
39
41
  c.sse?.end?.();
40
42
  } catch {}
@@ -57,6 +59,14 @@ export function createClientRegistry({
57
59
  grace.maybeArm(reason);
58
60
  }
59
61
 
62
+ /** Refresh a client's lease clock (heartbeat). False for an unknown token. */
63
+ function touch(token) {
64
+ const c = clients.get(token);
65
+ if (!c) return false;
66
+ c.lastSeen = nowMs();
67
+ return true;
68
+ }
69
+
60
70
  function register({
61
71
  leadPid,
62
72
  cwd,
@@ -64,11 +74,14 @@ export function createClientRegistry({
64
74
  clientKind = 'session',
65
75
  registrationId = null,
66
76
  revision = 0,
77
+ remote = false,
78
+ leaseMs,
67
79
  } = {}) {
68
80
  const identity = {
69
81
  leadPid: parsePid(leadPid) || 0,
70
82
  cwd: cwd || null,
71
83
  lifecycle: lifecycle !== false,
84
+ remote: remote === true,
72
85
  clientKind: clientKind === 'desktop' ? 'desktop' : 'session',
73
86
  revision: Math.max(0, Number(revision) || 0),
74
87
  };
@@ -79,6 +92,7 @@ export function createClientRegistry({
79
92
  clients.set(token, {
80
93
  token,
81
94
  ...identity,
95
+ leaseMs: clampLeaseMs(leaseMs),
82
96
  sse: null,
83
97
  // True while the socket asked us to stop writing (see writeFrame).
84
98
  paused: false,
@@ -92,7 +106,8 @@ export function createClientRegistry({
92
106
  grace.startSweep();
93
107
  log(
94
108
  `client registered token=${token} lead=${identity.leadPid} cwd=${identity.cwd || '-'}` +
95
- ` lifecycle=${identity.lifecycle} kind=${identity.clientKind}`
109
+ ` lifecycle=${identity.lifecycle} kind=${identity.clientKind}` +
110
+ (identity.remote ? ` remote leaseMs=${clients.get(token).leaseMs}` : '')
96
111
  );
97
112
  if (typeof onClientRegistered === 'function') {
98
113
  try {
@@ -102,6 +117,7 @@ export function createClientRegistry({
102
117
  cwd: identity.cwd,
103
118
  lifecycle: identity.lifecycle,
104
119
  clientKind: identity.clientKind,
120
+ ...(identity.remote ? { remote: true } : {}),
105
121
  });
106
122
  } catch {}
107
123
  }
@@ -124,6 +140,7 @@ export function createClientRegistry({
124
140
  forgetReplaysFor: replays.forgetFor,
125
141
  dropClient,
126
142
  register,
143
+ touch,
127
144
  close,
128
145
  };
129
146
  }
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * session-transport/routes.mjs — the HTTP front door: /health (identity
3
3
  * travels with every probe), the daemon-token gate, client register /
4
- * deregister, the /events SSE attach, /call, /shutdown and the /upgrade
4
+ * deregister / heartbeat, the /events SSE attach, /call, /shutdown, /stop and the /upgrade
5
5
  * handshake a newer build uses to replace this daemon.
6
6
  */
7
+ import { readFileSync } from 'node:fs';
7
8
  import { sendJson, sendError } from '../../runtime/memory/lib/http-wire.mjs';
9
+ import { modelProxyClientMode } from '../../runtime/shared/model-proxy-mode.mjs';
8
10
  import {
9
11
  compareRuntimeVersions,
10
12
  SESSION_CAPABILITY_FINGERPRINT,
@@ -13,6 +15,32 @@ import {
13
15
  runtimeVersion,
14
16
  } from '../session-wire.mjs';
15
17
 
18
+ let installBuildHash;
19
+ /** Build hash of a desktop-installed server runtime: the `.install-complete`
20
+ * marker the installer writes at the package root. Null for any other
21
+ * install (npm, source checkout, packaged desktop). */
22
+ function remoteInstallBuildHash() {
23
+ if (installBuildHash !== undefined) return installBuildHash;
24
+ try {
25
+ const marker = JSON.parse(readFileSync(new URL('../../../.install-complete', import.meta.url), 'utf8'));
26
+ installBuildHash = typeof marker?.buildHash === 'string' && marker.buildHash ? marker.buildHash : null;
27
+ } catch {
28
+ installBuildHash = null;
29
+ }
30
+ return installBuildHash;
31
+ }
32
+
33
+ /** Turns in flight: what makes a daemon too busy to replace without asking. */
34
+ function busySessionCount(status) {
35
+ return Math.max(0, Number(status?.busy) || 0);
36
+ }
37
+
38
+ /** Leave through the embedder's own graceful SIGTERM path. */
39
+ function requestProcessStop() {
40
+ if (process.listenerCount('SIGTERM') > 0) process.emit('SIGTERM', 'SIGTERM');
41
+ else process.kill(process.pid, 'SIGTERM');
42
+ }
43
+
16
44
  export function createTransportRoutes({
17
45
  serverToken,
18
46
  drain,
@@ -25,11 +53,23 @@ export function createTransportRoutes({
25
53
  transportMemory,
26
54
  onClientsEmpty,
27
55
  onUpgradeRequested,
56
+ admitWork = () => true,
28
57
  }) {
58
+ // Checked on every register/call, last right before the work is handed on
59
+ // (after the body is read): a daemon that lost (or cannot re-verify) its
60
+ // owner lease serves nothing.
61
+ const refuseUnadmitted = (res) => {
62
+ if (admitWork()) return false;
63
+ sendError(res, 'daemon owner lease is not held', 503);
64
+ return true;
65
+ };
66
+
29
67
  const health = (res) => {
30
68
  // Identity travels with EVERY health probe: a view negotiates version
31
69
  // skew before it attaches, so an embedder can never forget to publish
32
70
  // it and leave clients guessing.
71
+ const status = getStatus();
72
+ const busySessions = busySessionCount(status);
33
73
  sendJson(res, {
34
74
  status: 'ok',
35
75
  pid: process.pid,
@@ -38,10 +78,15 @@ export function createTransportRoutes({
38
78
  protocol: SESSION_PROTOCOL,
39
79
  revision: SESSION_REVISION,
40
80
  capabilityFingerprint: SESSION_CAPABILITY_FINGERPRINT,
81
+ modelProxy: modelProxyClientMode() ? 'client' : null,
41
82
  draining: drain.reason || null,
42
83
  drainCommitted: drain.committed,
43
84
  version: runtimeVersion(),
44
- ...getStatus(),
85
+ packageVersion: runtimeVersion(),
86
+ buildHash: remoteInstallBuildHash(),
87
+ ...status,
88
+ busySessions,
89
+ idle: busySessions === 0,
45
90
  activeCalls: lanes.active,
46
91
  queuedCalls: lanes.queued,
47
92
  queuedUrgentCalls: lanes.queuedUrgent,
@@ -54,6 +99,7 @@ export function createTransportRoutes({
54
99
 
55
100
  const register = async (req, res) => {
56
101
  const body = await readBody(req);
102
+ if (refuseUnadmitted(res)) return;
57
103
  if (drain.committed) {
58
104
  sendError(res, `daemon is draining: ${drain.reason}`, 503);
59
105
  return;
@@ -70,6 +116,8 @@ export function createTransportRoutes({
70
116
  clientKind: body.clientKind,
71
117
  registrationId: body.registrationId,
72
118
  revision: body.revision,
119
+ remote: body.remote === true,
120
+ leaseMs: body.leaseMs,
73
121
  });
74
122
  sendJson(res, {
75
123
  token: clientToken,
@@ -85,6 +133,16 @@ export function createTransportRoutes({
85
133
  sendJson(res, { ok: true });
86
134
  };
87
135
 
136
+ // Lease keep-alive for remote clients (the daemon token gate already ran).
137
+ const heartbeat = async (req, res) => {
138
+ const body = await readBody(req);
139
+ if (!body.token || !registry.touch(String(body.token))) {
140
+ sendError(res, 'unknown client token', 404);
141
+ return;
142
+ }
143
+ sendJson(res, { ok: true });
144
+ };
145
+
88
146
  const events = (url, res) => {
89
147
  const clientToken = url.searchParams.get('token');
90
148
  if (!attachSse(clientToken, res)) sendError(res, 'unknown client token', 404);
@@ -92,11 +150,15 @@ export function createTransportRoutes({
92
150
  };
93
151
 
94
152
  const call = async (req, res) => {
153
+ if (refuseUnadmitted(res)) return;
95
154
  if (drain.committed) {
96
155
  sendError(res, `daemon is draining: ${drain.reason}`, 503);
97
156
  return;
98
157
  }
99
158
  const body = await readBody(req);
159
+ // Again right before dispatch: the lease may have been fenced while the
160
+ // body was still arriving.
161
+ if (refuseUnadmitted(res)) return;
100
162
  await callRoute(body, res);
101
163
  };
102
164
 
@@ -109,6 +171,19 @@ export function createTransportRoutes({
109
171
  }
110
172
  };
111
173
 
174
+ // Authenticated stop for a desktop replacing this daemon with another
175
+ // build: refused while turns run unless forced (the user confirmed).
176
+ const stop = async (req, res) => {
177
+ const body = await readBody(req);
178
+ const busySessions = busySessionCount(getStatus());
179
+ if (busySessions > 0 && body.force !== true) {
180
+ sendError(res, `daemon is busy: ${busySessions} running session(s)`, 409);
181
+ return;
182
+ }
183
+ sendJson(res, { ok: true, pid: process.pid, busySessions });
184
+ setImmediate(requestProcessStop);
185
+ };
186
+
112
187
  const upgrade = async (req, res) => {
113
188
  const body = await readBody(req);
114
189
  const requestedProtocol = Number(body.protocol);
@@ -150,8 +225,10 @@ export function createTransportRoutes({
150
225
  const tokenRoutes = {
151
226
  'POST /client/register': register,
152
227
  'POST /client/deregister': deregister,
228
+ 'POST /client/heartbeat': heartbeat,
153
229
  'POST /call': call,
154
230
  'POST /shutdown': (_req, res) => shutdown(res),
231
+ 'POST /stop': stop,
155
232
  'POST /upgrade': upgrade,
156
233
  };
157
234
 
@@ -76,6 +76,7 @@ export function createTransportStack({
76
76
  onClientDropped,
77
77
  onUpgradeRequested,
78
78
  getStatus,
79
+ admitWork,
79
80
  }) {
80
81
  const body = createBodyReader();
81
82
  const callCache = createSessionCallCache({
@@ -125,6 +126,7 @@ export function createTransportStack({
125
126
  }),
126
127
  onClientsEmpty,
127
128
  onUpgradeRequested,
129
+ admitWork,
128
130
  });
129
131
 
130
132
  return { registry, clients, lanes, callCache, broadcast, handleRequest };
@@ -32,6 +32,9 @@ export function createSessionTransport({
32
32
  onClientDropped = null,
33
33
  onUpgradeRequested = null,
34
34
  getStatus = () => ({}),
35
+ // Asked before every register/call; false answers 503 (the daemon's owner
36
+ // lease is not held — daemon-owner.mjs).
37
+ admitWork = () => true,
35
38
  } = {}) {
36
39
  if (typeof handleCall !== 'function') throw new Error('handleCall is required');
37
40
 
@@ -52,6 +55,7 @@ export function createSessionTransport({
52
55
  onClientDropped,
53
56
  onUpgradeRequested,
54
57
  getStatus,
58
+ admitWork,
55
59
  });
56
60
 
57
61
  function beginDrain(reason = 'daemon replacement') {
@@ -69,10 +73,19 @@ export function createSessionTransport({
69
73
  return true;
70
74
  }
71
75
 
76
+ // MIXDOG_SESSION_PORT pins the port (an SSH forward needs a stable one).
77
+ function configuredPort() {
78
+ const port = Number(process.env.MIXDOG_SESSION_PORT);
79
+ return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : 0;
80
+ }
81
+
72
82
  function start() {
73
83
  if (closed) return Promise.reject(new Error('session service transport is closed'));
84
+ const fixedPort = configuredPort();
74
85
  listener ??= createLoopbackListener({
75
86
  server: http.createServer(handleRequest),
87
+ basePort: fixedPort,
88
+ maxPort: fixedPort,
76
89
  onListening(port) {
77
90
  boundPort = port;
78
91
  log(`session service transport listening on 127.0.0.1:${boundPort} pid=${process.pid}`);
@@ -81,7 +94,17 @@ export function createSessionTransport({
81
94
  if (!fatal) log(`server error: ${error?.message || error}`);
82
95
  },
83
96
  });
84
- return listener.start().then((port) => ({ port, token: serverToken }));
97
+ return listener.start().then(async (port) => {
98
+ // The listener falls back to an OS-chosen port when the range is busy;
99
+ // a pinned port must never silently become a different one.
100
+ if (fixedPort && port !== fixedPort) {
101
+ await listener.stop();
102
+ listener = null;
103
+ boundPort = null;
104
+ throw new Error(`session port ${fixedPort} (MIXDOG_SESSION_PORT) is unavailable`);
105
+ }
106
+ return { port, token: serverToken };
107
+ });
85
108
  }
86
109
 
87
110
  async function stop() {
package/src/tui/App.jsx CHANGED
@@ -21,6 +21,7 @@ import { useApp, useStdin, useStdout } from 'ink';
21
21
  import { useSession } from './hooks/useSession.mjs';
22
22
  import { pickFolder } from '../standalone/folder-dialog.mjs';
23
23
  import { deriveSlashPalette } from './app/slash-palette.mjs';
24
+ import { skillMentionQuery, useSkillMentionSkills } from './app/skill-mention-palette.mjs';
24
25
  import { shouldSupersedePanelEpoch, supersedePanelEpoch } from './app/panel-epoch.mjs';
25
26
  import { createPanelSurface } from './app/panel-surface.mjs';
26
27
  import { resolvePickerState } from './app/picker-index-mode.mjs';
@@ -215,6 +216,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
215
216
  setPromptDraft,
216
217
  promptDraftOverride,
217
218
  setPromptDraftOverride,
219
+ promptCaret,
220
+ setPromptCaret,
218
221
  promptLayoutValueRef,
219
222
  setPromptLayoutRows,
220
223
  textEntryLayoutRows,
@@ -625,7 +628,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
625
628
  pastedTextsRef,
626
629
  });
627
630
 
628
- const { activeSlashQuery, slashCommands, slashPaletteOpen } = deriveSlashPalette({
631
+ const mentionSkills = useSkillMentionSkills(store, skillMentionQuery(promptDraft, promptCaret) !== null);
632
+ const { activeSlashQuery, slashCommands, slashPaletteOpen, paletteKind } = deriveSlashPalette({
629
633
  providerPrompt,
630
634
  settingsPrompt,
631
635
  toolApproval,
@@ -636,6 +640,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
636
640
  commandBusy: state.commandBusy,
637
641
  promptDraft,
638
642
  slashDismissedFor,
643
+ skills: mentionSkills,
644
+ promptCaret,
639
645
  });
640
646
  slashPaletteRef.current = { open: slashPaletteOpen, count: slashCommands.length };
641
647
  scrollFocusRef.current = {
@@ -833,10 +839,12 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
833
839
  hasUserMessages,
834
840
  initialStatusLine,
835
841
  onPromptDraftChange,
842
+ onPromptCaretChange: setPromptCaret,
836
843
  onSubmit,
837
844
  overlayHintAttachItemIndex,
838
845
  overlayHintFallbackRow,
839
846
  overlayHintOnLastItem,
847
+ paletteKind,
840
848
  panelInkMaskEpoch,
841
849
  panelTransitionEpoch,
842
850
  picker,
@@ -18,6 +18,12 @@ import { textEntryClearsByEmpty } from '../text-entry-policy.mjs';
18
18
  import { withPickerEnterLatch } from '../picker-enter-latch.mjs';
19
19
  import { providerPromptFields, settingsPromptFields } from './text-entry-labels.mjs';
20
20
 
21
+ // `$name` skill completion reuses the command palette with its own copy.
22
+ const SKILL_PALETTE_TEXT = {
23
+ description: 'Type to filter · Enter or Tab inserts the highlighted skill.',
24
+ help: '↑/↓ select · Enter insert · Esc cancel',
25
+ };
26
+
21
27
  function renderToolApproval({ toolApproval, store, frameColumns, expandedOptionPanel }) {
22
28
  const resolve = (approved) => {
23
29
  store.resolveToolApproval?.(toolApproval.id, {
@@ -166,13 +172,15 @@ export function renderFloatingPanel(ctx) {
166
172
  );
167
173
  }
168
174
  if (slashPaletteOpen) {
175
+ const skills = ctx.paletteKind === 'skills';
169
176
  return (
170
177
  <SlashCommandPalette
171
178
  commands={slashCommands}
172
179
  selectedIndex={slashIndex}
173
- title="Commands"
180
+ title={skills ? 'Skills' : 'Commands'}
174
181
  columns={frameColumns}
175
182
  query={activeSlashQuery}
183
+ {...(skills ? SKILL_PALETTE_TEXT : {})}
176
184
  />
177
185
  );
178
186
  }
@@ -52,8 +52,10 @@ export function renderAppView(ctx) {
52
52
  inputHint,
53
53
  inputHintTone,
54
54
  liveSpinner,
55
+ onPromptCaretChange,
55
56
  onPromptDraftChange,
56
57
  onSubmit,
58
+ paletteKind,
57
59
  panelTransitionClearRows,
58
60
  picker,
59
61
  promptBoxRectRef,
@@ -85,6 +87,7 @@ export function renderAppView(ctx) {
85
87
  onSubmit={onSubmit}
86
88
  disabled={exiting || !!picker || !!contextPanel || !!toolApproval || !tuiReady}
87
89
  onDraftChange={onPromptDraftChange}
90
+ onCaretChange={onPromptCaretChange}
88
91
  interruptActive={state.busy}
89
92
  onInterrupt={handlePromptInterrupt}
90
93
  initialValue={promptDraft}
@@ -108,6 +111,9 @@ export function renderAppView(ctx) {
108
111
  commandPaletteActive={slashPaletteOpen && slashCommands.length > 0}
109
112
  commandPaletteOpen={slashPaletteOpen}
110
113
  commandPaletteOptionCount={slashCommands.length}
114
+ // The `$name` skill palette leaves Left/Right to the caret: the
115
+ // mention follows it.
116
+ commandPaletteMovesCaret={paletteKind === 'skills'}
111
117
  onCommandPaletteNavigate={(direction) => {
112
118
  setSlashIndex((index) => stepPaletteIndex(index, direction, slashCommands.length));
113
119
  }}