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
@@ -7,11 +7,14 @@
7
7
  * Kept apart from the cache/fingerprint policy in store-summary-reader.mjs
8
8
  * because this runs exactly once per distinct content and is the only part
9
9
  * that lazily enters the heavier reconnect-recovery boundary (dynamic imports
10
- * preserved verbatim so the cold summary path stays leaf-only).
10
+ * keep the cold summary path leaf-only).
11
11
  */
12
12
  import { sessionContextMeasurement, contextMeasurementStats } from '../../../shared/context-measurement.mjs';
13
13
  import { nextProjectionStamp } from './store-transcript-cache.mjs';
14
+ import { join } from 'node:path';
14
15
  import { cleanValue, desktopSession, positiveNumber } from './store-summary-fields.mjs';
16
+ import { compactionArchiveRange, readCompactionArchivePage } from './compaction-archive.mjs';
17
+ import { dataDir } from './store-summary-locations.mjs';
15
18
 
16
19
  /** The parse/recovery/projection body of a stored transcript read. Runs once
17
20
  * per distinct content; its result is shared read-only through the cache. */
@@ -90,17 +93,31 @@ export async function projectStoredTranscript(sessionId, doc, options) {
90
93
  // tail is identical either way.
91
94
  const itemLimit = Number.isFinite(options.itemLimit) ? options.itemLimit : Number.POSITIVE_INFINITY;
92
95
  const restored = restoreTranscriptItems(messages, { sessionId, itemLimit: itemLimit + 1 });
93
- const transcriptHasOlder = restored.length > itemLimit;
96
+ let transcriptHasOlder = restored.length > itemLimit;
97
+ let items = transcriptHasOlder ? restored.slice(-itemLimit) : restored;
98
+ // A paged window continues past the first stored message into the
99
+ // display-only compaction archive (newest segment first). A legacy whole
100
+ // read never receives it.
101
+ const archiveRange = Number.isFinite(itemLimit) && !transcriptHasOlder ? compactionArchiveRange(session) : null;
102
+ if (archiveRange) {
103
+ const archived = await readCompactionArchivePage(join(dataDir(), 'sessions'), sessionId, {
104
+ ...archiveRange,
105
+ limit: itemLimit - restored.length,
106
+ });
107
+ if (archived.items.length) items = [...archived.items, ...restored];
108
+ transcriptHasOlder = archived.hasOlder;
109
+ }
94
110
  return {
95
111
  sessionId,
96
112
  projectionStamp: nextProjectionStamp(),
97
113
  ...(options.includeMessages ? { messages } : {}),
98
- items: transcriptHasOlder ? restored.slice(-itemLimit) : restored,
114
+ items,
99
115
  transcriptHasOlder,
100
116
  provider: session.provider || '',
101
117
  model: session.model || '',
102
118
  effort: session.effort || '',
103
119
  fast: session.fast === true,
120
+ ...(session.autoEffort === false ? { autoEffort: false } : {}),
104
121
  modelParameters: session.modelParameters || {},
105
122
  cwd: session.cwd || '',
106
123
  desktopSession: desktopSession(session.desktopSession, session.cwd),
@@ -16,6 +16,7 @@
16
16
  import { isActualUserInstructionMessage, isInjectedSkillBodyMessage, isSummaryMessage } from './compact/messages.mjs';
17
17
  import { ACTIVE_TURN_CONTINUATION_ANCHOR, ACTIVE_TURN_CONTINUATION_SOURCE } from './compact/continuation.mjs';
18
18
  import { parseTaskNotification } from '../../../shared/task-notification-envelope.mjs';
19
+ import { isImportedMessage } from './imported-message.mjs';
19
20
 
20
21
  export const SYNTHETIC_USER_ENVELOPE_TAG = 'mixdog-runtime';
21
22
 
@@ -62,7 +63,8 @@ function isAlreadyEnveloped(text) {
62
63
 
63
64
  // null => a real user instruction (or nothing to wrap); otherwise the kind.
64
65
  export function classifySyntheticUserMessage(message) {
65
- if (message?.role !== 'user') return null;
66
+ // Imported prose stays the user's own (unwrapped) voice.
67
+ if (message?.role !== 'user' || isImportedMessage(message)) return null;
66
68
  const text = messageText(message);
67
69
  if (!text.trim()) return null;
68
70
  if (isAlreadyEnveloped(text)) return null;
@@ -92,7 +92,7 @@ function crossTurnStub(batch, call, ctSig) {
92
92
  // (which spans ONE assistant turn): when the model re-issues an IDENTICAL
93
93
  // normalized call that has already failed repeatFailLimit times in a row
94
94
  // across iterations, stop re-executing — the result will not change, and
95
- // each retry burns a full LLM round-trip until the hard iteration cap. The
95
+ // each retry burns a full LLM round-trip with no iteration cap to end it. The
96
96
  // call stays unresolved; the guard only skips re-execution.
97
97
  function repeatFailureSkip(batch, call, sigs) {
98
98
  const { sessionRef, repeatFailLimit } = batch;
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { contextMeasurementStats } from '../../../../shared/context-measurement.mjs';
8
8
  import { toolResultText } from './tool-result-text.mjs';
9
+ import { restoredUserImageRefs, restoredUserText } from './user-text.mjs';
9
10
  import {
10
11
  completionCardFromExecution,
11
12
  parseModelVisibleCompletionWrapper,
@@ -37,6 +38,7 @@ import {
37
38
  } from '../../../../shared/tool-execution-contract.mjs';
38
39
  import { toolResultTerminalStatus } from '../../../../shared/tool-status.mjs';
39
40
  import { transcriptRouteMetadataFields } from '../../../../shared/transcript-metadata.mjs';
41
+ import { isImportedMessage } from '../imported-message.mjs';
40
42
 
41
43
  function restoredTranscriptMetadata(message) {
42
44
  const value = message?.meta?.transcript;
@@ -48,12 +50,23 @@ function restoredTranscriptMetadata(message) {
48
50
  if (completionValue && completionStatus && Number.isFinite(completionElapsedMs)) {
49
51
  completion = { status: completionStatus, elapsedMs: Math.max(0, completionElapsedMs) };
50
52
  if (typeof completionValue.verb === 'string' && completionValue.verb) completion.verb = completionValue.verb;
53
+ if (typeof completionValue.checkpointId === 'string' && completionValue.checkpointId) {
54
+ completion.checkpointId = completionValue.checkpointId;
55
+ }
51
56
  }
52
57
  return {
58
+ // The stable message id lets a live view map a compaction's dropped
59
+ // messages back to these rows.
60
+ ...(typeof value.messageId === 'string' && value.messageId ? { messageId: value.messageId } : {}),
53
61
  ...(Number.isFinite(Number(value.at)) ? { at: Number(value.at) } : {}),
54
62
  ...transcriptRouteMetadataFields(value),
55
63
  ...(typeof value.sender === 'string' && value.sender ? { sender: value.sender } : {}),
56
64
  ...(typeof value.device === 'string' && value.device ? { device: value.device } : {}),
65
+ ...(typeof value.checkpointId === 'string' && value.checkpointId ? { checkpointId: value.checkpointId } : {}),
66
+ // Skills this user turn explicitly loaded (header selection or `$name`).
67
+ ...(Array.isArray(value.appliedSkills) && value.appliedSkills.length
68
+ ? { appliedSkills: value.appliedSkills.filter((name) => typeof name === 'string' && name) }
69
+ : {}),
57
70
  ...(completion ? { completion } : {}),
58
71
  };
59
72
  }
@@ -70,15 +83,16 @@ function restoredAssistantTranscriptItems(message, nextId) {
70
83
  ...completion,
71
84
  ...(completion.status === 'done' ? { finalAssistantId: items[0].id } : {}),
72
85
  ...(metadata.at ? { at: metadata.at } : {}),
86
+ ...(metadata.messageId ? { messageId: metadata.messageId } : {}),
73
87
  });
74
88
  }
75
89
  return items;
76
90
  }
77
91
 
78
92
  // Restored tool cards: stored assistant messages keep their (compacted)
79
- // tool_calls and the follow-up role:'tool' results, but resume used to drop
80
- // both — a reopened session lost every tool marker (user bug). Rebuild one
81
- // transcript tool item per call and attach its result by tool_call_id.
93
+ // tool_calls and the follow-up role:'tool' results. Rebuild one transcript
94
+ // tool item per call and attach its result by tool_call_id, so a reopened
95
+ // session keeps its tool markers.
82
96
  function restoredMessageToolCalls(message) {
83
97
  if (Array.isArray(message?.tool_calls)) return message.tool_calls;
84
98
  if (Array.isArray(message?.toolCalls)) return message.toolCalls;
@@ -112,7 +126,12 @@ function restoredToolCallItems(message, nextId, pendingByCallId) {
112
126
  ...(Number.isFinite(at) ? { at, startedAt: at, completedAt: at } : {}),
113
127
  };
114
128
  const callId = typeof call?.id === 'string' ? call.id : '';
115
- if (callId) pendingByCallId.set(callId, item);
129
+ // The call id ties the card to its call and result messages (a live view
130
+ // greys it when a compaction drops them).
131
+ if (callId) {
132
+ item.callId = callId;
133
+ pendingByCallId.set(callId, item);
134
+ }
116
135
  items.push(item);
117
136
  }
118
137
  return items;
@@ -203,6 +222,9 @@ function buildRestoredAggregateItem(members) {
203
222
  ...(loadingTargets.length > 0 ? { loadingTargets } : {}),
204
223
  },
205
224
  aggregate: true,
225
+ ...(members.some(({ item }) => item.callId)
226
+ ? { callIds: members.map(({ item }) => item.callId).filter(Boolean) }
227
+ : {}),
206
228
  categories: Object.fromEntries(header.categories),
207
229
  doneCategories: aggregateDoneCategories(calls),
208
230
  completedCount: calls.length,
@@ -219,8 +241,8 @@ function buildRestoredAggregateItem(members) {
219
241
 
220
242
  // Restored transcripts must mirror the live turn's category merging: the live
221
243
  // session runtime (turn.mjs) collapses consecutive same-bucket tool calls into one
222
- // aggregate card, but resume rebuilt one card per call, so a reopened session
223
- // un-merged every run (user bug). Walk the restored items and merge adjacent
244
+ // aggregate card, so a reopened session must not show them un-merged. Walk the
245
+ // restored items and merge adjacent
224
246
  // tool cards whose aggregateBucketForCategory matches; any non-tool item
225
247
  // (user/assistant/turndone) is a block boundary, same as the live seal rule.
226
248
  // Runs of 1 keep the plain per-call card (its argument summary stays visible).
@@ -279,9 +301,11 @@ function restoredMessageItemUpperBound(message) {
279
301
  function restoredNotificationToolItem(parsed, text, message, nextId, errorLabel) {
280
302
  const label = parsed.label || 'notification';
281
303
  const at = Number(message?.meta?.transcript?.at);
304
+ const messageId = message?.meta?.transcript?.messageId;
282
305
  return {
283
306
  kind: 'tool',
284
307
  id: nextId(),
308
+ ...(typeof messageId === 'string' && messageId ? { messageId } : {}),
285
309
  name: parsed.name || 'agent',
286
310
  args: parsed.args || {
287
311
  type: label,
@@ -303,7 +327,12 @@ function restoredUserTranscriptItems(message, nextId) {
303
327
  // skill bodies (meta:'skill'), hook/system reminders, and tag-wrapped context
304
328
  // blocks must not restore as user bubbles in any client.
305
329
  if (message?.meta === 'skill' || message?.meta === 'hook') return [];
306
- const text = (typeof message?.content === 'string' ? message.content : toolResultText(message?.content)).trim();
330
+ // Imported prose renders verbatim: never a control row, card or display rewrite.
331
+ if (isImportedMessage(message)) {
332
+ const text = (typeof message.content === 'string' ? message.content : toolResultText(message.content)).trim();
333
+ return text ? [{ kind: 'user', id: nextId(), text, ...restoredTranscriptMetadata(message) }] : [];
334
+ }
335
+ const text = restoredUserText(message?.content).trim();
307
336
  // Persisted async-completion wrappers ("Async shell task ... finished. Result:
308
337
  // > ...") are the only durable record of a background completion — the live
309
338
  // Response card is an event-time push that does not survive a transcript
@@ -329,11 +358,21 @@ function restoredUserTranscriptItems(message, nextId) {
329
358
  // step, but render it like a live cancel tail (◈ Cancelled) instead of a
330
359
  // raw user bubble with bracketed internals.
331
360
  if (isTranscriptCancelledStatusText(text)) {
332
- return [{ kind: 'turndone', id: nextId(), status: 'cancelled', elapsedMs: 0 }];
361
+ const { messageId } = restoredTranscriptMetadata(message);
362
+ return [{ kind: 'turndone', id: nextId(), status: 'cancelled', elapsedMs: 0, ...(messageId ? { messageId } : {}) }];
333
363
  }
334
364
  const synthetic = parseSyntheticAgentMessage(text);
335
365
  if (!synthetic) {
336
- return [{ kind: 'user', id: nextId(), text, ...restoredTranscriptMetadata(message) }];
366
+ const imageRefs = restoredUserImageRefs(message?.content);
367
+ return [
368
+ {
369
+ kind: 'user',
370
+ id: nextId(),
371
+ text,
372
+ ...restoredTranscriptMetadata(message),
373
+ ...(imageRefs.length ? { imageRefs } : {}),
374
+ },
375
+ ];
337
376
  }
338
377
  return [restoredNotificationToolItem(synthetic, text, message, nextId, /^(failed|error|killed|cancelled)$/i)];
339
378
  }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The text a restored user bubble shows for a stored prompt, matching what the
3
+ * live bubble showed for it:
4
+ * - An image part is drawn by the `[Image: …]` metadata line the composer put
5
+ * before it; the part itself adds no raw `[image: image/png]` marker (a bare
6
+ * one gets a metadata line of its own, which renders as an image chip).
7
+ * - A desktop `<visualize_feedback>` block (the agent-facing pin/choice
8
+ * record) is shown as the lines the live bubble showed: `📌 N · label —
9
+ * note` per pin, one `☑ group: label, …` line for the picks and one
10
+ * `🎚 id: value (was old), …` line for tweaks (drawn with an icon). Intake
11
+ * moves a long block into the attachment store; it is read back from there.
12
+ */
13
+ import { isAttachmentReference, readAttachmentTextWithin } from '../../../../attachments/store.mjs';
14
+ import { toolResultText } from './tool-result-text.mjs';
15
+
16
+ const IMAGE_METADATA_LINE = /\[Image(?::| source:) [^\]\r\n]+\]\s*$/;
17
+ const FEEDBACK_OPEN = '<visualize_feedback>';
18
+ const FEEDBACK_BLOCK = /<visualize_feedback>\n([\s\S]*?)\n?<\/visualize_feedback>/g;
19
+ // Feedback blocks are bounded (20 pins, trimmed HTML); a larger blob is a long
20
+ // prompt or paste, which restores as before without being read. The cap holds
21
+ // on the blob itself: the reference's claimed sizeBytes is not trusted.
22
+ const FEEDBACK_MAX_BYTES = 128 * 1024;
23
+ const QUOTED = '("(?:[^"\\\\]|\\\\.)*")';
24
+ const CHOICE_LINE = new RegExp(`· group: (?:${QUOTED}|\\(none\\)) → choice: ${QUOTED}(?: \\(${QUOTED}\\))?$`);
25
+
26
+ function unquote(value) {
27
+ if (!value) return '';
28
+ try {
29
+ return String(JSON.parse(value));
30
+ } catch {
31
+ return '';
32
+ }
33
+ }
34
+
35
+ /** The live bubble's lines for one feedback block body. */
36
+ function feedbackDisplay(body) {
37
+ const pins = [];
38
+ const choices = [];
39
+ let pin = null;
40
+ let note = false;
41
+ let inChoices = false;
42
+ const tweakSets = [];
43
+ let tweaks = null;
44
+ for (const line of body.split('\n')) {
45
+ if (line.startsWith('tweaks (')) {
46
+ pin = null;
47
+ inChoices = false;
48
+ tweaks = null;
49
+ tweakSets.push([]);
50
+ tweaks = tweakSets.at(-1);
51
+ continue;
52
+ }
53
+ if (tweaks) {
54
+ const change = /^ {4}(.+?) \([^)]*\): (.*) \(was (.*)\)$/.exec(line);
55
+ if (change) tweaks.push(`${change[1]}: ${change[2]} (was ${change[3]})`);
56
+ else if (line.startsWith(' - tab: ')) {
57
+ tweaks = [];
58
+ tweakSets.push(tweaks);
59
+ } else tweaks = null;
60
+ if (tweaks) continue;
61
+ }
62
+ const head = /^pin (\d+) · (.*)$/.exec(line);
63
+ if (head) {
64
+ pin = { n: head[1], label: head[2], note: '' };
65
+ pins.push(pin);
66
+ note = false;
67
+ inChoices = false;
68
+ continue;
69
+ }
70
+ if (line === 'choices:') {
71
+ pin = null;
72
+ inChoices = true;
73
+ continue;
74
+ }
75
+ if (pin && line.startsWith(' note: ')) {
76
+ pin.note = line.slice(8);
77
+ note = true;
78
+ continue;
79
+ }
80
+ if (pin && note && line.startsWith(' ')) {
81
+ pin.note += `\n${line.slice(4)}`;
82
+ continue;
83
+ }
84
+ note = false;
85
+ const choice = inChoices ? CHOICE_LINE.exec(line) : null;
86
+ if (choice) {
87
+ choices.push({ group: unquote(choice[1]), choice: unquote(choice[2]), label: unquote(choice[3]) });
88
+ }
89
+ }
90
+ const lines = pins.map((entry) => `📌 ${entry.n} · ${entry.label} — ${entry.note.replace(/\s+/g, ' ')}`);
91
+ if (choices.length) {
92
+ lines.push(
93
+ `☑ ${choices.map((entry) => (entry.group ? `${entry.group}: ` : '') + (entry.label || entry.choice)).join(', ')}`
94
+ );
95
+ }
96
+ const changed = tweakSets.filter((set) => set.length).map((set) => set.join(', '));
97
+ if (changed.length) lines.push(`🎚 ${changed.join('; ')}`);
98
+ return lines.join('\n');
99
+ }
100
+
101
+ export function withVisualizeFeedbackDisplay(text) {
102
+ const value = String(text ?? '');
103
+ if (!value.includes(FEEDBACK_OPEN)) return value;
104
+ return value.replace(FEEDBACK_BLOCK, (_block, body) => feedbackDisplay(body));
105
+ }
106
+
107
+ /** A stored text reference's body when it is a feedback block, else ''. */
108
+ function referencedFeedbackText(part) {
109
+ if (part?.type !== 'text' || part.textKind) return '';
110
+ try {
111
+ const text = readAttachmentTextWithin(part, FEEDBACK_MAX_BYTES);
112
+ return text?.startsWith(FEEDBACK_OPEN) ? text : '';
113
+ } catch {
114
+ // A blob gone from the store restores as it always did: without its text.
115
+ return '';
116
+ }
117
+ }
118
+
119
+ /** The display lines of a stored prompt's parts, and for each image part the
120
+ * line that draws it: the composer's metadata line right before it, else a
121
+ * bare line of its own (a bare line never stands for the next image). */
122
+ function restoredUserParts(content) {
123
+ const texts = [];
124
+ const images = [];
125
+ let pendingMetadata = '';
126
+ for (const part of content) {
127
+ if (part?.type === 'image' || part?.type === 'input_image') {
128
+ let line = pendingMetadata;
129
+ if (!line) {
130
+ line = `[Image: ${part.mimeType || part.mediaType || 'image'}]`;
131
+ texts.push(line);
132
+ }
133
+ pendingMetadata = '';
134
+ images.push({ part, line });
135
+ continue;
136
+ }
137
+ const text = isAttachmentReference(part) ? referencedFeedbackText(part) : toolResultText([part]);
138
+ if (text) texts.push(text);
139
+ pendingMetadata = IMAGE_METADATA_LINE.test(text) ? text.trimEnd().split(/\r?\n/).pop().trim() : '';
140
+ }
141
+ return { texts, images };
142
+ }
143
+
144
+ export function restoredUserText(content) {
145
+ if (!Array.isArray(content)) {
146
+ return withVisualizeFeedbackDisplay(typeof content === 'string' ? content : toolResultText(content));
147
+ }
148
+ return withVisualizeFeedbackDisplay(restoredUserParts(content).texts.join('\n'));
149
+ }
150
+
151
+ /** The stored original of each image the restored bubble draws, by the
152
+ * `[Image: …]` line that draws it. Every image keeps its slot (one without a
153
+ * stored original has no `ref`) so later images do not shift onto it; empty
154
+ * when no image has a stored original. */
155
+ export function restoredUserImageRefs(content) {
156
+ if (!Array.isArray(content)) return [];
157
+ const slots = restoredUserParts(content).images.map(({ part, line }) =>
158
+ isAttachmentReference(part) ? { line, ref: part.attachmentRef } : { line }
159
+ );
160
+ return slots.some((slot) => slot.ref) ? slots : [];
161
+ }
@@ -3,13 +3,25 @@ import { buildPowerShellFilterTeePlan } from '../shell-analysis.mjs';
3
3
  import { normalizeErrorMessage } from '../path-diagnostics.mjs';
4
4
  import { planDirectExeSpawn } from '../shell-direct-exe.mjs';
5
5
  import { POWERSHELL_UTF8_PREFIX } from '../../../../../shared/shell-display.mjs';
6
+ import { renderPowerShellSkillFunction, skillScriptRuntime } from '../../../../../shared/skill-script-runtime.mjs';
6
7
  import { buildShellSpawnEnv } from './spawn-env.mjs';
7
8
  import { formatShellToolFailure } from './result-format.mjs';
8
9
 
10
+ // Every PowerShell model command defines `mixdog-skill-node` first, so the
11
+ // skill launcher runs without the .cmd's cmd.exe argument re-parsing.
12
+ // Injected whenever absent, independent of the UTF-8 prefix deduplication.
13
+ function prefixPowerShellSkillFunction(command) {
14
+ const fn = renderPowerShellSkillFunction(skillScriptRuntime());
15
+ const text = String(command || '');
16
+ return text.trimStart().startsWith(`${fn}\n`) ? text : `${fn}\n${text}`;
17
+ }
18
+
9
19
  function prefixPowerShellUtf8(command) {
10
20
  const prefix = POWERSHELL_UTF8_PREFIX;
11
21
  const text = String(command || '');
12
- return text.trimStart().startsWith(prefix) ? text : `${prefix}\n${text}`;
22
+ const trimmed = text.trimStart();
23
+ const body = trimmed.startsWith(prefix) ? trimmed.slice(prefix.length).replace(/^[ \t]*\r?\n/, '') : text;
24
+ return `${prefix}\n${prefixPowerShellSkillFunction(body)}`;
13
25
  }
14
26
 
15
27
  // How the command reaches the child: a direct exe spawn, PowerShell with the
@@ -54,6 +66,10 @@ export async function planShellInvocation({ command, resolvedSpec, cwd }) {
54
66
  plan.wrappedCommand = prefixPowerShellUtf8(plan.teePlan ? plan.teePlan.command : command);
55
67
  return plan;
56
68
  }
69
+ if (shellType === 'powershell') {
70
+ plan.wrappedCommand = prefixPowerShellSkillFunction(command);
71
+ return plan;
72
+ }
57
73
  if (process.platform !== 'win32' && (shell.includes('bash') || shell.includes('zsh'))) {
58
74
  try {
59
75
  plan.wrappedCommand = await wrapCommandWithSnapshot(shell, command);
@@ -1,4 +1,5 @@
1
1
  import { resourceAdmission } from '../../../../../shared/resource-admission.mjs';
2
+ import { withSkillLauncherPath } from '../../../../../shared/skill-script-runtime.mjs';
2
3
  import {
3
4
  applyShellEgressPolicy,
4
5
  scrubLoaderVars,
@@ -24,5 +25,8 @@ export function buildShellSpawnEnv(cwd, baseEnv = process.env) {
24
25
  scrubLoaderVars(spawnEnv);
25
26
  scrubRuntimeRootVars(spawnEnv);
26
27
  applyShellEgressPolicy(spawnEnv);
28
+ // `mixdog-skill-node` (a skill's ${MIXDOG_SKILL_NODE}) resolves in every
29
+ // model shell; only this env gets the entry, never the daemon's own.
30
+ withSkillLauncherPath(spawnEnv);
27
31
  return spawnEnv;
28
32
  }
@@ -5,10 +5,15 @@ import { isAbsolute, sep } from 'node:path';
5
5
  import { canonicalCachePath, deleteReadRangeIndexForPath } from './read-range-index.mjs';
6
6
  import { resolveAgainstCwd } from './path-utils.mjs';
7
7
  import { fileVersion, sameFileVersion } from './file-version.mjs';
8
+ import { canCacheReadFile } from './read-cache-policy.mjs';
8
9
 
9
10
  const RESULT_CACHE = new Map(); // key → { ts, value, paths, scopes, readSnapshotMeta, contentPrefixHash, bytes }
10
11
  const RESULT_CACHE_INFLIGHT = new Map(); // key → { promise, controller, subscribers, settled }
11
12
  const RESULT_CACHE_COMPUTE = new AsyncLocalStorage();
13
+ const READ_CACHE_PUBLICATION = new AsyncLocalStorage();
14
+ // Only active reads retain a slot. Invalidating an inactive path creates
15
+ // no history, and the final reader releases its path in a finally block.
16
+ const ACTIVE_READ_CACHE_PATHS = new Map();
12
17
  const RESULT_CACHE_TTL_MS = 30_000;
13
18
  const RESULT_CACHE_MAX_ENTRIES = 200;
14
19
  // A byte budget from the bytes env var, else the megabytes one, else the
@@ -72,6 +77,37 @@ function cachePathsOverlap(a, b) {
72
77
  return a.startsWith(b.endsWith(sep) ? b : `${b}${sep}`) || b.startsWith(a.endsWith(sep) ? a : `${a}${sep}`);
73
78
  }
74
79
 
80
+ export async function runReadCachePublication(fullPaths, read) {
81
+ const paths = normalizeCacheMetaPaths(Array.isArray(fullPaths) ? fullPaths : [fullPaths]);
82
+ const leases = paths.map((path) => {
83
+ let slot = ACTIVE_READ_CACHE_PATHS.get(path);
84
+ if (!slot) {
85
+ slot = { generation: 0, references: 0 };
86
+ ACTIVE_READ_CACHE_PATHS.set(path, slot);
87
+ }
88
+ slot.references++;
89
+ return { path, slot, generation: slot.generation, released: false };
90
+ });
91
+ try {
92
+ return await READ_CACHE_PUBLICATION.run(leases, read);
93
+ } finally {
94
+ for (const lease of leases) {
95
+ lease.released = true;
96
+ if (--lease.slot.references === 0) ACTIVE_READ_CACHE_PATHS.delete(lease.path);
97
+ }
98
+ }
99
+ }
100
+
101
+ export function canPublishReadCache(targets = null) {
102
+ const leases = READ_CACHE_PUBLICATION.getStore();
103
+ if (!leases) return true;
104
+ const paths = normalizeCacheMetaPaths(Array.isArray(targets) ? targets : [targets]);
105
+ return leases.every((lease) => {
106
+ if (paths.length && !paths.some((path) => cachePathsOverlap(path, lease.path))) return true;
107
+ return !lease.released && lease.generation === lease.slot.generation;
108
+ });
109
+ }
110
+
75
111
  function cacheEntryOverlapsPaths(entry, affectedPaths) {
76
112
  const entryPaths = Array.isArray(entry?.paths) ? entry.paths : [];
77
113
  const entryScopes = Array.isArray(entry?.scopes) ? entry.scopes : [];
@@ -107,6 +143,11 @@ export function cacheSet(key, value, meta = {}) {
107
143
  // A detached/aborted computation must never repopulate a newer cache.
108
144
  const computation = RESULT_CACHE_COMPUTE.getStore();
109
145
  if (computation?.invalidated || computation?.controller?.signal?.aborted) return;
146
+ const publicationPaths = [
147
+ ...(Array.isArray(meta.paths) ? meta.paths : []),
148
+ ...(Array.isArray(meta.scopes) ? meta.scopes : []),
149
+ ];
150
+ if (!canPublishReadCache(publicationPaths)) return;
110
151
  // Replace-in-place: clear old entry's byte accounting before write.
111
152
  if (RESULT_CACHE.has(key)) resultCacheDelete(key);
112
153
  const bytes = estimateResultBytes(value);
@@ -239,7 +280,7 @@ export async function runRawContentInFlight(fullPath, loader = fsPromises.readFi
239
280
  // Buffered reads provide their observed version: a later read must not
240
281
  // join older bytes merely because a watcher has not fired yet. Preserve
241
282
  // the unversioned helper contract for callers that supply no stat.
242
- if (stat !== undefined && !version) return await loader(fullPath);
283
+ if (stat !== undefined && (!version || !canCacheReadFile(version))) return await loader(fullPath);
243
284
  const key = version ? JSON.stringify([path, version]) : path;
244
285
  return await runPathInFlight(RAW_CONTENT_INFLIGHT, key, path, () => loader(fullPath));
245
286
  }
@@ -260,6 +301,10 @@ function rawContentCacheDelete(key) {
260
301
  export function rawContentCacheGet(fullPath, stat, now = Date.now()) {
261
302
  if (!fullPath || !stat) return null;
262
303
  const key = canonicalCachePath(fullPath);
304
+ if (!canCacheReadFile(stat)) {
305
+ rawContentCacheDelete(key);
306
+ return null;
307
+ }
263
308
  const entry = RAW_CONTENT_CACHE.get(key);
264
309
  if (!entry) return null;
265
310
  if (now - entry.ts > RAW_CONTENT_CACHE_TTL_MS) {
@@ -276,9 +321,10 @@ export function rawContentCacheGet(fullPath, stat, now = Date.now()) {
276
321
  }
277
322
 
278
323
  export function rawContentCacheSet(fullPath, stat, rawBuf, now = Date.now()) {
324
+ if (!canPublishReadCache(fullPath)) return;
279
325
  if (!fullPath || !Buffer.isBuffer(rawBuf)) return;
280
326
  const version = fileVersion(stat);
281
- if (!version || rawBuf.length !== version.size || rawBuf.length > RAW_CONTENT_CACHE_MAX_BYTES) return;
327
+ if (!version || !canCacheReadFile(version) || rawBuf.length !== version.size || rawBuf.length > RAW_CONTENT_CACHE_MAX_BYTES) return;
282
328
  const key = canonicalCachePath(fullPath);
283
329
  rawContentCacheDelete(key);
284
330
  RAW_CONTENT_CACHE.set(key, {
@@ -450,6 +496,7 @@ function runExtraInvalidationListeners(affectedPaths = null) {
450
496
  }
451
497
 
452
498
  function cacheInvalidateAll() {
499
+ for (const slot of ACTIVE_READ_CACHE_PATHS.values()) slot.generation++;
453
500
  for (const entry of RESULT_CACHE_INFLIGHT.values()) {
454
501
  entry.invalidated = true;
455
502
  }
@@ -470,6 +517,9 @@ function cacheInvalidatePaths(paths) {
470
517
  cacheInvalidateAll();
471
518
  return;
472
519
  }
520
+ for (const [path, slot] of ACTIVE_READ_CACHE_PATHS) {
521
+ if (affectedPaths.some((affected) => cachePathsOverlap(path, affected))) slot.generation++;
522
+ }
473
523
  for (const inFlight of [RAW_CONTENT_INFLIGHT, READ_ONLY_STAT_INFLIGHT]) {
474
524
  for (const [key, entry] of inFlight) {
475
525
  if (affectedPaths.some((affected) => cachePathsOverlap(entry.path, affected))) inFlight.delete(key);