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
@@ -25,6 +25,8 @@ import {
25
25
  import { getProvider } from '../../providers/registry.mjs';
26
26
  import { isSessionCompactionBlocked, getSessionAbortSignal, _runtimeEntries } from './runtime-liveness.mjs';
27
27
  import { mintSessionId } from './session-id.mjs';
28
+ import { hideCompactionArchive } from '../compaction-archive-capture.mjs';
29
+ import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
28
30
 
29
31
  /** Force-flush session metrics to disk. Used by watchdog terminal-reap (fix B). */
30
32
  export async function flushSessionMetrics(sessionId) {
@@ -97,14 +99,6 @@ export async function updateSessionManualTitle(id, title) {
97
99
  return true;
98
100
  }
99
101
  // --- Clear messages (keep system prompt + provider/model/cwd) ---
100
- function bestEffortStderr(line) {
101
- try {
102
- process.stderr.write(line);
103
- } catch {
104
- /* best-effort */
105
- }
106
- }
107
-
108
102
  // Token/provider accounting a cleared transcript starts from; shared by the
109
103
  // live session and the cold fork of its outgoing transcript.
110
104
  function clearedTokenAccounting(now) {
@@ -112,6 +106,7 @@ function clearedTokenAccounting(now) {
112
106
  providerState: undefined,
113
107
  totalInputTokens: 0,
114
108
  totalOutputTokens: 0,
109
+ totalToolCalls: 0,
115
110
  reasoningUsage: null,
116
111
  totalCachedReadTokens: 0,
117
112
  totalCacheWriteTokens: 0,
@@ -133,11 +128,18 @@ async function compactBeforeClear(session, sessionId, clearOptions) {
133
128
  let clearCompactError = null;
134
129
  if (messages.length >= 3) {
135
130
  try {
136
- const compactResult = await runSessionCompaction(session, { mode: 'manual', force: true, sessionId });
131
+ // The clear right after discards this compaction's display too, so it
132
+ // archives nothing (clear semantics are unchanged).
133
+ const compactResult = await runSessionCompaction(session, {
134
+ mode: 'manual',
135
+ force: true,
136
+ sessionId,
137
+ archive: false,
138
+ });
137
139
  if (compactResult?.error) clearCompactError = new Error(compactResult.error);
138
140
  } catch (err) {
139
141
  clearCompactError = err;
140
- bestEffortStderr(`[session] auto-clear pre-compact failed (sess=${sessionId}): ${err?.message || err}\n`);
142
+ writeStderrBestEffort(`[session] auto-clear pre-compact failed (sess=${sessionId}): ${err?.message || err}\n`);
141
143
  }
142
144
  messages = Array.isArray(session.messages) ? session.messages : [];
143
145
  }
@@ -208,6 +210,11 @@ function forkOutgoingTranscript(session, messages, now) {
208
210
  ...clearedTokenAccounting(now),
209
211
  };
210
212
  delete fork.liveTurnMessages;
213
+ // The archive sidecar belongs to the source id; the fork has none.
214
+ if (fork.compaction?.archive) {
215
+ fork.compaction = { ...fork.compaction };
216
+ delete fork.compaction.archive;
217
+ }
211
218
  setLiveSession(fork);
212
219
  void saveSessionAsync(fork)
213
220
  .then(() => {
@@ -219,10 +226,10 @@ function forkOutgoingTranscript(session, messages, now) {
219
226
  evictLiveSession(forkId);
220
227
  })
221
228
  .catch((err) => {
222
- bestEffortStderr(`[session] clear-fork save failed (sess=${forkId}): ${err?.message || err}\n`);
229
+ writeStderrBestEffort(`[session] clear-fork save failed (sess=${forkId}): ${err?.message || err}\n`);
223
230
  });
224
231
  } catch (err) {
225
- bestEffortStderr(`[session] clear-fork failed (sess=${session.id}): ${err?.message || err}\n`);
232
+ writeStderrBestEffort(`[session] clear-fork failed (sess=${session.id}): ${err?.message || err}\n`);
226
233
  }
227
234
  }
228
235
 
@@ -277,6 +284,9 @@ export async function clearSessionMessages(sessionId, options = {}) {
277
284
  lastClearAfterMessageTokens: tokens.afterMessageTokens,
278
285
  lastClearCompactError: clearCompactError?.message || null,
279
286
  };
287
+ // A clear wipes the visible conversation: earlier compactions' archived
288
+ // display goes with it (nothing new is archived).
289
+ hideCompactionArchive(session);
280
290
  if (summaryCarriedForward && tokens.postClearPolicy) {
281
291
  recordContextUsageSnapshot(session, tokens.postClearPolicy, {
282
292
  messages: keep,
@@ -32,11 +32,13 @@ import {
32
32
  unusedModelEditToolName,
33
33
  } from '../../../../shared/edit-tool-dialect.mjs';
34
34
  import { resolveSessionContextMeta } from './context-meta.mjs';
35
+ import { normalizeContextTokens } from '../../../../shared/llm/default-context-window.mjs';
35
36
  import { getAgentRuntimeSync, warnAgentRuntimeResolveFailureOnce } from './agent-runtime-singleton.mjs';
36
37
  import { ensureCodexWireSessionId, mintSessionId } from './session-id.mjs';
37
38
  import { providerCacheKey } from './provider-cache-key.mjs';
38
39
  import { buildSessionRecord, normalizeDesktopSessionMetadata, sessionOriginFields } from './session-record.mjs';
39
40
  import { refreshSessionBp3Environment } from './prompt-utils.mjs';
41
+ import { isWorktreeBound } from '../../../../shared/worktrees/contract.mjs';
40
42
 
41
43
  // Owner liveness (attach-on-resume guard) and the durable turn-checkpoint
42
44
  // recovery it gates; re-exported so prior importers of this module stay
@@ -153,6 +155,8 @@ function resolveSessionRoute(opts, presetObj, profile) {
153
155
  const toolPreset = opts.tools || presetObj?.tools || (typeof opts.preset === 'string' ? opts.preset : null) || 'full';
154
156
  const effort = Object.hasOwn(opts, 'effort') ? opts.effort || null : presetObj?.effort || null;
155
157
  const fast = presetObj?.fast === true || opts.fast === true;
158
+ // Absent means on: an explicit request wins, else the preset's own flag.
159
+ const autoEffort = opts.autoEffort !== undefined ? opts.autoEffort !== false : presetObj?.autoEffort !== false;
156
160
  let modelParameters = {};
157
161
  if (opts.modelParameters && typeof opts.modelParameters === 'object') {
158
162
  modelParameters = { ...opts.modelParameters };
@@ -160,6 +164,7 @@ function resolveSessionRoute(opts, presetObj, profile) {
160
164
  modelParameters = { ...presetObj.modelParameters };
161
165
  }
162
166
  const contextPercent = normalizeContextPercent(opts.contextPercent);
167
+ const contextTokens = normalizeContextTokens(opts.contextTokens) || null;
163
168
  if (!providerName) throw new Error('createSession: provider is required');
164
169
  if (!modelName) throw new Error('createSession: model is required');
165
170
  const provider = getProvider(providerName);
@@ -172,7 +177,19 @@ function resolveSessionRoute(opts, presetObj, profile) {
172
177
  // materialization. Supplying that reservation here keeps the address
173
178
  // stable across intake -> queued turn -> provider execution.
174
179
  const id = requestedId || mintSessionId();
175
- return { providerName, modelName, provider, toolPreset, effort, fast, modelParameters, contextPercent, id };
180
+ return {
181
+ providerName,
182
+ modelName,
183
+ provider,
184
+ toolPreset,
185
+ effort,
186
+ fast,
187
+ autoEffort,
188
+ modelParameters,
189
+ contextPercent,
190
+ contextTokens,
191
+ id,
192
+ };
176
193
  }
177
194
 
178
195
  // Unified-shard policy — no broad role-specific schema filter. Keep
@@ -213,6 +230,7 @@ export function createSession(opts) {
213
230
  const contextMeta = resolveSessionContextMeta(provider, modelName, {
214
231
  selectedContextWindow: opts.selectedContextWindow,
215
232
  contextPercent: route.contextPercent,
233
+ contextTokens: route.contextTokens,
216
234
  });
217
235
  const session = buildSessionRecord({
218
236
  opts,
@@ -287,6 +305,10 @@ function applyRouteFields(session, route) {
287
305
  if (route.provider) session.provider = route.provider;
288
306
  if (route.model) session.model = route.model;
289
307
  if (Object.hasOwn(route, 'fast')) session.fast = route.fast === true;
308
+ if (Object.hasOwn(route, 'autoEffort')) {
309
+ if (route.autoEffort === false) session.autoEffort = false;
310
+ else delete session.autoEffort;
311
+ }
290
312
  if (Object.hasOwn(route, 'effort')) session.effort = route.effort || null;
291
313
  ensureCodexWireSessionId(session);
292
314
  if (Object.hasOwn(route, 'modelParameters')) {
@@ -294,6 +316,8 @@ function applyRouteFields(session, route) {
294
316
  route.modelParameters && typeof route.modelParameters === 'object' ? { ...route.modelParameters } : {};
295
317
  }
296
318
  if (Object.hasOwn(route, 'contextPercent')) session.contextPercent = normalizeContextPercent(route.contextPercent);
319
+ if (Object.hasOwn(route, 'contextTokens'))
320
+ session.contextTokens = normalizeContextTokens(route.contextTokens) || null;
297
321
  if (Object.hasOwn(route, 'selectedContextWindow')) {
298
322
  session.selectedContextWindow = Number(route.selectedContextWindow) || null;
299
323
  }
@@ -441,13 +465,18 @@ function _applyDesktopResumeScope(session, options) {
441
465
  }
442
466
  // The host's summary may predate a cwd change. It supplies the
443
467
  // classification check, not permission to overwrite the session's
444
- // newer execution Project. Task metadata always remains pathless.
468
+ // newer execution Project. Task metadata always remains pathless. A Lead
469
+ // in its worktree executes outside the Project: its registered Project
470
+ // stays the workspace record's projectPath, never the worktree cwd.
471
+ const workspace = session.workspace;
472
+ const executionProject =
473
+ isWorktreeBound(workspace) && typeof workspace.projectPath === 'string' ? workspace.projectPath : session.cwd;
445
474
  session.desktopSession =
446
475
  expectedDesktop.classification === 'project'
447
476
  ? normalizeDesktopSessionMetadata(
448
477
  {
449
478
  ...expectedDesktop,
450
- projectPath: session.cwd || expectedDesktop.projectPath,
479
+ projectPath: executionProject || expectedDesktop.projectPath,
451
480
  },
452
481
  session.cwd
453
482
  )
@@ -60,6 +60,33 @@ function buildShellEnvironmentContext(opts, ownerIsAgent, toolsForRouting) {
60
60
  .join('\n');
61
61
  }
62
62
 
63
+ /**
64
+ * Workspace-scoped prompt facts for an existing session re-rooted at `cwd`
65
+ * (a Lead workspace change or a worker following its Lead): the shell/git/
66
+ * listing environment and the instruction files of the new root, derived
67
+ * exactly as at creation. `undefined` means "keep what the session has": a
68
+ * tool-free role carries neither, a profile skipping instruction files keeps
69
+ * none.
70
+ * @returns {{ environmentContext: string|undefined, instructionFilesContext: string|undefined }}
71
+ */
72
+ export function buildWorkspacePromptContext(session, cwd) {
73
+ const ownerIsAgent = session?.owner === 'agent';
74
+ const toolFreeRole =
75
+ ownerIsAgent && Array.isArray(session?.schemaAllowedTools) && session.schemaAllowedTools.length === 0;
76
+ if (toolFreeRole) return { environmentContext: undefined, instructionFilesContext: undefined };
77
+ const tools = [
78
+ ...(Array.isArray(session?.tools) ? session.tools : []),
79
+ ...(Array.isArray(session?.deferredToolCatalog) ? session.deferredToolCatalog : []),
80
+ ];
81
+ return {
82
+ environmentContext: buildShellEnvironmentContext({ cwd }, ownerIsAgent, tools),
83
+ instructionFilesContext:
84
+ session?.skipInstructionFiles === true
85
+ ? undefined
86
+ : loadInstructionFilesContext({ mode: configuredInstructionFilesMode(), cwd }),
87
+ };
88
+ }
89
+
63
90
  // BP1/BP2/BP3 rule blocks plus the volatile environment, composed into the
64
91
  // four system blocks; environmentTailContext is the persisted env tail.
65
92
  export function composeSessionSystem(opts, { profile, providerName, modelName, surface }) {
@@ -92,7 +92,18 @@ export function sessionOriginFields(opts, { profile, presetObj, providerCacheOpt
92
92
 
93
93
  // The persisted session record.
94
94
  export function buildSessionRecord({ opts, route, presetObj, surface, prompt, messages, contextMeta, origin }) {
95
- const { providerName, modelName, toolPreset, effort, fast, modelParameters, contextPercent, id } = route;
95
+ const {
96
+ providerName,
97
+ modelName,
98
+ toolPreset,
99
+ effort,
100
+ fast,
101
+ autoEffort,
102
+ modelParameters,
103
+ contextPercent,
104
+ contextTokens,
105
+ id,
106
+ } = route;
96
107
  return {
97
108
  id,
98
109
  codexWireSessionId: providerName === 'openai-oauth' ? mintUuidV7() : null,
@@ -115,8 +126,11 @@ export function buildSessionRecord({ opts, route, presetObj, surface, prompt, me
115
126
  presetName: presetObj?.name || null,
116
127
  effort,
117
128
  fast,
129
+ // Absent means Auto effort is on; only the off state is persisted.
130
+ ...(autoEffort === false ? { autoEffort: false } : {}),
118
131
  modelParameters,
119
132
  contextPercent,
133
+ ...(contextTokens ? { contextTokens } : {}),
120
134
  selectedContextWindow: opts.selectedContextWindow || null,
121
135
  agent: opts.agent,
122
136
  owner: opts.owner || 'user',
@@ -144,6 +158,7 @@ export function buildSessionRecord({ opts, route, presetObj, surface, prompt, me
144
158
  lastHeartbeatAt: null,
145
159
  totalInputTokens: 0,
146
160
  totalOutputTokens: 0,
161
+ totalToolCalls: 0,
147
162
  // Refreshed on each completed ask() — surfaced by agent type=list for
148
163
  // debugging + consumed by store.mjs's idle-sweep to reclaim stalled
149
164
  // agent sessions past RUNNING_STALL_MS.
@@ -35,6 +35,7 @@ function standaloneStatusRouteInfo(session) {
35
35
  modelDisplay: session.modelDisplay || session.displayName || session.model,
36
36
  effort: session.effort || '',
37
37
  fast: session.fast === true,
38
+ ...(session.autoEffort === false ? { autoEffort: false } : {}),
38
39
  modelParameters: session.modelParameters || {},
39
40
  contextWindow: session.contextWindow || null,
40
41
  rawContextWindow: session.rawContextWindow || session.contextWindow || null,
@@ -390,6 +390,7 @@ export function applyAskTerminalUsageTotals(session, result, options = {}) {
390
390
  accumulateSessionUsage(session, {
391
391
  deltaInput: result.usage.inputTokens || 0,
392
392
  deltaOutput: result.usage.outputTokens || 0,
393
+ deltaToolCalls: result.toolCallsTotal || 0,
393
394
  ...reasoningUsage(result.usage),
394
395
  deltaCachedRead: result.usage.cachedTokens || 0,
395
396
  deltaCacheWrite: result.usage.cacheWriteTokens || 0,
@@ -465,6 +466,7 @@ function accumulateSessionUsage(session, delta) {
465
466
  : uncachedInputTokensForProvider(session.provider, deltaInput, deltaCachedRead, deltaCacheWrite);
466
467
  session.totalInputTokens = (session.totalInputTokens || 0) + (deltaInput || 0);
467
468
  session.totalOutputTokens = (session.totalOutputTokens || 0) + (deltaOutput || 0);
469
+ session.totalToolCalls = (session.totalToolCalls || 0) + (delta.deltaToolCalls || 0);
468
470
  session.tokensCumulative = (session.tokensCumulative || 0) + (deltaInput || 0) + (deltaOutput || 0);
469
471
  session.totalCachedReadTokens = (session.totalCachedReadTokens || 0) + (deltaCachedRead || 0);
470
472
  session.totalCacheWriteTokens = (session.totalCacheWriteTokens || 0) + (deltaCacheWrite || 0);
@@ -72,7 +72,6 @@ export {
72
72
  // attach-on-resume guard) — resolved through the manager facade so the
73
73
  // session-runtime turn API reaches it as mgr.publishSessionPresence.
74
74
  export { publishSessionPresence, deleteSessionPresence } from './store.mjs';
75
- export { isInternalRuntimeNotificationText as _isInternalRuntimeNotificationText } from './manager/prompt-utils.mjs';
76
75
 
77
76
  // ── Usage-metrics surface — re-exported unchanged so loop.mjs / smoke scripts
78
77
  // keep resolving these through the facade. ────────────────────────────────
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Stable per-message identity: `meta.transcript.messageId`.
3
+ *
4
+ * Prompts and replies get theirs at creation (shared/transcript-metadata.mjs).
5
+ * Every other message — tool calls and results, injected rows, legacy
6
+ * transcripts — is stamped before a compaction runs or a stored session is
7
+ * resumed (stampMessageIds), and every compaction rewrite carries the id of
8
+ * the message it rewrites to its copy (spread copies keep `meta`;
9
+ * constructed copies use carryMessageId). A compaction's dropped messages
10
+ * are then exactly the ids its result no longer holds.
11
+ *
12
+ * A message whose `meta` is a string marker ('skill', 'hook': injected
13
+ * model context that is never displayed) cannot carry an id without changing
14
+ * what that marker means; it stays unstamped and has no display identity.
15
+ *
16
+ * Leaf module: no store, no config.
17
+ */
18
+ import { createHash } from 'node:crypto';
19
+
20
+ /** The message's stable id, or ''. */
21
+ export function messageIdOf(message) {
22
+ const meta = message?.meta;
23
+ if (!meta || typeof meta !== 'object') return '';
24
+ const id = meta.transcript?.messageId;
25
+ return typeof id === 'string' ? id : '';
26
+ }
27
+
28
+ /** Whether `meta` can hold a transcript id (absent or a plain object). */
29
+ export function canCarryMessageId(message) {
30
+ if (!message || typeof message !== 'object') return false;
31
+ const meta = message.meta;
32
+ return meta === undefined || meta === null || (typeof meta === 'object' && !Array.isArray(meta));
33
+ }
34
+
35
+ function withMessageId(meta, messageId) {
36
+ const base = meta && typeof meta === 'object' ? meta : {};
37
+ const transcript = base.transcript && typeof base.transcript === 'object' ? base.transcript : {};
38
+ return { ...base, transcript: { ...transcript, messageId } };
39
+ }
40
+
41
+ // A deterministic seed: the same unstamped transcript gets the same ids on
42
+ // every stamping (a legacy session reopened twice before its stamps are
43
+ // saved keeps them; two compactions of one transcript agree). The content
44
+ // part is bounded — its length and edges, media by kind and size — so
45
+ // stamping never serializes whole images or documents. The position keeps
46
+ // equal messages apart.
47
+ const SEED_EDGE_CHARS = 512;
48
+ function boundedText(value) {
49
+ const text = String(value);
50
+ if (text.length <= SEED_EDGE_CHARS * 2) return text;
51
+ return `${text.slice(0, SEED_EDGE_CHARS)}\u0000${text.length}\u0000${text.slice(-SEED_EDGE_CHARS)}`;
52
+ }
53
+ function boundedContent(content) {
54
+ if (typeof content === 'string') return boundedText(content);
55
+ if (!Array.isArray(content)) return content == null ? '' : typeof content;
56
+ return content
57
+ .map((part) => {
58
+ if (!part || typeof part !== 'object') return String(part);
59
+ if (typeof part.text === 'string') return `t:${boundedText(part.text)}`;
60
+ const data = part.data ?? part.source?.data ?? part.image_url?.url ?? part.url ?? part.ref ?? '';
61
+ return `${part.type || 'part'}:${typeof data === 'string' ? data.length : 0}`;
62
+ })
63
+ .join('\u0001');
64
+ }
65
+ function seededMessageId(sessionId, index, message) {
66
+ const calls = Array.isArray(message.toolCalls) ? message.toolCalls : message.tool_calls;
67
+ const callIds = Array.isArray(calls) ? calls.map((call) => call?.id || '').join(',') : '';
68
+ const seed = [
69
+ sessionId,
70
+ index,
71
+ message.role,
72
+ message.toolCallId || message.tool_call_id || '',
73
+ callIds,
74
+ boundedContent(message.content),
75
+ ].join('\u0002');
76
+ return `msg_${createHash('sha1').update(seed).digest('hex').slice(0, 32)}`;
77
+ }
78
+
79
+ /** Stamp a stable id on every message that can carry one and has none, in
80
+ * place. Returns how many were stamped (the caller persists them). */
81
+ export function stampMessageIds(messages, { sessionId = '' } = {}) {
82
+ if (!Array.isArray(messages)) return 0;
83
+ let stamped = 0;
84
+ const seen = new Set();
85
+ messages.forEach((message, index) => {
86
+ const existing = messageIdOf(message);
87
+ if (existing) {
88
+ seen.add(existing);
89
+ return;
90
+ }
91
+ if (!canCarryMessageId(message)) return;
92
+ let id = seededMessageId(String(sessionId || ''), index, message);
93
+ // Never two messages with one id within a transcript.
94
+ for (let salt = 1; seen.has(id); salt += 1) id = seededMessageId(`${sessionId}#${salt}`, index, message);
95
+ seen.add(id);
96
+ message.meta = withMessageId(message.meta, id);
97
+ stamped += 1;
98
+ });
99
+ return stamped;
100
+ }
101
+
102
+ /** JSON.stringify replacer that leaves stable ids out: hashes and archives
103
+ * the compactor derives from message JSON describe the conversation, not
104
+ * its display bookkeeping (a meta that only held the id is left out too). */
105
+ export function withoutMessageIdsReplacer(key, value) {
106
+ if (key !== 'meta' || !value || typeof value !== 'object' || typeof value.transcript?.messageId !== 'string') {
107
+ return value;
108
+ }
109
+ const { messageId: _id, ...transcript } = value.transcript;
110
+ const meta = { ...value };
111
+ if (Object.keys(transcript).length) meta.transcript = transcript;
112
+ else delete meta.transcript;
113
+ return Object.keys(meta).length ? meta : undefined;
114
+ }
115
+
116
+ /** `copy` carrying the stable id of `original` (a compaction rewrite that
117
+ * builds a new message instead of spreading the original). Only the id
118
+ * travels; the copy's own meta is kept. */
119
+ export function carryMessageId(copy, original) {
120
+ const id = messageIdOf(original);
121
+ if (!id || !canCarryMessageId(copy) || messageIdOf(copy) === id) return copy;
122
+ return { ...copy, meta: withMessageId(copy.meta, id) };
123
+ }
@@ -0,0 +1,29 @@
1
+ // Tests that compare a transcript before and after a compaction: compaction
2
+ // stamps stable ids (message-ids.mjs) on messages that had none, which is
3
+ // bookkeeping, not a change of the conversation.
4
+
5
+ /** `messages` without the stable ids stamped on them (and without the meta a
6
+ * stamp alone created). */
7
+ export function withoutStampedIds(messages) {
8
+ return messages.map((message) => {
9
+ const transcript = message?.meta?.transcript;
10
+ if (!transcript?.messageId) return message;
11
+ const { messageId: _id, ...rest } = transcript;
12
+ const meta = { ...message.meta };
13
+ if (Object.keys(rest).length) meta.transcript = rest;
14
+ else delete meta.transcript;
15
+ const out = { ...message };
16
+ if (Object.keys(meta).length) out.meta = meta;
17
+ else delete out.meta;
18
+ return out;
19
+ });
20
+ }
21
+
22
+ /** Whether every message that can carry an id has one. */
23
+ export function everyMessageStamped(messages) {
24
+ return messages.every(
25
+ (message) =>
26
+ (message?.meta !== undefined && (typeof message.meta !== 'object' || message.meta === null)) ||
27
+ typeof message?.meta?.transcript?.messageId === 'string'
28
+ );
29
+ }
@@ -29,20 +29,14 @@ import { traceAgentCompact, messagePrefixHash } from '../agent-trace.mjs';
29
29
  import { invalidateProviderRequestToolsScope } from '../runtime-core/provider-request-tools.mjs';
30
30
  import { bumpUsageMetricsEpoch } from './manager/usage-metrics.mjs';
31
31
  import { resetReadStateAfterCompaction } from './read-dedup.mjs';
32
+ import { scheduleCompactionArchive, stampCompactionMessageIds } from './compaction-archive-capture.mjs';
33
+ import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
32
34
  import {
33
35
  acknowledgePendingGoalReminder,
34
36
  markPendingGoalReminder,
35
37
  snapshotPendingGoalReminder,
36
38
  } from '../runtime-core/goal-reminder.mjs';
37
39
 
38
- function writeStderr(line) {
39
- try {
40
- process.stderr.write(line);
41
- } catch {
42
- /* best-effort */
43
- }
44
- }
45
-
46
40
  async function setStage(opts, stage) {
47
41
  try {
48
42
  await opts.onStageChange?.(stage);
@@ -126,20 +120,23 @@ function preSendCompactDecision(state, compactPolicy) {
126
120
  activeTurn: true,
127
121
  }).reducesTokens;
128
122
  }
123
+ let compactTrigger = 'auto';
124
+ if (reactivePending) compactTrigger = 'reactive';
125
+ else if (cacheExpired) compactTrigger = 'cache_expired';
129
126
  return {
130
127
  messageTokensEst,
131
128
  pressureTokens,
132
129
  shouldCompact,
133
130
  requireReduction,
134
- compactTrigger: reactivePending ? 'reactive' : cacheExpired ? 'cache_expired' : 'auto',
131
+ compactTrigger,
135
132
  compactBudgetTokens,
136
133
  };
137
134
  }
138
135
 
139
- // Gauge sync (user: 컴팩트될 때 컨텍스트 표기량이 달랐다). The host refreshes
140
- // its context readout on provider usage deltas and turn end, so a tool batch
141
- // that pushed the transcript over the trigger fired compaction while the
142
- // screen still showed headroom. Publish the decision's OWN numerator here —
136
+ // Gauge sync. The host refreshes its context readout only on provider usage
137
+ // deltas and turn end, so a tool batch that pushed the transcript over the
138
+ // trigger would compact while the screen still showed headroom. Publish the
139
+ // decision's OWN numerator here —
143
140
  // before compaction mutates the transcript — so the readout and the trigger
144
141
  // describe one moment.
145
142
  function publishContextPressure(opts, sessionId, compactPolicy, { pressureTokens, shouldCompact }) {
@@ -158,15 +155,14 @@ function publishContextPressure(opts, sessionId, compactPolicy, { pressureTokens
158
155
  }
159
156
 
160
157
  // Pre/PostCompact bridge to the standard hook bus: a session-property hook
161
- // (manager/loop have no bus access), { trigger } normalized to
162
- // 'auto'|'manual'. Best-effort — a hook must never break compaction or the
163
- // loop.
158
+ // (manager/loop have no bus access); a pre-send pass is always trigger
159
+ // 'auto'. Best-effort — a hook must never break compaction or the loop.
164
160
  async function runCompactHook(ctx, name) {
165
161
  const { opts, sessionRef, sessionId, cwd } = ctx.state;
166
162
  const hook = typeof opts[name] === 'function' ? opts[name] : sessionRef?.[name];
167
163
  if (typeof hook !== 'function') return;
168
164
  try {
169
- await hook({ sessionId, cwd, trigger: ctx.decision.compactTrigger === 'manual' ? 'manual' : 'auto' });
165
+ await hook({ sessionId, cwd, trigger: 'auto' });
170
166
  } catch {
171
167
  /* best-effort */
172
168
  }
@@ -342,7 +338,7 @@ function throwCompactFailure(ctx, run, compactErr) {
342
338
  const { sessionRef, sessionId, signal, model } = state;
343
339
  acknowledgeGoalReminder(sessionRef, run);
344
340
  if (isCompactAbort(signal, compactErr)) {
345
- writeStderr(
341
+ writeStderrBestEffort(
346
342
  `[loop] pre-send compact cancelled (sess=${sessionId || 'unknown'}): ${compactErr?.message || compactErr}\n`
347
343
  );
348
344
  throw compactErr;
@@ -381,8 +377,16 @@ function throwCompactFailure(ctx, run, compactErr) {
381
377
  function adoptCompactedTranscript(ctx, run) {
382
378
  const { state } = ctx;
383
379
  const { messages, sessionRef, sessionId, opts } = state;
380
+ const replaced = messages.slice();
384
381
  messages.length = 0;
385
382
  messages.push(...run.compacted);
383
+ // Display-only archive of the dropped conversation; the projection and the
384
+ // write run off this synchronous adoption.
385
+ const archived = sessionRef
386
+ ? scheduleCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: messages })
387
+ : null;
388
+ ctx.droppedMessages = archived?.dropped ?? 0;
389
+ ctx.droppedIdentities = archived?.identities ?? null;
386
390
  acknowledgeGoalReminder(sessionRef, run);
387
391
  resetReadStateAfterCompaction(sessionId);
388
392
  // Cross-turn receipts reference tool results that compaction just removed
@@ -404,9 +408,10 @@ function adoptCompactedTranscript(ctx, run) {
404
408
  // providerState so the next send starts a fresh chain.
405
409
  ctx.providerState = undefined;
406
410
  ctx.providerStateCleared = true;
407
- // Compaction shrank the transcript, so prior turns no longer pressure the
408
- // window — reset the iteration counter so a steadily-compacting long task
409
- // isn't killed by the cap, while a non-compacting tight loop still hits it.
411
+ // Compaction starts a new loop epoch: the iteration counter restarts at 0
412
+ // so iteration indices (traces, usage metrics, cross-turn dedup windows)
413
+ // count from the compacted transcript. The counter bounds nothing — the
414
+ // loop has no iteration cap.
410
415
  ctx.iterations = 0;
411
416
  // New loop epoch so persistIterationMetrics idempotency keys do not
412
417
  // collide when iteration indices restart at 1 (incl. iter 1 → iter 1).
@@ -451,6 +456,8 @@ function reportCompactOutcome(ctx, run) {
451
456
  status: changed ? 'compacted' : 'no_change',
452
457
  afterTokens,
453
458
  changed,
459
+ droppedMessages: ctx.droppedMessages || 0,
460
+ droppedIdentities: ctx.droppedIdentities || null,
454
461
  freshContext: freshContextResult?.freshContext === true,
455
462
  handoffSource: freshContextResult?.handoffSource || null,
456
463
  durationMs,
@@ -460,6 +467,10 @@ function reportCompactOutcome(ctx, run) {
460
467
  async function compactBeforeSend(ctx) {
461
468
  const { state, decision } = ctx;
462
469
  const { messages, sessionRef, sessionId, opts } = state;
470
+ // Every message enters the compaction with a stable id (legacy ones are
471
+ // stamped now; the turn's saves persist them): the result's rewrites carry
472
+ // it, so what was dropped is exactly the ids the result no longer holds.
473
+ stampCompactionMessageIds(messages, sessionId || sessionRef?.id || '');
463
474
  // Snapshot BEFORE mutating the live array below, so compact_meta reports
464
475
  // real savings and the true input prefix rather than the replacement
465
476
  // transcript.
@@ -489,7 +500,7 @@ async function compactBeforeSend(ctx) {
489
500
  await compactTranscript(ctx, run);
490
501
  } catch (freshErr) {
491
502
  run.freshContextError = freshErr;
492
- writeStderr(
503
+ writeStderrBestEffort(
493
504
  `[loop] fresh-context compact failed (sess=${sessionId || 'unknown'}): ${freshErr?.message || freshErr}\n`
494
505
  );
495
506
  throw freshErr;
@@ -30,6 +30,7 @@ import { _saveAsyncQueued, _saveWorkerPending } from './save-worker.mjs';
30
30
  import { STORED_SESSION_UNREADABLE, _ensureLifecycleFields, _storedSessionFromFile } from './serialize.mjs';
31
31
  import { _savePending } from './pending-saves.mjs';
32
32
  import { isOrdinarySession } from '../store-summary-visibility.mjs';
33
+ import { closeSessionCatalogDb } from '../catalog-db/index.mjs';
33
34
 
34
35
  // Disk mtime of the summary index when the in-memory cache was last refreshed
35
36
  // from it — the cross-process staleness detector.
@@ -145,8 +146,9 @@ function scheduleSessionSummaryIndexRebuild() {
145
146
 
146
147
  /**
147
148
  * Resolves once no summary-index writer of this process is running: the
148
- * background rebuild worker a cold listing (or sweep) spawned has exited and
149
- * every queued index mutation has landed. Both write the index, its lock and
149
+ * background rebuild worker a cold listing (or sweep) spawned has exited,
150
+ * every queued index mutation has landed and the session catalog DB is
151
+ * closed. Both write the index, its lock and
150
152
  * temp files into the data dir after the listing/sweep/save that caused them
151
153
  * has returned, so an owner that removes or switches the data dir awaits this
152
154
  * first.
@@ -159,6 +161,8 @@ export async function settleSessionSummaryIndex() {
159
161
  await new Promise((resolve) => worker.once('exit', resolve));
160
162
  }
161
163
  await settleSummaryIndexWrites();
164
+ // The daemon's session catalog DB holds its file, WAL and lock open.
165
+ await closeSessionCatalogDb();
162
166
  }
163
167
 
164
168
  export function listStoredSessionSummaries(options = {}) {
@@ -31,6 +31,7 @@ import { _uncacheSessionSummary, _queueSessionSummaryRemoval } from './summary-c
31
31
  import { _savePending, _clearDebounce, _releasePendingSlot } from './pending-saves.mjs';
32
32
  import { _runtimeLivenessVeto, _heartbeatLivenessVeto, _deleteHeartbeatUnlessNewer } from './liveness-veto.mjs';
33
33
  import { _runSessionPurgeHooks } from './session-observers.mjs';
34
+ import { deleteCompactionArchive } from '../compaction-archive.mjs';
34
35
 
35
36
  /** Strictly enumerate child-agent session files linked to one visible parent.
36
37
  * Used only by explicit parent deletion; ordinary close/context switches keep
@@ -130,6 +131,8 @@ export function deleteSession(id, options = {}) {
130
131
  // Preserve a sidecar published strictly after the sweep's scan snapshot.
131
132
  _deleteHeartbeatUnlessNewer(id, options);
132
133
  deleteSessionPresence(id);
134
+ // The display-only compaction archive has no meaning without its session.
135
+ deleteCompactionArchive(getStoreDir(), id);
133
136
  _clearLiveSession(id);
134
137
  // The file is gone: every marker of the retired incarnation goes with it
135
138
  // (save error + failed-snapshot evidence, split-brain drop flag, lifecycle