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
@@ -1,5 +1,6 @@
1
1
  // Steering-message normalization/merge helpers.
2
2
  // Merges queued steering entries into a single content payload + display text.
3
+ import { stableMessageId } from '../../../../shared/transcript-metadata.mjs';
3
4
 
4
5
  function steeringContentText(content) {
5
6
  if (typeof content === 'string') return content;
@@ -135,10 +136,14 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
135
136
  const rawSubmittedAt = Number(merged.submittedAt);
136
137
  const submittedAt = Number.isFinite(rawSubmittedAt) && rawSubmittedAt > 0 ? rawSubmittedAt : undefined;
137
138
  const injectedAt = Date.now();
139
+ // Every steered message carries a stable id from creation; the surface
140
+ // gets it back (onSteerMessage) for the row it draws. A steered prompt
141
+ // keeps the id the surface gave it, else gets one.
142
+ const messageId = stableMessageId(merged.transcriptMeta?.messageId);
138
143
  const steeringTranscriptMeta =
139
144
  merged.transcriptMeta && typeof merged.transcriptMeta === 'object'
140
- ? { at: submittedAt ?? injectedAt, ...merged.transcriptMeta }
141
- : null;
145
+ ? { at: submittedAt ?? injectedAt, ...merged.transcriptMeta, messageId }
146
+ : { messageId };
142
147
  // Tag steering-origin user messages so provider lowering keeps them
143
148
  // distinct from preceding tool results. Keep each queued command as
144
149
  // its own user turn instead of collapsing priority/mode buckets
@@ -151,17 +156,18 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
151
156
  const notification = merged.mode === 'task-notification';
152
157
  const execution =
153
158
  notification && merged.execution && typeof merged.execution === 'object' ? { ...merged.execution } : null;
154
- messages.push({
159
+ const message = {
155
160
  role: 'user',
156
161
  content: merged.content,
157
162
  meta: {
158
163
  source: notification ? 'task-notification' : 'steering',
159
164
  ...(execution ? { execution } : {}),
160
165
  ...(submissionIds.length ? { submissionIds } : {}),
161
- ...(steeringTranscriptMeta ? { transcript: steeringTranscriptMeta } : {}),
166
+ transcript: steeringTranscriptMeta,
162
167
  },
163
- });
164
- if (!notification) onSkillPrompt(merged.content);
168
+ };
169
+ messages.push(message);
170
+ if (!notification) onSkillPrompt(merged.content, message);
165
171
  const text = merged.text || steeringContentText(merged.content);
166
172
  try {
167
173
  opts.onSteerMessage?.(text, {
@@ -172,9 +178,12 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
172
178
  ...(notification ? { mode: 'task-notification' } : {}),
173
179
  ...(execution ? { execution } : {}),
174
180
  ...(Array.isArray(merged.images) && merged.images.length ? { images: merged.images } : {}),
175
- ...(steeringTranscriptMeta ? { transcriptMeta: steeringTranscriptMeta } : {}),
181
+ transcriptMeta: steeringTranscriptMeta,
182
+ messageId,
176
183
  });
177
- } catch {}
184
+ } catch {
185
+ /* best-effort host callback */
186
+ }
178
187
  return {
179
188
  count: Number(merged.count) || 1,
180
189
  textLength: String(text || '').length,
@@ -224,9 +233,11 @@ export function createSteeringDrain({ messages, opts, sessionId, onSkillPrompt }
224
233
  try {
225
234
  process.stderr.write(
226
235
  `[steer] sess=${sessionId} injected ${stage} user message(s)` +
227
- ` (merged=${totalCount} len=${totalTextLen} waitMs=${Math.max(0, maxQueueWaitMs)})\n`
236
+ ` (merged=${totalCount} len=${totalTextLen} waitMs=${maxQueueWaitMs})\n`
228
237
  );
229
- } catch {}
238
+ } catch {
239
+ /* best-effort diagnostics */
240
+ }
230
241
  }
231
242
  return true;
232
243
  };
@@ -12,6 +12,8 @@ import {
12
12
  filterSkillsExcludingDisabled,
13
13
  isSkillDisabled,
14
14
  skillMissingFeature,
15
+ skillOutsideScope,
16
+ skillOutsideScopeError,
15
17
  } from '../../context/collect.mjs';
16
18
 
17
19
  // Eager-dispatch: tools with readOnlyHint:true in their declaration are safe
@@ -96,6 +98,7 @@ export function viewSkill(cwd, name, session = null) {
96
98
  return `Error: skill "${skillName}" needs the ${missingFeature} built-in feature, which is not installed or is switched off in Extensions → Plugin → Built-in`;
97
99
  }
98
100
  if (isSkillDisabled(skillName)) return `Error: skill "${skillName}" is disabled`;
101
+ if (skillOutsideScope(skillName, cwd)) return skillOutsideScopeError(skillName);
99
102
  const res = loadSkillResource(skillName, cwd);
100
103
  if (!res) return `Error: skill "${skillName}" not found`;
101
104
  // Return the general tool envelope: the model-visible tool_result is the
@@ -40,6 +40,7 @@ export function usageDeltaEvent({
40
40
  model,
41
41
  usage,
42
42
  sendTools,
43
+ toolCalls = 0,
43
44
  }) {
44
45
  return {
45
46
  sessionId,
@@ -51,6 +52,8 @@ export function usageDeltaEvent({
51
52
  usageMetricsEpoch,
52
53
  deltaInput: usage.inputTokens || 0,
53
54
  deltaOutput: usage.outputTokens || 0,
55
+ // Tool calls this response dispatched; the session's cumulative count.
56
+ deltaToolCalls: toolCalls,
54
57
  ...reasoningUsage(usage),
55
58
  deltaPrompt: usage.promptTokens || 0,
56
59
  // Cache delta carried alongside input/output so live metrics reflect
@@ -12,6 +12,17 @@ import {
12
12
 
13
13
  const SYNTHETIC_PROMPT_SOURCES = ['goal-continuation', 'goal-closeout'];
14
14
 
15
+ // A caller-declared prompt source the runtime trusts as provenance of the
16
+ // initial prompt: Goal turns are runtime-synthetic; a task notification (an
17
+ // idle-drained completion, an automation run's agent results) is stored like
18
+ // one the steering/queue drains inject, never as the user speaking — so none
19
+ // of these run explicit skill selection.
20
+ function initialPromptSourceFor(promptSource) {
21
+ if (SYNTHETIC_PROMPT_SOURCES.includes(promptSource)) return { source: promptSource, synthetic: true };
22
+ if (promptSource === PENDING_MODE_TASK_NOTIFICATION) return { source: promptSource };
23
+ return null;
24
+ }
25
+
15
26
  /** Prompt provenance for a drained queue turn: the source plus its execution when one exists. */
16
27
  const promptSourceFields = (source, execution) => {
17
28
  if (!source) return null;
@@ -42,9 +53,7 @@ export function createAskPromptQueue({ sessionId, promptSource }) {
42
53
  // Local FIFO of follow-up prompts drained from the pending-message queue
43
54
  // after each turn — keeps queued `agent type=send` messages in order.
44
55
  const tail = [];
45
- let initialPromptSource = SYNTHETIC_PROMPT_SOURCES.includes(promptSource)
46
- ? { source: promptSource, synthetic: true }
47
- : null;
56
+ let initialPromptSource = initialPromptSourceFor(promptSource);
48
57
 
49
58
  /** The next turn's input — prompt, caller context, consumed queue entries
50
59
  * and provenance — or null when nothing model-visible is left to run. */
@@ -40,13 +40,23 @@ const traceAskStart = (sessionId, explicitPrefetch) => {
40
40
 
41
41
  /** The transcript timestamps the caller supplied for this ask: the persisted
42
42
  * user metadata plus a one-shot reader for the assistant metadata. */
43
- function createAskTranscriptMeta(rawTranscriptMeta) {
43
+ export function createAskTranscriptMeta(rawTranscriptMeta) {
44
44
  return {
45
45
  raw: rawTranscriptMeta,
46
46
  user: persistedUserTranscriptMetadata(rawTranscriptMeta),
47
47
  takeAssistant: () => {
48
+ const live = rawTranscriptMeta && typeof rawTranscriptMeta === 'object';
49
+ // An id the live row already minted is the one persisted; otherwise the
50
+ // row has not been drawn yet and takes this message's id and time.
51
+ const rowKnowsId =
52
+ live && typeof rawTranscriptMeta.assistantMessageId === 'string' && rawTranscriptMeta.assistantMessageId;
48
53
  const metadata = persistedAssistantTranscriptMetadata(rawTranscriptMeta);
49
- if (rawTranscriptMeta && typeof rawTranscriptMeta === 'object') delete rawTranscriptMeta.assistantAt;
54
+ if (live) {
55
+ // One-shot: the next reply gets its own time and stable id.
56
+ delete rawTranscriptMeta.assistantAt;
57
+ delete rawTranscriptMeta.assistantMessageId;
58
+ rawTranscriptMeta.persistedAssistant = rowKnowsId ? null : { messageId: metadata.messageId, at: metadata.at };
59
+ }
50
60
  return metadata;
51
61
  },
52
62
  };
@@ -138,7 +148,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
138
148
  // queued send always lands AFTER the in-flight prompt here, ordering is
139
149
  // preserved and the spawn/connecting startup race disappears.
140
150
  for (;;) {
141
- const input = queue.nextTurn({ prompt, context, explicitPrefetch });
151
+ // `skillSelection` (askOpts) is the trusted selection input of the
152
+ // caller's own prompt only; drained follow-up turns never inherit it.
153
+ const input = queue.nextTurn({ prompt, context, explicitPrefetch, skillSelection: askOpts.skillSelection });
142
154
  if (!input) {
143
155
  void takeoverHydration.then((count) => {
144
156
  if (count <= 0) return;
@@ -8,7 +8,12 @@
8
8
  // model declares effort levels (the effort is sent with each request).
9
9
  import { readSection } from '../../../../shared/config.mjs';
10
10
  import { autoEffortModel, turnEffortConfigurationMode } from '../../providers/effort-configuration.mjs';
11
- import { autoEffortBase, normalizeAutoEffortMode, resolveAutoEffort } from '../../providers/auto-effort.mjs';
11
+ import {
12
+ autoEffortApplicable,
13
+ autoEffortBase,
14
+ normalizeAutoEffortMode,
15
+ resolveAutoEffort,
16
+ } from '../../providers/auto-effort.mjs';
12
17
  import { builtinFeatureActive } from '../../runtime-core/builtin-features.mjs';
13
18
  import { effortOptionsFor } from '../../runtime-core/effort.mjs';
14
19
  import { effortJudgeSupportsSteps, judgeTurn, recordEffortDecision } from '../../../../effort-judge/judge-client.mjs';
@@ -70,18 +75,14 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
70
75
  // Agent sessions are judged like any other: their spawn brief and later Lead
71
76
  // `send` messages arrive as plain prompts.
72
77
  if (mode === 'off' || input?.promptSource) return null;
78
+ // The session's route turned Auto effort off: its chosen effort stands.
79
+ if (session?.autoEffort === false) return null;
73
80
  const request = promptContentText(input?.prompt).trim();
74
81
  // Skill bodies and runtime blocks arrive as tagged text, not as a request.
75
82
  if (!request || request.startsWith('<')) return null;
76
83
  if (!turnEffortConfigurationMode(session, provider)) return null;
77
84
  const chosen = String(session.effort || '').toLowerCase();
78
85
  const base = autoEffortBase(chosen);
79
- // The previous user request shows the work a short follow-up continues.
80
- const judged = await judgeTurn({
81
- request,
82
- prev: lastText(session.messages, 'assistant'),
83
- prevRequest: lastText(session.messages, 'user'),
84
- });
85
86
  const record = {
86
87
  at: new Date().toISOString(),
87
88
  sessionId,
@@ -92,15 +93,24 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
92
93
  base,
93
94
  requestChars: request.length,
94
95
  };
96
+ const options = effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model));
97
+ // No judged outcome can apply when the base is off the model's ladder, so
98
+ // the judge is not asked (in observe mode too: nothing would be logged).
99
+ if (!autoEffortApplicable({ base, options })) {
100
+ recordEffortDecision({ ...record, skipped: 'default-outside-auto-range' });
101
+ return null;
102
+ }
103
+ // The previous user request shows the work a short follow-up continues.
104
+ const judged = await judgeTurn({
105
+ request,
106
+ prev: lastText(session.messages, 'assistant'),
107
+ prevRequest: lastText(session.messages, 'user'),
108
+ });
95
109
  if (!judged.probs) {
96
110
  recordEffortDecision({ ...record, skipped: judged.skipped });
97
111
  return null;
98
112
  }
99
- const resolved = resolveAutoEffort({
100
- base,
101
- options: effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model)),
102
- probs: judged.probs,
103
- });
113
+ const resolved = resolveAutoEffort({ base, options, probs: judged.probs });
104
114
  recordEffortDecision({
105
115
  ...record,
106
116
  ...(resolved || { skipped: 'default-outside-auto-range' }),
@@ -5,7 +5,7 @@
5
5
  // (runAskAgentLoop). Provider-not-available and trim failures throw here so
6
6
  // the caller marks the session errored instead of leaving stage='connecting'.
7
7
  import { getProvider } from '../../providers/registry.mjs';
8
- import { prepareExplicitSkills } from '../explicit-skills.mjs';
8
+ import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
9
9
  import { prepareTurnEffortConfiguration } from '../../providers/effort-configuration.mjs';
10
10
  import { resolveTurnAutoEffort, turnStepAutoEffort } from './ask-turn-auto-effort.mjs';
11
11
  import { saveSessionAsync } from '../store.mjs';
@@ -151,9 +151,16 @@ export async function runAskAgentLoop({
151
151
  }
152
152
  try {
153
153
  if (!input.promptSource) {
154
- await prepareExplicitSkills(input.prompt, outgoing, session, { cwd: effectiveCwd, signal: turnSignal });
154
+ const applied = await prepareExplicitSkills(input.prompt, outgoing, session, {
155
+ cwd: effectiveCwd,
156
+ signal: turnSignal,
157
+ selection: input.skillSelection,
158
+ });
159
+ // The user turn message sits right after the history it was built on.
160
+ const userMessage = recordAppliedSkills(outgoing, outgoing[prepared.beforeCount - 1], applied);
155
161
  session.messages = filterModelVisibleSessionMessages(outgoing);
156
162
  checkpoint.schedule(true);
163
+ notifySkillsApplied(askOpts?.onSkillsApplied, applied, userMessage);
157
164
  }
158
165
  return await _api_call_with_interrupt(sessionId, (signal) =>
159
166
  agentLoop(
@@ -13,7 +13,9 @@ const optionalFn = (value) => (typeof value === 'function' ? value : undefined);
13
13
  const relay = (fn, ...args) => {
14
14
  try {
15
15
  fn?.(...args);
16
- } catch {}
16
+ } catch {
17
+ /* a host callback must never break the turn */
18
+ }
17
19
  };
18
20
 
19
21
  /**
@@ -70,10 +72,10 @@ export function buildAgentLoopOptions({
70
72
  checkpoint.schedule();
71
73
  if (typeof askOpts?.onReasoningDelta === 'function') askOpts.onReasoningDelta(chunk);
72
74
  },
73
- onAssistantText: (text) => {
75
+ onAssistantText: (text, detail) => {
74
76
  interruption.recordAssistantText(text);
75
77
  checkpoint.schedule(true);
76
- if (typeof askOpts?.onAssistantText === 'function') askOpts.onAssistantText(text);
78
+ if (typeof askOpts?.onAssistantText === 'function') askOpts.onAssistantText(text, detail);
77
79
  },
78
80
  takeAssistantTranscriptMetadata,
79
81
  // Send-opt only. Never copy onto `session` — a transient
@@ -150,6 +152,10 @@ export function buildAgentLoopOptions({
150
152
  checkpoint.schedule(true);
151
153
  relay(askOpts?.onSteerMessage, text, detail);
152
154
  },
155
+ onSkillsApplied: (names, detail) => {
156
+ checkpoint.schedule(true);
157
+ relay(askOpts?.onSkillsApplied, names, detail);
158
+ },
153
159
  notifyFn: optionalFn(askOpts?.notifyFn),
154
160
  // Same projection compaction uses, so a session presents one identity on
155
161
  // every request it makes.
@@ -22,11 +22,13 @@ import { recordProviderContextBaseline } from '../loop/compact-policy.mjs';
22
22
  import { throwIfAborted } from '../../../../shared/abort-race.mjs';
23
23
  import { appendAssistantTurnMessage, persistProviderState, recordAskUsage } from './ask-turn-commit.mjs';
24
24
 
25
- function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt = 0) {
25
+ export function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt = 0) {
26
26
  if (!Array.isArray(messages) || !completion || typeof completion !== 'object') return false;
27
27
  const elapsedMs = Math.max(0, Number(completion.elapsedMs || 0));
28
28
  const status = typeof completion.status === 'string' && completion.status ? completion.status : 'done';
29
29
  const verb = typeof completion.verb === 'string' && completion.verb ? completion.verb : 'Thought';
30
+ const checkpointId =
31
+ typeof completion.checkpointId === 'string' && completion.checkpointId ? completion.checkpointId : '';
30
32
  let turnStart = -1;
31
33
  const expectedAt = Number(turnStartedAt || 0);
32
34
  if (expectedAt > 0) {
@@ -60,7 +62,7 @@ function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt
60
62
  ...meta,
61
63
  transcript: {
62
64
  ...transcript,
63
- completion: { status, verb, elapsedMs },
65
+ completion: { status, verb, elapsedMs, ...(checkpointId ? { checkpointId } : {}) },
64
66
  },
65
67
  },
66
68
  };
@@ -145,6 +147,7 @@ export function publishAskTurn({ sessionId, opened, terminalResultPreview, askOp
145
147
  status: 'done',
146
148
  verb: rawTranscriptMeta?.completionVerb,
147
149
  elapsedMs: Date.now() - turnStartedAt,
150
+ checkpointId: rawTranscriptMeta?.checkpointId,
148
151
  },
149
152
  turnStartedAt
150
153
  );
@@ -15,6 +15,7 @@ import {
15
15
  } from '../context-utils.mjs';
16
16
  import { previewFreshContextCompaction, runFreshContextCompact } from '../loop/fresh-context.mjs';
17
17
  import { positiveInt } from '../../../../shared/numbers.mjs';
18
+ import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
18
19
  import { traceAgentCompact, messagePrefixHash } from '../../agent-trace.mjs';
19
20
  import { uncachedInputTokensForProvider } from './usage-metrics.mjs';
20
21
  import { pruneOffloadSession } from '../tool-result-offload.mjs';
@@ -33,6 +34,7 @@ import {
33
34
  shouldCompactForRequestMedia,
34
35
  } from '../loop/compact-policy.mjs';
35
36
  import { snapshotProviderRequestTools } from '../../runtime-core/tool-catalog.mjs';
37
+ import { scheduleCompactionArchive, stampCompactionMessageIds } from '../compaction-archive-capture.mjs';
36
38
 
37
39
  // 'compacting' is a transient in-flight stage written just before Compact
38
40
  // runs. If the process crashes or only partially
@@ -126,7 +128,7 @@ function handoffSummaryTimeoutMs(session, messageTokens) {
126
128
  * mutated and nothing is persisted — so a caller may compact a COPY of a
127
129
  * conversation. Session inheritance relies on exactly that: an oversized
128
130
  * transcript is compacted for the heir while the session that owns it keeps
129
- * every message (user: 자동으로 컴팩트하고 승계).
131
+ * every message.
130
132
  */
131
133
  export async function runHandoffCompaction({
132
134
  session,
@@ -190,9 +192,8 @@ async function sessionCompactionPlan(session, opts) {
190
192
  }
191
193
  // Reserve must mirror loop.mjs (buildCompactPolicy): request reserve (tool
192
194
  // schema) PLUS the configured reserve (session.compaction.reservedTokens or
193
- // MIXDOG_AGENT_COMPACT_RESERVED_TOKENS env). The old request-only value left
194
- // the manual / auto-clear compact budget without the configured headroom the
195
- // loop path reserves, so a compacted transcript could overflow on next send.
195
+ // MIXDOG_AGENT_COMPACT_RESERVED_TOKENS env). Without the configured headroom
196
+ // the loop path reserves, a compacted transcript could overflow on next send.
196
197
  // A cold (just loaded) transcript is metered in slices first; the plan
197
198
  // numbers below then read the per-message memos.
198
199
  await primeContextEstimates(messages, session);
@@ -202,6 +203,7 @@ async function sessionCompactionPlan(session, opts) {
202
203
  resolvedSessionId,
203
204
  mode,
204
205
  force,
206
+ archive: opts.archive !== false,
205
207
  messages,
206
208
  boundary,
207
209
  alignedPolicy,
@@ -292,8 +294,8 @@ function compactionRecord(plan, { stage, after, now, reserve, outcome }) {
292
294
  }
293
295
 
294
296
  // compact_meta parity with the loop's pre-send pass: the out-of-loop
295
- // (post-turn/manual) compaction — and its failure — used to be invisible to
296
- // trace analytics.
297
+ // (post-turn/manual) compaction — and its failure — is visible to trace
298
+ // analytics.
297
299
  function traceSessionCompaction(plan, compactStartedAt, { counts, failure }) {
298
300
  const { session, mode } = plan;
299
301
  traceAgentCompact({
@@ -342,13 +344,9 @@ async function runSessionHandoff(plan, opts) {
342
344
  } catch (err) {
343
345
  run.freshContextError = err;
344
346
  run.compactError = err;
345
- try {
346
- process.stderr.write(
347
- `[session] fresh-context ${mode} compact failed (sess=${session.id || 'unknown'}): ${err?.message || err}\n`
348
- );
349
- } catch {
350
- /* best-effort */
351
- }
347
+ writeStderrBestEffort(
348
+ `[session] fresh-context ${mode} compact failed (sess=${session.id || 'unknown'}): ${err?.message || err}\n`
349
+ );
352
350
  }
353
351
  if (!run.compacted && !run.compactError) {
354
352
  run.compactError = new Error('fresh-context compact produced no messages');
@@ -515,8 +513,8 @@ function compactionOutcome(session, freshContextResult, changed, now) {
515
513
  }
516
514
 
517
515
  // Observability parity with the loop's pre-send pass: record the
518
- // out-of-loop mutation as compact_meta (observed live: a 403k→10k post-turn
519
- // compact traced as intentional_transition: null with no compact_meta).
516
+ // out-of-loop mutation as compact_meta, so a large post-turn compact is not
517
+ // traced as intentional_transition: null.
520
518
  function traceCommittedCompaction(plan, compactStartedAt, { messages, compacted, beforeBytes, afterBytes, changed }) {
521
519
  let beforePrefixHash = null;
522
520
  try {
@@ -645,6 +643,19 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
645
643
  },
646
644
  outcome: compactionOutcome(session, freshContextResult, changed, now),
647
645
  });
646
+ // Display-only: the dropped conversation stays readable above the divider.
647
+ // Only the selection and the segment number happen here; the projection and
648
+ // the write run later, off this commit.
649
+ const archived =
650
+ changed && plan.archive
651
+ ? scheduleCompactionArchive({
652
+ session,
653
+ sessionId: resolvedSessionId,
654
+ before: messages,
655
+ after: transcript,
656
+ at: now,
657
+ })
658
+ : null;
648
659
  if (changed) {
649
660
  invalidateProviderContextBaseline(session);
650
661
  if (postCompactPolicy) {
@@ -678,6 +689,10 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
678
689
  freshContextError: null,
679
690
  handoffSource: freshContextResult?.handoffSource || 'session-local',
680
691
  usage: freshContextResult?.usage || null,
692
+ // Messages that left the model context (0 for a rewrite-only pass):
693
+ // surfaces archive only those rows.
694
+ droppedMessages: archived?.dropped ?? 0,
695
+ droppedIdentities: archived?.identities ?? null,
681
696
  }
682
697
  );
683
698
  }
@@ -692,7 +707,8 @@ export async function runSessionCompaction(session, opts = {}) {
692
707
  });
693
708
  }
694
709
  plan.requireReduction =
695
- (plan.mode === 'auto' || opts.requireReduction === true) && !shouldCompactForRequestMedia(plan.messages, session.provider);
710
+ (plan.mode === 'auto' || opts.requireReduction === true) &&
711
+ !shouldCompactForRequestMedia(plan.messages, session.provider);
696
712
  const skipped = () =>
697
713
  compactionResult(plan, { changed: false, skipped: true, reason: 'no_token_reduction' }, unchangedAfter(plan), {
698
714
  freshContext: false,
@@ -713,6 +729,11 @@ export async function runSessionCompaction(session, opts = {}) {
713
729
  }).reducesTokens
714
730
  )
715
731
  return skipped();
732
+ // Every message enters the compaction with a stable id (legacy ones are
733
+ // stamped now and persisted): the result's rewrites carry it, so what was
734
+ // dropped is exactly the ids the result no longer holds. The snapshot is a
735
+ // prefix of the live transcript holding the same message objects.
736
+ stampCompactionMessageIds(plan.messages, plan.resolvedSessionId);
716
737
  const compactStartedAt = Date.now();
717
738
  try {
718
739
  await opts.onStageChange?.('compacting');
@@ -117,8 +117,12 @@ export function resolveSessionContextMeta(provider, model, seed = {}, { wholeWin
117
117
  // (a session older than selectedContextWindow, a route saved before the
118
118
  // catalog warmed) is still the user's choice and keeps the served window.
119
119
  const recordsSelection = wholeWindow || boundedPercent(seed.contextPercent) !== null;
120
+ // An exact token selection without its resolved window still names it,
121
+ // bounded by what the model serves.
122
+ const selectedTokens = positiveInt(seed.contextTokens);
120
123
  const requestedContextWindow =
121
124
  positiveInt(seed.selectedContextWindow) ||
125
+ (selectedTokens ? Math.min(selectedTokens, servedContextWindow) : 0) ||
122
126
  (recordsSelection
123
127
  ? servedContextWindow
124
128
  : contextWindowRange({ provider: providerNameOf(provider), contextWindow: servedContextWindow }).defaultWindow);
@@ -158,6 +158,14 @@ export function lateDeliveryText(text, entry, now = Date.now()) {
158
158
  return `[late delivery: queued ${age} ago, before the current session owner started]\n${value}`;
159
159
  }
160
160
 
161
+ // A non-completion entry may be queued explicitly as a task notification (a
162
+ // runtime/channel notice, never the user typing): the mode survives
163
+ // normalization and the durable spool so the drain stores it as one and it
164
+ // never drives explicit skill selection.
165
+ function explicitModeField(entry) {
166
+ return entry?.mode === PENDING_MODE_TASK_NOTIFICATION ? { mode: PENDING_MODE_TASK_NOTIFICATION } : {};
167
+ }
168
+
161
169
  export function normalizePendingMessageEntry(entry) {
162
170
  if (typeof entry === 'string') {
163
171
  const text = entry.trim();
@@ -173,7 +181,10 @@ export function normalizePendingMessageEntry(entry) {
173
181
  id: pendingMessageId(entry),
174
182
  enqueuedAt: Number(entry.enqueuedAt) || Date.now(),
175
183
  };
176
- const entryOptions = entry.options && typeof entry.options === 'object' ? { options: entry.options } : {};
184
+ const entryOptions = {
185
+ ...(entry.options && typeof entry.options === 'object' ? { options: entry.options } : {}),
186
+ ...explicitModeField(entry),
187
+ };
177
188
  const marker =
178
189
  entry.notificationKind === COMPLETION_NOTIFICATION_KIND
179
190
  ? {
@@ -259,26 +270,28 @@ function normalizePersistedEntryBase(entry) {
259
270
  }
260
271
  : null;
261
272
  }
273
+ const mode = explicitModeField(entry);
262
274
  if (typeof entry.message === 'string') {
263
275
  const message = entry.message.trim();
264
- return message ? { id, message, enqueuedAt } : null;
276
+ return message ? { id, message, ...mode, enqueuedAt } : null;
265
277
  }
266
278
  if (Object.hasOwn(entry, 'content')) {
267
279
  const normalized = normalizePendingMessageEntry(entry);
268
280
  if (!normalized) return null;
269
281
  if (typeof normalized.content === 'string' && !normalized.options) {
270
- return { id, message: normalized.text || normalized.content, enqueuedAt };
282
+ return { id, message: normalized.text || normalized.content, ...mode, enqueuedAt };
271
283
  }
272
284
  return {
273
285
  id,
274
286
  content: normalized.content,
275
287
  text: normalized.text,
276
288
  ...(normalized.options ? { options: normalized.options } : {}),
289
+ ...mode,
277
290
  enqueuedAt,
278
291
  };
279
292
  }
280
293
  const text = pendingMessageText(entry);
281
- return text ? { id, message: text, enqueuedAt } : null;
294
+ return text ? { id, message: text, ...mode, enqueuedAt } : null;
282
295
  }
283
296
 
284
297
  export function modelVisiblePendingMessages(messages) {
@@ -185,6 +185,96 @@ export function refreshSessionBp3Environment(session, cwd) {
185
185
  return true;
186
186
  }
187
187
 
188
+ const CORE_MEMORY_HEADING = '# Core Memory\n';
189
+ const INSTRUCTION_FILES_HEADING = '# Instruction Files';
190
+
191
+ function trimmedSlice(value) {
192
+ return typeof value === 'string' ? value.trim() : '';
193
+ }
194
+
195
+ // Where a heading-led slice starts in a BP3 join: `cut` ends the text before
196
+ // it (separator included), `start` is the slice's first character.
197
+ function bp3SliceAt(text, heading) {
198
+ if (text.startsWith(heading)) return { cut: 0, start: 0 };
199
+ const at = text.indexOf(`${BP3_PART_SEPARATOR}${heading}`);
200
+ return at < 0 ? null : { cut: at, start: at + BP3_PART_SEPARATOR.length };
201
+ }
202
+
203
+ /**
204
+ * Replace the workspace-scoped tail of a BP3 core (composeSystemPrompt order:
205
+ * …, `# Core Memory`, `# Instruction Files`). The instruction-files slice is
206
+ * always last and may itself contain separators, so it is cut by position;
207
+ * the core-memory slice is one separator-free part. `undefined` keeps a slice,
208
+ * an empty string drops it.
209
+ * @param {string} core
210
+ * @param {{ coreMemoryContext?: string, instructionFilesContext?: string }} slices
211
+ */
212
+ export function replaceWorkspaceCoreSlices(core, { coreMemoryContext, instructionFilesContext } = {}) {
213
+ let head = String(core || '');
214
+ let instructionFiles = '';
215
+ const instructionAt = bp3SliceAt(head, INSTRUCTION_FILES_HEADING);
216
+ if (instructionAt) {
217
+ instructionFiles = head.slice(instructionAt.start);
218
+ head = head.slice(0, instructionAt.cut);
219
+ }
220
+ let parts = head ? head.split(BP3_PART_SEPARATOR) : [];
221
+ if (coreMemoryContext !== undefined) {
222
+ parts = parts.filter((part) => !part.startsWith(CORE_MEMORY_HEADING));
223
+ const memory = trimmedSlice(coreMemoryContext);
224
+ if (memory) parts.push(`${CORE_MEMORY_HEADING}${memory}`);
225
+ }
226
+ if (instructionFilesContext !== undefined) instructionFiles = trimmedSlice(instructionFilesContext);
227
+ return [...parts, instructionFiles].filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
228
+ }
229
+
230
+ /**
231
+ * Replace the shell/project facts that lead the persisted environment tail
232
+ * (`- Cwd …`, shell tools, git state, cwd listing); the trailing response
233
+ * language block is kept. `undefined` keeps the facts.
234
+ */
235
+ export function replaceWorkspaceEnvironment(environmentTail, environmentContext) {
236
+ if (environmentContext === undefined) return environmentTail;
237
+ const parts = typeof environmentTail === 'string' && environmentTail ? environmentTail.split(BP3_PART_SEPARATOR) : [];
238
+ const facts = trimmedSlice(environmentContext);
239
+ if (parts[0]?.startsWith('- ')) parts[0] = facts;
240
+ else parts.unshift(facts);
241
+ return parts.filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
242
+ }
243
+
244
+ /**
245
+ * Re-root an existing session's prompt at a new workspace without touching
246
+ * the conversation: environment facts, project memory and instruction files
247
+ * are replaced in place (never appended), then the `# Session` block is
248
+ * rebuilt for `cwd`. Messages are replaced, not mutated, so delta saves write
249
+ * a full snapshot.
250
+ * @param {object} session
251
+ * @param {{ cwd: string, environmentContext?: string, coreMemoryContext?: string, instructionFilesContext?: string }} context
252
+ * @returns {boolean}
253
+ */
254
+ export function refreshSessionWorkspaceContext(
255
+ session,
256
+ { cwd, environmentContext, coreMemoryContext, instructionFilesContext } = {}
257
+ ) {
258
+ if (!session) return false;
259
+ if (typeof session.bp3EnvironmentContext === 'string') {
260
+ session.bp3EnvironmentContext = replaceWorkspaceEnvironment(session.bp3EnvironmentContext, environmentContext);
261
+ }
262
+ const slices = { coreMemoryContext, instructionFilesContext };
263
+ if (session.bp3EnvSplit === true) {
264
+ const target = bp3SystemMessage(session);
265
+ if (target && typeof target.content === 'string') {
266
+ const nextCore = replaceWorkspaceCoreSlices(target.content, slices);
267
+ replaceSystemMessageContent(session, target, nextCore);
268
+ session.bp3CoreContext = nextCore;
269
+ }
270
+ } else if (typeof session.bp3CoreContext === 'string') {
271
+ // Legacy combined layout: refreshSessionBp3Environment rebuilds the tier3
272
+ // block from the core plus the environment below.
273
+ session.bp3CoreContext = replaceWorkspaceCoreSlices(session.bp3CoreContext, slices);
274
+ }
275
+ return refreshSessionBp3Environment(session, cwd);
276
+ }
277
+
188
278
  export function resetSessionBp3Environment(session) {
189
279
  if (session?.bp3EnvSplit === true) {
190
280
  const target = bpEnvSystemMessage(session);