mixdog 1.3.0 → 1.3.2

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 (423) hide show
  1. package/NOTICE.md +52 -9
  2. package/README.md +8 -4
  3. package/package.json +2 -2
  4. package/scripts/native-release-status.mjs +238 -0
  5. package/src/defaults/shipped-history.json +8 -1
  6. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  7. package/src/defaults/skills/setup/SKILL.md +23 -1
  8. package/src/defaults/skills/setup/references/actions.md +54 -4
  9. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  10. package/src/defaults/skills/setup-guide/SKILL.md +17 -10
  11. package/src/defaults/skills/visualize/SKILL.md +9 -6
  12. package/src/defaults/skills/visualize/references/craft.md +8 -1
  13. package/src/help.mjs +1 -1
  14. package/src/lib/keychain-cjs.cjs +4 -1
  15. package/src/lib/rules-builder.cjs +14 -7
  16. package/src/output-styles/common.md +13 -8
  17. package/src/rules/agent-mode/AGENT_MODE.md +9 -6
  18. package/src/rules/agent-mode/MIXY.md +11 -6
  19. package/src/rules/lead/LEAD.md +2 -2
  20. package/src/rules/shared/10-tool-workflow.md +4 -0
  21. package/src/rules/shared/20-research.md +2 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  23. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  25. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  26. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  27. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  28. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  29. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  31. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  32. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +81 -16
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -4
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  36. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  37. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  38. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  41. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
  43. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  44. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +34 -16
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  48. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  49. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -1
  50. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  51. package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
  52. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  53. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  54. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  56. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  57. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  58. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
  59. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +10 -2
  60. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  62. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  64. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  65. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
  68. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  69. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  72. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  73. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +5 -5
  74. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +17 -6
  78. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +41 -10
  79. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +6 -4
  80. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  81. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  83. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  85. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +21 -2
  86. package/src/runtime/agent/orchestrator/session/agent-room.mjs +149 -45
  87. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  88. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  89. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  90. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  91. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  92. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  93. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  94. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  95. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  96. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  97. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  99. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  100. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +156 -25
  101. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  102. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  103. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  104. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  105. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  106. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  108. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
  109. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  110. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +111 -9
  111. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -14
  112. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  113. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  114. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  115. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  116. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +46 -4
  119. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  120. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  124. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  125. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  126. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  128. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  130. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
  131. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  132. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  134. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +62 -47
  135. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +6 -8
  136. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
  139. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  140. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  142. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +5 -2
  143. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  144. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  145. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +35 -4
  146. package/src/runtime/agent/orchestrator/session/routine-result.mjs +179 -16
  147. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  148. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  149. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  150. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  151. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  152. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  153. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  154. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  155. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  156. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  157. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  158. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  159. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  160. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  161. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +49 -14
  162. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +134 -17
  163. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  164. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  167. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  168. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  169. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  170. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  171. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  175. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  176. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  177. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  178. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  179. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  180. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  181. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  182. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  183. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  184. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  185. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  186. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  187. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  188. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  189. package/src/runtime/attachments/limits.mjs +5 -1
  190. package/src/runtime/attachments/office-extract.mjs +3 -6
  191. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  192. package/src/runtime/attachments/store.mjs +55 -30
  193. package/src/runtime/browser-bridge/client.mjs +73 -20
  194. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  195. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  196. package/src/runtime/channels/lib/state-file.mjs +15 -15
  197. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  198. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  199. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  200. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  201. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  202. package/src/runtime/effort-judge/judge-client.mjs +6 -3
  203. package/src/runtime/effort-judge/model-install.mjs +16 -8
  204. package/src/runtime/effort-judge/model-manifest.json +7 -7
  205. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  206. package/src/runtime/local-provider/catalog.mjs +5 -2
  207. package/src/runtime/local-provider/registered-models.mjs +64 -27
  208. package/src/runtime/local-provider/server.mjs +2 -3
  209. package/src/runtime/media/adapters/image-api.mjs +89 -0
  210. package/src/runtime/media/auth.mjs +64 -1
  211. package/src/runtime/media/catalog.mjs +66 -4
  212. package/src/runtime/media/jobs.mjs +4 -0
  213. package/src/runtime/media/lanes.mjs +46 -5
  214. package/src/runtime/media/media-usage.mjs +17 -0
  215. package/src/runtime/media/renditions.mjs +15 -15
  216. package/src/runtime/media/store.mjs +36 -35
  217. package/src/runtime/media/tool.mjs +8 -1
  218. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  219. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  220. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  221. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  222. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  223. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  224. package/src/runtime/office/core/journal.mjs +7 -14
  225. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  226. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  227. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  228. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  229. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  230. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  231. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  232. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  233. package/src/runtime/shared/agent-kind.mjs +152 -0
  234. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  235. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  236. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  237. package/src/runtime/shared/agent-route-config.d.mts +1 -0
  238. package/src/runtime/shared/agent-route-config.mjs +22 -8
  239. package/src/runtime/shared/agent-store.mjs +149 -0
  240. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  241. package/src/runtime/shared/atomic-file.d.mts +50 -0
  242. package/src/runtime/shared/atomic-file.mjs +394 -132
  243. package/src/runtime/shared/background-tasks.mjs +7 -4
  244. package/src/runtime/shared/bot-hops.mjs +30 -0
  245. package/src/runtime/shared/bot-split-migration.mjs +457 -0
  246. package/src/runtime/shared/bounded-download.mjs +4 -2
  247. package/src/runtime/shared/config-patch.mjs +20 -1
  248. package/src/runtime/shared/config.mjs +39 -9
  249. package/src/runtime/shared/connector-catalog.d.mts +1 -2
  250. package/src/runtime/shared/connector-catalog.mjs +2 -4
  251. package/src/runtime/shared/connector-triggers.mjs +2 -0
  252. package/src/runtime/shared/defaults-separation.mjs +6 -2
  253. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  254. package/src/runtime/shared/err-text.mjs +7 -1
  255. package/src/runtime/shared/error-presentation.mjs +113 -10
  256. package/src/runtime/shared/file-has-data.mjs +28 -0
  257. package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
  258. package/src/runtime/shared/handoff-artifacts.mjs +61 -0
  259. package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
  260. package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
  261. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  262. package/src/runtime/shared/model-route-fallback.mjs +79 -7
  263. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  264. package/src/runtime/shared/plain-preview.mjs +22 -17
  265. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  266. package/src/runtime/shared/projects.mjs +50 -41
  267. package/src/runtime/shared/provider-accounts.mjs +17 -12
  268. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  269. package/src/runtime/shared/routine-trigger.mjs +14 -6
  270. package/src/runtime/shared/run-child.d.mts +3 -0
  271. package/src/runtime/shared/run-child.mjs +24 -3
  272. package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
  273. package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
  274. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  275. package/src/runtime/shared/singleton-owner.mjs +7 -6
  276. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  277. package/src/runtime/shared/staged-update.mjs +4 -2
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  279. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  280. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  281. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  282. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  283. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  284. package/src/runtime/shared/usage-readout-totals.mjs +15 -13
  285. package/src/runtime/shared/user-cwd.mjs +9 -4
  286. package/src/runtime/shared/user-data-guard.mjs +46 -44
  287. package/src/runtime/shared/utf8-cut.mjs +10 -0
  288. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  289. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  290. package/src/runtime/tidy/core-install.mjs +26 -4
  291. package/src/runtime/tidy/install.mjs +91 -23
  292. package/src/runtime/web-search/lib/config.mjs +4 -16
  293. package/src/session-runtime/agent-home-delivery.mjs +859 -136
  294. package/src/session-runtime/agent-room-delivery.mjs +167 -19
  295. package/src/session-runtime/agent-task-sessions.mjs +831 -69
  296. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  297. package/src/session-runtime/boot/apis.mjs +58 -24
  298. package/src/session-runtime/boot/config.mjs +1 -0
  299. package/src/session-runtime/boot/facade.mjs +11 -1
  300. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  301. package/src/session-runtime/boot/providers.mjs +16 -6
  302. package/src/session-runtime/boot/shared.mjs +12 -6
  303. package/src/session-runtime/boot/tools.mjs +2 -2
  304. package/src/session-runtime/bot-status.mjs +432 -0
  305. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  306. package/src/session-runtime/config-lifecycle.mjs +78 -32
  307. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  308. package/src/session-runtime/handoff-attachments.mjs +593 -0
  309. package/src/session-runtime/held-requests.mjs +36 -33
  310. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  311. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  312. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +45 -6
  313. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  314. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  315. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  316. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  317. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  318. package/src/session-runtime/media-api.mjs +5 -2
  319. package/src/session-runtime/model-route/route-persist.mjs +2 -2
  320. package/src/session-runtime/model-route/route-tuning.mjs +11 -2
  321. package/src/session-runtime/model-route/set-route.mjs +82 -2
  322. package/src/session-runtime/native-web-search.mjs +2 -2
  323. package/src/session-runtime/new-session-config.mjs +6 -1
  324. package/src/session-runtime/owned-automation-run.mjs +8 -1
  325. package/src/session-runtime/room-member-gate.mjs +16 -3
  326. package/src/session-runtime/route-resolution.mjs +21 -19
  327. package/src/session-runtime/route-state.mjs +1 -59
  328. package/src/session-runtime/routine-home-turn.mjs +5 -3
  329. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  330. package/src/session-runtime/runtime-paths.mjs +11 -5
  331. package/src/session-runtime/runtime-tunables.mjs +6 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  336. package/src/session-runtime/services/agent-tool/helpers.mjs +24 -5
  337. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  338. package/src/session-runtime/services/agent-tool/send-flow.mjs +36 -24
  339. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  340. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  341. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +12 -6
  342. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
  343. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  344. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  345. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  346. package/src/session-runtime/services/agent-tool.mjs +6 -0
  347. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  348. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  349. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  350. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  351. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  352. package/src/session-runtime/services/provider-admin.mjs +14 -4
  353. package/src/session-runtime/services/schedule-access.mjs +33 -4
  354. package/src/session-runtime/services/seeds.mjs +4 -3
  355. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  356. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  357. package/src/session-runtime/session-create/materialize.mjs +2 -3
  358. package/src/session-runtime/session-create/session-options.mjs +15 -2
  359. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  360. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  361. package/src/session-runtime/setup-tool/executor.mjs +20 -3
  362. package/src/session-runtime/setup-tool/extended-actions.mjs +19 -7
  363. package/src/session-runtime/setup-tool/settings-contract.mjs +37 -1
  364. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  365. package/src/session-runtime/skills-api.mjs +12 -5
  366. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  367. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  368. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  369. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  370. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  371. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  372. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  373. package/src/session-runtime/tool-policy-refresh.mjs +47 -358
  374. package/src/session-runtime/tool-surface.mjs +5 -3
  375. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +99 -81
  376. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  377. package/src/session-runtime/turn/turn-run.mjs +2 -1
  378. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  379. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +578 -301
  380. package/src/session-runtime/workflow-agents-api/agent-route.mjs +113 -53
  381. package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
  382. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  383. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  384. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  385. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  386. package/src/standalone/daemon-agent-control.mjs +4 -1
  387. package/src/standalone/daemon-session-control.mjs +60 -11
  388. package/src/standalone/daemon.mjs +34 -4
  389. package/src/standalone/local-session-runtime.mjs +22 -14
  390. package/src/standalone/session-client.mjs +7 -2
  391. package/src/standalone/session-protocol.mjs +7 -0
  392. package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
  393. package/src/standalone/session-service/agent-tree.mjs +4 -3
  394. package/src/standalone/session-service/session-calls/session-turns.mjs +19 -0
  395. package/src/standalone/session-service.mjs +11 -0
  396. package/src/standalone/session-state-projection.mjs +19 -9
  397. package/src/standalone/session-tool-output.mjs +35 -9
  398. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  399. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  400. package/src/tui/app/slash-commands.mjs +1 -1
  401. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  402. package/src/tui/dist/index.mjs +599 -342
  403. package/src/tui/prompt-history-store.mjs +16 -21
  404. package/src/tui/session/context-state.mjs +4 -0
  405. package/src/tui/session/notice-surface.mjs +3 -4
  406. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  407. package/src/tui/session/session-api/media.mjs +2 -1
  408. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  409. package/src/tui/session/session-api/routes-write.mjs +203 -0
  410. package/src/tui/session/session-api/routes.mjs +23 -236
  411. package/src/tui/session/session-api/settings.mjs +14 -1
  412. package/src/tui/session/session-flow/drain.mjs +28 -3
  413. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  414. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  415. package/src/tui/session/session-flow/steering.mjs +8 -3
  416. package/src/tui/session/tool-approval.mjs +185 -20
  417. package/src/tui/session/transcript-intake.mjs +4 -0
  418. package/src/tui/session/turn-live-call.mjs +11 -3
  419. package/src/tui/session/turn.mjs +36 -17
  420. package/src/tui/transcript-tool-failures.mjs +8 -2
  421. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  422. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  423. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -150,6 +150,13 @@ function isOverloadFailure(status, errorKind) {
150
150
  return status === 529 || errorKind === 'overloaded';
151
151
  }
152
152
 
153
+ function exceedsAdviceCeiling(caught, opts) {
154
+ const ceiling = Number(opts.maxRetryAfterMs);
155
+ if (!(ceiling > 0)) return false;
156
+ const advised = retryAfterMsFromError(caught);
157
+ return advised != null && advised > ceiling;
158
+ }
159
+
153
160
  // Decides whether `caught` may be re-issued: returns the error to throw (the
154
161
  // caught one, the exhausted-marked one on the last attempt, or the model
155
162
  // fallback trigger) or null when the next attempt may run — and only then
@@ -206,6 +213,16 @@ function retryVerdict(caught, { opts, attempt, maxAttempts, state }) {
206
213
  const typedQuota = QUOTA_KINDS.has(providerErrorKind(caught, { quotaFlags: false }).kind);
207
214
  if (typedQuota || opts.retry429 === false || status !== 429) return caught;
208
215
  }
216
+ // Opt-in `maxRetryAfterMs`: a server wait longer than the caller will sit
217
+ // through is a lock-out, not a retry. It surfaces now (with its advice for
218
+ // the notice and the account pool) instead of parking the send for it.
219
+ if (exceedsAdviceCeiling(caught, opts)) {
220
+ return markProviderRecoveryExhausted(caught, {
221
+ owner: opts.recoveryOwner || 'withRetry',
222
+ attempts: attempt + 1,
223
+ terminal: true,
224
+ });
225
+ }
209
226
  // Background calls (titles and the like) never retry an overload: during a
210
227
  // capacity cascade every retry multiplies gateway load, and nobody is
211
228
  // waiting on the result.
@@ -4,6 +4,7 @@ import { clean, hasOwn } from './session-text.mjs';
4
4
  import { normalizeEffortInput, normalizeSavedEffort } from './effort.mjs';
5
5
  import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
6
6
  import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
7
+ import { normalizeCompactionSummaryMode } from '../session/compact/summary-mode.mjs';
7
8
 
8
9
  const DEFAULT_PROVIDER = 'anthropic-oauth';
9
10
  export const DEFAULT_MODEL = '';
@@ -191,12 +192,23 @@ const AUTO_CLEAR_DEFAULT_IDLE_MS = 60 * 60 * 1000;
191
192
  // reusable. Gemini, xAI and Mistral
192
193
  // caches run ~1h, Groq's implicit cache persists ~2h, and DeepSeek's
193
194
  // implicit cache is evicted after "a few hours to a few days" (kept at 24h).
194
- // OpenAI (direct and OAuth/Codex) guarantees a 30-minute minimum prompt-cache
195
- // TTL on GPT-5.6+ (refreshed on use; no 24h retention there), so the sweep
196
- // matches that window. Older OpenAI models that still take
197
- // prompt_cache_retention='24h' are not distinguished: these call sites only
198
- // know the provider, not the model, so 30m applies to the whole provider.
195
+ // OpenAI public API (GPT-5.6+ Responses, cache-strategy.mjs): the moving tail
196
+ // is written with ttl "30m" and only the Lead-class stable prefix with "12h",
197
+ // so the sweep matches the 30m tail — a clear after that costs nothing extra
198
+ // (the tail is cold anyway) and the cleared session restarts on the warm 12h
199
+ // prefix. Earlier OpenAI models (prompt_cache_retention, typically ~30 min)
200
+ // are not distinguished: these call sites only know the provider.
201
+ // OpenAI OAuth (Codex/ChatGPT login) keeps the cache of logged-in users
202
+ // indefinitely (2026-10-20), so its built-in window is unlimited: no idle
203
+ // clear; compaction happens only at the context limit or on request.
199
204
  // Unknown/unrecognized providers fall back to the 'default' 1h entry.
205
+ //
206
+ // Unlimited ("never auto-clear") is stored as 0 in providerIdleMs — a value
207
+ // older builds dropped, so no older config carries it — and resolves to
208
+ // AUTO_CLEAR_UNLIMITED_MS, which every idle comparison treats as never
209
+ // reached. Input also accepts 'unlimited' / 'never'.
210
+ export const AUTO_CLEAR_UNLIMITED = 0;
211
+ export const AUTO_CLEAR_UNLIMITED_MS = Number.MAX_SAFE_INTEGER;
200
212
  const AUTO_CLEAR_PROVIDER_IDLE_MS = Object.freeze({
201
213
  anthropic: 60 * 60 * 1000,
202
214
  'anthropic-oauth': 60 * 60 * 1000,
@@ -205,20 +217,48 @@ const AUTO_CLEAR_PROVIDER_IDLE_MS = Object.freeze({
205
217
  openai: 30 * 60 * 1000,
206
218
  deepseek: 24 * 60 * 60 * 1000,
207
219
  xai: 60 * 60 * 1000,
208
- 'openai-oauth': 30 * 60 * 1000,
220
+ 'openai-oauth': AUTO_CLEAR_UNLIMITED,
209
221
  mistral: 60 * 60 * 1000,
210
222
  default: AUTO_CLEAR_DEFAULT_IDLE_MS,
211
223
  });
212
224
  const AUTO_CLEAR_PROVIDER_IDS = Object.freeze(Object.keys(AUTO_CLEAR_PROVIDER_IDLE_MS));
213
225
 
226
+ // Completed-agent retention where it must differ from the idle window. Reap
227
+ // frees in-memory sessions and worker-index rows (resources), so it stays
228
+ // finite even where the cache is permanent: openai-oauth uses the 1h product
229
+ // default, which covers Lead follow-ups (24/25 Lead waits were 5–27 min).
230
+ const AGENT_TERMINAL_REAP_MS = Object.freeze({ 'openai-oauth': 60 * 60 * 1000 });
231
+
232
+ /** A stored/requested idle value: AUTO_CLEAR_UNLIMITED, a window ≥ 1 min, or
233
+ * null when absent or invalid. */
234
+ export function normalizeAutoClearIdleValue(value) {
235
+ if (value === null || value === undefined || typeof value === 'boolean') return null;
236
+ if (typeof value === 'string') {
237
+ const text = value.trim().toLowerCase();
238
+ if (!text) return null;
239
+ if (text === 'unlimited' || text === 'never') return AUTO_CLEAR_UNLIMITED;
240
+ }
241
+ const idleMs = Number(value);
242
+ if (!Number.isFinite(idleMs) || idleMs < 0) return null;
243
+ if (idleMs === 0) return AUTO_CLEAR_UNLIMITED;
244
+ return Math.max(60_000, Math.round(idleMs));
245
+ }
246
+
247
+ const resolvedIdleMs = (stored) => (stored === AUTO_CLEAR_UNLIMITED ? AUTO_CLEAR_UNLIMITED_MS : stored);
248
+
249
+ /** Whether a resolved idle window means "never auto-clear". */
250
+ export function isAutoClearUnlimited(idleMs) {
251
+ return Number(idleMs) >= AUTO_CLEAR_UNLIMITED_MS;
252
+ }
253
+
214
254
  function normalizeAutoClearProviderIdleMs(value = {}) {
215
255
  const raw = value && typeof value === 'object' ? value : {};
216
256
  const out = {};
217
257
  for (const [key, ms] of Object.entries(raw)) {
218
258
  const provider = clean(key).toLowerCase();
219
- const idleMs = Number(ms);
220
- if (!provider || !Number.isFinite(idleMs) || idleMs <= 0) continue;
221
- out[provider] = Math.max(60_000, Math.round(idleMs));
259
+ const idleMs = normalizeAutoClearIdleValue(ms);
260
+ if (!provider || idleMs === null) continue;
261
+ out[provider] = idleMs;
222
262
  }
223
263
  return out;
224
264
  }
@@ -228,49 +268,71 @@ function normalizeAutoClearProviderIdleMs(value = {}) {
228
268
  export function autoClearIdleMsForProvider(provider, providerIdleMs = null) {
229
269
  const overrides = normalizeAutoClearProviderIdleMs(providerIdleMs);
230
270
  const key = clean(provider).toLowerCase();
231
- if (overrides[key]) return overrides[key];
232
- if (key && AUTO_CLEAR_PROVIDER_IDLE_MS[key]) return AUTO_CLEAR_PROVIDER_IDLE_MS[key];
233
- if (overrides.default) return overrides.default;
271
+ if (key && Object.hasOwn(overrides, key)) return resolvedIdleMs(overrides[key]);
272
+ if (key && Object.hasOwn(AUTO_CLEAR_PROVIDER_IDLE_MS, key)) return resolvedIdleMs(AUTO_CLEAR_PROVIDER_IDLE_MS[key]);
273
+ if (Object.hasOwn(overrides, 'default')) return resolvedIdleMs(overrides.default);
234
274
  return AUTO_CLEAR_PROVIDER_IDLE_MS.default;
235
275
  }
236
276
 
237
- export function autoClearProviderDefaults(providerIdleMs = null) {
277
+ // One row per built-in provider plus every provider in `extraProviders` (the
278
+ // providers the user has enabled or routes to), before the 'default' row.
279
+ // A provider without a built-in entry resolves like any unknown provider.
280
+ export function autoClearProviderDefaults(providerIdleMs = null, extraProviders = []) {
238
281
  const overrides = normalizeAutoClearProviderIdleMs(providerIdleMs);
239
- return AUTO_CLEAR_PROVIDER_IDS.map((provider) => {
240
- const builtInMs = AUTO_CLEAR_PROVIDER_IDLE_MS[provider] ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
241
- const idleMs = overrides[provider] ?? builtInMs;
282
+ const ids = AUTO_CLEAR_PROVIDER_IDS.filter((id) => id !== 'default');
283
+ for (const value of extraProviders || []) {
284
+ const id = clean(value).toLowerCase();
285
+ if (id && !ids.includes(id) && id !== 'default') ids.push(id);
286
+ }
287
+ for (const id of Object.keys(overrides)) if (!ids.includes(id) && id !== 'default') ids.push(id);
288
+ ids.push('default');
289
+ return ids.map((provider) => {
290
+ const builtIn = Object.hasOwn(AUTO_CLEAR_PROVIDER_IDLE_MS, provider)
291
+ ? AUTO_CLEAR_PROVIDER_IDLE_MS[provider]
292
+ : AUTO_CLEAR_PROVIDER_IDLE_MS.default;
293
+ const idleMs = Object.hasOwn(overrides, provider)
294
+ ? resolvedIdleMs(overrides[provider])
295
+ : provider === 'default' || Object.hasOwn(AUTO_CLEAR_PROVIDER_IDLE_MS, provider)
296
+ ? resolvedIdleMs(builtIn)
297
+ : autoClearIdleMsForProvider(provider, overrides);
242
298
  return {
243
299
  provider,
244
300
  idleMs,
245
- builtInMs,
246
- custom: overrides[provider] != null,
301
+ builtInMs: resolvedIdleMs(builtIn),
302
+ unlimited: isAutoClearUnlimited(idleMs),
303
+ builtInUnlimited: builtIn === AUTO_CLEAR_UNLIMITED,
304
+ custom: Object.hasOwn(overrides, provider),
247
305
  };
248
306
  });
249
307
  }
250
308
 
251
309
  // Agent terminal retention is deliberately narrower than Auto Clear's general
252
- // resolution: listed providers use their provider cache lifetime. Unlisted
310
+ // resolution: listed providers use their provider cache lifetime, or the
311
+ // finite AGENT_TERMINAL_REAP_MS entry where that cache is permanent. Unlisted
253
312
  // providers use the Advanced "default" row, or the built-in default when no
254
- // override is configured, so completed agents are still eventually reclaimed.
313
+ // override is configured, so completed agents are still eventually reclaimed;
314
+ // an unlimited override never disables reaping.
255
315
  export function resolveAgentTerminalReapMs(config, provider) {
256
316
  const key = clean(provider).toLowerCase();
257
317
  const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
258
318
  const overrides = normalizeAutoClearProviderIdleMs(raw.providerIdleMs);
319
+ const finite = (value) => (value > 0 ? value : null);
259
320
  if (!key || key === 'default' || !Object.hasOwn(AUTO_CLEAR_PROVIDER_IDLE_MS, key)) {
260
- return overrides.default ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
321
+ return finite(overrides.default) ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
261
322
  }
262
- return overrides[key] ?? AUTO_CLEAR_PROVIDER_IDLE_MS[key];
323
+ return finite(overrides[key]) ?? AGENT_TERMINAL_REAP_MS[key] ?? AUTO_CLEAR_PROVIDER_IDLE_MS[key];
263
324
  }
264
325
 
265
326
  // Resolve the effective auto-clear idle window for a config + provider:
266
327
  // a provider-specific override (autoClear.providerIdleMs[provider]) wins;
267
328
  // else the global user-set idleMs (autoClear.idleMs); else the provider's
268
329
  // built-in window, then providerIdleMs.default / the built-in 1h default.
330
+ // Unlimited resolves to AUTO_CLEAR_UNLIMITED_MS.
269
331
  export function resolveAutoClearIdleMs(config, provider) {
270
332
  const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
271
333
  const key = clean(provider).toLowerCase();
272
- const providerOverride = key && key !== 'default' ? normalizeAutoClearProviderIdleMs(raw.providerIdleMs)[key] : null;
273
- if (providerOverride) return providerOverride;
334
+ const overrides = normalizeAutoClearProviderIdleMs(raw.providerIdleMs);
335
+ if (key && key !== 'default' && Object.hasOwn(overrides, key)) return resolvedIdleMs(overrides[key]);
274
336
  const idleMs = Number(raw.idleMs);
275
337
  if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
276
338
  return autoClearIdleMsForProvider(provider, raw.providerIdleMs);
@@ -350,12 +412,16 @@ export function normalizeCompactionConfig(value = {}) {
350
412
  delete next.recallConcurrency;
351
413
  delete next.recallCycle1DeadlineMs;
352
414
  delete next.enabled;
415
+ delete next.useMaintainer;
353
416
  return {
354
417
  ...next,
355
418
  auto: raw.auto !== false && raw.enabled !== false,
356
419
  // Compact a conversation on its current model before switching it to
357
420
  // another one in place (default on).
358
421
  onModelChange: raw.onModelChange !== false,
422
+ // Which model writes compaction summaries (smart / maintainer / self);
423
+ // replaces the previous release's boolean useMaintainer.
424
+ summaryMode: normalizeCompactionSummaryMode(raw),
359
425
  };
360
426
  }
361
427
 
@@ -1,11 +1,18 @@
1
1
  // Small filesystem read helpers used by session-runtime submodules.
2
2
  import { readdirSync, readFileSync } from 'node:fs';
3
+ import { holdsNoData } from '../../../shared/atomic-file.mjs';
3
4
 
4
5
  export { readJsonSafe } from '../../../shared/json-file.mjs';
5
6
 
7
+ // The default data dir of a loader factory: a path, or a resolver read per
8
+ // call so a later runtime start's data dir is honored.
9
+ export const defaultDirResolver = (dataDir) => (typeof dataDir === 'function' ? dataDir : () => dataDir);
10
+
6
11
  export function readTextSafe(path) {
7
12
  try {
8
- return readFileSync(path, 'utf8').trim();
13
+ const text = readFileSync(path, 'utf8');
14
+ // A crash can leave NUL bytes (String.trim keeps them): no data is no text.
15
+ return holdsNoData(text) ? '' : text.trim();
9
16
  } catch {
10
17
  return '';
11
18
  }
@@ -133,7 +133,7 @@ export function createGoalMutations(ctx) {
133
133
  await write(
134
134
  join(root, 'history', id, `${assertSessionId(goal.id)}.json`),
135
135
  { version: GOAL_FILE_VERSION, goal },
136
- { lock: true, secret: true, fsync: false, timeoutMs: 2_000 }
136
+ { lock: true, secret: true, timeoutMs: 2_000 }
137
137
  );
138
138
  };
139
139
 
@@ -1,6 +1,13 @@
1
- import { readFileSync, readdirSync, rmSync, statSync } from 'node:fs';
1
+ import { readdirSync, rmSync, statSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
2
3
  import { join } from 'node:path';
3
- import { writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
4
+ import {
5
+ parseJsonText,
6
+ readJsonStateSync,
7
+ unsupportedState,
8
+ withFileLock,
9
+ writeJsonAtomicAsync,
10
+ } from '../../../shared/atomic-file.mjs';
4
11
  import { clean } from '../../../shared/clean.mjs';
5
12
  import {
6
13
  DEFAULT_COMPLETED_GOAL_TTL_MS,
@@ -87,31 +94,58 @@ export function reportGoalStorageError(error) {
87
94
  process.emitWarning(error?.message || String(error), { code: 'GOAL_STORAGE_ERROR' });
88
95
  }
89
96
 
97
+ // A damaged record (empty, NUL, torn, invalid) never blocks the Goal
98
+ // feature: readJsonStateSync keeps its bytes aside as `<file>.corrupt-<ts>`,
99
+ // restores the last good `.bak` when there is one, else the session has no
100
+ // Goal. A record from a newer build is left in place and reads as no Goal.
101
+ // Real I/O errors still throw.
90
102
  function readGoalRecordFile(path, sessionId, normalizeGoal, at) {
103
+ const record = readJsonStateSync(path, {
104
+ secret: true,
105
+ fallback: null,
106
+ validate(parsed) {
107
+ if (Number.isSafeInteger(parsed?.version) && parsed.version > GOAL_FILE_VERSION) {
108
+ throw unsupportedState(`Goal record version ${parsed.version} is newer than this build`);
109
+ }
110
+ if (
111
+ !parsed ||
112
+ typeof parsed !== 'object' ||
113
+ !Object.hasOwn(parsed, 'goal') ||
114
+ parsed.version !== GOAL_FILE_VERSION
115
+ ) {
116
+ throw new Error('unsupported or invalid Goal record');
117
+ }
118
+ if (parsed.goal !== null && (typeof parsed.goal !== 'object' || Array.isArray(parsed.goal)))
119
+ throw new Error('invalid Goal value');
120
+ return { version: GOAL_FILE_VERSION, goal: normalizeGoal(parsed.goal, sessionId, at) };
121
+ },
122
+ });
123
+ return record ?? { version: GOAL_FILE_VERSION, goal: null };
124
+ }
125
+
126
+ // A record written by a newer build reads as no Goal and stays in place; a
127
+ // write must not replace it (nor, through `.bak`, its backup). Caller holds the
128
+ // record's lock. Unreadable or unparsable content is damage, not a newer build.
129
+ async function refuseNewerRecord(path) {
91
130
  let text;
92
131
  try {
93
- text = readFileSync(path, 'utf8');
132
+ text = await readFile(path, 'utf8');
94
133
  } catch (error) {
95
- if (error?.code === 'ENOENT') return { version: GOAL_FILE_VERSION, goal: null };
134
+ if (error?.code === 'ENOENT') return;
96
135
  throw error;
97
136
  }
137
+ let parsed;
98
138
  try {
99
- const parsed = JSON.parse(text);
100
- if (
101
- !parsed ||
102
- typeof parsed !== 'object' ||
103
- !Object.hasOwn(parsed, 'goal') ||
104
- parsed.version !== GOAL_FILE_VERSION
105
- ) {
106
- throw new Error('unsupported or invalid Goal record');
107
- }
108
- if (parsed.goal !== null && (typeof parsed.goal !== 'object' || Array.isArray(parsed.goal)))
109
- throw new Error('invalid Goal value');
110
- return { version: GOAL_FILE_VERSION, goal: normalizeGoal(parsed.goal, sessionId, at) };
111
- } catch (cause) {
112
- throw new Error(
113
- `cannot read Goal record ${path}: ${cause.message}; original file preserved, repair or explicitly clear it before creating a Goal`,
114
- { cause }
139
+ parsed = parseJsonText(text);
140
+ } catch {
141
+ return;
142
+ }
143
+ if (Number.isSafeInteger(parsed?.version) && parsed.version > GOAL_FILE_VERSION) {
144
+ throw Object.assign(
145
+ new Error(
146
+ `Goal record ${path} was written by a newer build (version ${parsed.version} > ${GOAL_FILE_VERSION}); update Mixdog to change this Goal`
147
+ ),
148
+ { code: 'EGOALNEWERVERSION' }
115
149
  );
116
150
  }
117
151
  }
@@ -149,7 +183,7 @@ export function createGoalStorage({ pathFor, normalizeGoal, now, writeRecord = w
149
183
  try {
150
184
  record = readGoalRecordFile(path, id, normalizeGoal, now());
151
185
  } catch (error) {
152
- if (cached && TRANSIENT_READ_CODES.has(error?.cause?.code ?? error?.code)) {
186
+ if (cached && TRANSIENT_READ_CODES.has(error?.code)) {
153
187
  return structuredClone(cached.record);
154
188
  }
155
189
  throw error;
@@ -166,7 +200,19 @@ export function createGoalStorage({ pathFor, normalizeGoal, now, writeRecord = w
166
200
  const snapshot = structuredClone(record);
167
201
  writing.add(id);
168
202
  try {
169
- await writeRecord(pathFor(id), snapshot, { lock: true, secret: true, fsync: false, timeoutMs: 2_000 });
203
+ const path = pathFor(id);
204
+ // The newer-build check and the write share one lock hold, so nothing
205
+ // can publish a newer record between them. The writer's own lock
206
+ // request re-enters this hold.
207
+ await withFileLock(
208
+ `${path}.lock`,
209
+ async () => {
210
+ await refuseNewerRecord(path);
211
+ // Durable, with the replaced version kept as `.bak` for damage recovery.
212
+ await writeRecord(path, snapshot, { lock: true, secret: true, backup: true, timeoutMs: 2_000 });
213
+ },
214
+ { timeoutMs: 2_000 }
215
+ );
170
216
  // The write lock has already been released. A subsequent stat could
171
217
  // belong to another writer, so validate the file on the next read.
172
218
  cache.set(id, { record: snapshot, stamp: null });
@@ -112,7 +112,7 @@ const GOAL_TOOL_ACTIONS = {
112
112
  status(ctx, id) {
113
113
  // Same view the user sees. Reading the stored record here showed the
114
114
  // model Goals the user had already archived away.
115
- ctx.readRecord(id); // Corruption is an actionable error, not "no Goal".
115
+ ctx.readRecord(id); // A real I/O error is an actionable error, not "no Goal".
116
116
  return toolReply(ctx, id, ctx.visibleSnapshot(id), { full: true });
117
117
  },
118
118
  async create(ctx, id, args) {
@@ -23,13 +23,13 @@ tool and brief each agent as the Lead rules describe.`;
23
23
  export const AGENT_MODE_DELEGATION = `# Delegation
24
24
 
25
25
  Handle conversation yourself. Delegate specialised work to sub-agents. Hand
26
- substantial coding to a new coding-mode task session (\`session create\`) with
26
+ substantial coding to a new agent task session (\`session create\`) with
27
27
  a self-contained brief: start it when already requested, otherwise suggest it. Then relay the
28
- result in your own words.`;
28
+ result in your own words. Promise a follow-up only when the session result allows it.`;
29
29
 
30
- /** Mixy only: it also routes requests to the user's contact agents. */
31
- export const MIXY_CONTACTS_INTRO = `You also coordinate the user's contact agents: route a request to the
32
- contact best suited for it with the same kind of brief.`;
30
+ /** Mixy only: the user's other bots, its contact catalog's intro. */
31
+ export const MIXY_CONTACTS_INTRO = `The user's other bots. Hand one a request with \`session send\` to its chat and a
32
+ self-contained brief.`;
33
33
 
34
34
  export function orchestrationInstructions(value) {
35
35
  const mode = normalizeOrchestrationMode(value);
@@ -0,0 +1,81 @@
1
+ // A route's context-window selection against provider windows: the share and
2
+ // window a route names, and what a session is created with for it. Shared by
3
+ // every session builder (spawned agents, hidden-role dispatch, the Lead).
4
+ import { providerCachedModelMetadataSync } from '../providers/provider-catalog-cache.mjs';
5
+ import { contextWindowRange, normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
6
+ import { clean } from './session-text.mjs';
7
+
8
+ // The persisted provider model rows are the SAME source the picker sized its
9
+ // slider against, so they carry both windows (openai-oauth: 272k default /
10
+ // 1M max). Reading them keeps a percentage saved against the picker's scale
11
+ // meaningful; anything that only knows a single window would silently rescale
12
+ // it (30% of 272k instead of 30% of 1M).
13
+ const clampPercent = (percent) => Math.max(10, Math.min(100, percent));
14
+
15
+ function cachedRouteWindows(provider, model) {
16
+ const row = providerCachedModelMetadataSync(provider, model)?.rawProviderModel || null;
17
+ if (!row) return null;
18
+ const contextWindow = Number(row.contextWindow ?? row.context_window ?? row.max_input_tokens) || 0;
19
+ const maxContextWindow = Number(row.maxContextWindow ?? row.max_context_window) || 0;
20
+ return contextWindow > 0 || maxContextWindow > 0 ? { contextWindow, maxContextWindow } : null;
21
+ }
22
+
23
+ export function resolveRouteContextState(targetRoute = {}, modelMeta = null, windowLookup = cachedRouteWindows) {
24
+ // Only providers that implement getCachedModelInfo (openai-oauth, cursor,
25
+ // openai-compat, opencode-go) hand lookupModelMeta a window; every other one
26
+ // (anthropic-oauth, grok-oauth, gemini, …) gets the bare `{ id, provider }`
27
+ // placeholder. Reading the window from that alone yields 0, which dropped the
28
+ // saved context percentage for the entire session — while resolveSessionContextMeta
29
+ // went on to size that session from the provider/catalog window (Claude Opus 5:
30
+ // a full 1M rather than the selected 500k). Read the cached provider row here so
31
+ // the percentage and the session boundary cannot disagree.
32
+ const windowMeta =
33
+ Number(modelMeta?.contextWindow) > 0 || Number(modelMeta?.maxContextWindow) > 0
34
+ ? modelMeta
35
+ : windowLookup?.(clean(targetRoute?.provider), clean(targetRoute?.model)) || modelMeta;
36
+ const { defaultWindow, maxWindow } = contextWindowRange({
37
+ provider: clean(targetRoute?.provider),
38
+ contextWindow: windowMeta?.contextWindow,
39
+ maxContextWindow: windowMeta?.maxContextWindow,
40
+ });
41
+ if (!maxWindow) {
42
+ return { contextPercent: undefined, contextDefaultPercent: undefined, selectedContextWindow: undefined };
43
+ }
44
+ const contextDefaultPercent = clampPercent(Math.round((defaultWindow / maxWindow) * 10) * 10);
45
+ // An exact token selection overrides the 10% stops. It is clamped to the
46
+ // selectable range; one that lands on the default window is the default.
47
+ const requestedTokens = normalizeContextTokens(targetRoute?.contextTokens);
48
+ if (requestedTokens) {
49
+ const contextTokens = Math.max(Math.ceil(maxWindow * 0.1), Math.min(maxWindow, requestedTokens));
50
+ if (contextTokens === defaultWindow) {
51
+ return { contextPercent: contextDefaultPercent, contextDefaultPercent, selectedContextWindow: defaultWindow };
52
+ }
53
+ const contextPercent = clampPercent(Math.round((contextTokens / maxWindow) * 10) * 10);
54
+ return { contextPercent, contextDefaultPercent, selectedContextWindow: contextTokens, contextTokens };
55
+ }
56
+ const requested = Number(targetRoute?.contextPercent);
57
+ const contextPercent =
58
+ Number.isFinite(requested) && requested > 0 ? clampPercent(Math.round(requested / 10) * 10) : contextDefaultPercent;
59
+ const selectedContextWindow =
60
+ contextPercent === contextDefaultPercent
61
+ ? defaultWindow
62
+ : Math.max(1, Math.floor((maxWindow * contextPercent) / 100));
63
+ return { contextPercent, contextDefaultPercent, selectedContextWindow };
64
+ }
65
+
66
+ /**
67
+ * The session fields (`contextPercent`, `selectedContextWindow`, `contextTokens`)
68
+ * a route's own context choice creates a session with, from its resolved
69
+ * `state`. A model whose window is not known resolves to nothing: the choice
70
+ * then travels as stored (the session sizes it from the provider's window)
71
+ * instead of being dropped for the model's default. Only set fields are returned.
72
+ */
73
+ export function contextSelectionFromState(state, route) {
74
+ const windowKnown = state.contextDefaultPercent !== undefined;
75
+ const chosen = {
76
+ contextPercent: state.contextPercent ?? route.contextPercent,
77
+ selectedContextWindow: state.selectedContextWindow,
78
+ contextTokens: state.contextTokens ?? (windowKnown ? undefined : route.contextTokens),
79
+ };
80
+ return Object.fromEntries(Object.entries(chosen).filter(([, value]) => value));
81
+ }
@@ -1,6 +1,7 @@
1
1
  // Session message/preview text helpers. Pure, no runtime-closure deps.
2
2
  import { clean } from '../../../shared/clean.mjs';
3
3
  import { stripInjectedBlocks } from '../../../shared/injected-display-text.mjs';
4
+ import { CONTROL_MARKER_RE } from '../../../shared/tool-execution-contract.mjs';
4
5
 
5
6
  export function sessionMessageText(content) {
6
7
  if (content == null) return '';
@@ -42,7 +43,7 @@ function messageContextText(message) {
42
43
  const SYNTHETIC_SESSION_TEXT_PATTERNS = Object.freeze([
43
44
  /^\[mixdog-runtime\]/i,
44
45
  // User-cancel + process-restart control rows must never title/preview a session.
45
- /^\[(?:truncated|request interrupted by (?:user(?: for tool use)?|process restart))\]$/i,
46
+ CONTROL_MARKER_RE,
46
47
  /^a previous model worked on this task and produced the compacted handoff summary below\b/i,
47
48
  // Compact/auto-clear re-seed handoff variants: these lead the FIRST user
48
49
  // message of every post-compaction session, so titling from them painted
@@ -401,7 +401,18 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
401
401
  const catalog = sortedCatalogByMeasuredUsage([...byName.values()]);
402
402
  const defaultNames = defaultDeferredToolNames(catalog, mode);
403
403
  // Solo keeps `agent` in the deferred pool: loadable on request, never eager.
404
- if (session.orchestrationMode === 'none') defaultNames.delete('agent');
404
+ // The mode the prompt was built with: a mid-conversation workflow or
405
+ // orchestration mode switch (workflowOverlay, orchestrationModeOverlay)
406
+ // never reshapes the surface.
407
+ let orchestrationMode = session.orchestrationMode;
408
+ if (session.workflowOverlay) orchestrationMode = session.workflowOverlay.promptOrchestrationMode;
409
+ else if (session.orchestrationModeOverlay) orchestrationMode = session.orchestrationModeOverlay.prompt;
410
+ if (orchestrationMode === 'none') defaultNames.delete('agent');
411
+ // Tools a workflow switch opened stay loadable, never eager, until the
412
+ // prompt is rebuilt (session-runtime/tool-policy-refresh.mjs).
413
+ for (const name of Array.isArray(session.workflowOpenedTools) ? session.workflowOpenedTools : []) {
414
+ defaultNames.delete(name);
415
+ }
405
416
  // Bridge sessions expose the mode's core only: a stored (legacy all-selected)
406
417
  // selection never widens the wire surface, and load_tool is the bridge's own.
407
418
  const storedNames = providerMode === 'native' || bridge ? [] : storedDeferredToolNames(session);
@@ -73,9 +73,9 @@ export const SESSION_TOOL = {
73
73
  openWorldHint: false,
74
74
  agentHidden: true,
75
75
  },
76
- trigger: 'Session lookup/control, coding task sessions, title, compact, fork, Project and worktrees.',
76
+ trigger: 'Session lookup/control, bot status, agent task sessions, title, compact, fork, Project and worktrees.',
77
77
  description:
78
- 'Inspect sessions or control another by exact ID, never a guessed one. send acknowledges queue intake, not completion. Cancel/delete and worktree actions require the user’s request. Other actions affect only the current session.',
78
+ 'Inspect sessions or control another by exact ID, never a guessed one. send acknowledges queue intake, not completion: promise a report of it only when its result has reportsBack: true. create returns status: started — started, not done yet; it reports its final answer, after the agents it started finish, unless its result says reportsBack: false. reportsBack: false carries reportsBackReason, the reason to tell the user. Cancel/delete and worktree actions require the user’s request. Other actions affect only the current session. Pass files on with attachments; a reported turn returns the files it produced as attachments.',
79
79
  inputSchema: {
80
80
  type: 'object',
81
81
  properties: {
@@ -84,6 +84,7 @@ export const SESSION_TOOL = {
84
84
  enum: [
85
85
  'status',
86
86
  'list',
87
+ 'bots',
87
88
  'read',
88
89
  'send',
89
90
  'cancel',
@@ -102,13 +103,13 @@ export const SESSION_TOOL = {
102
103
  'prune-worktrees',
103
104
  ],
104
105
  description:
105
- 'status defaults to current; list pages sessions; read returns latest items; send queues prompt; cancel stops the turn; delete removes the session. create (agent conversations) starts a visible coding task session from brief and reports its turns back; unknown Project: list-projects first, still ambiguous: ask the user. compact runs after this turn; fork copies it without switching. merge-worktree merges into the Project’s checked-out branch; remove-worktree returns to the Project (repeat on WORKTREE_REMOVAL_INCOMPLETE); list-worktrees includes retaining sessions; prune-worktrees removes only clean, merged, unretained worktrees.',
106
+ 'status defaults to current; list pages sessions; bots shows each bot’s activity, work, approvals, routines and attention flags, or with session_id (a bot id or its chat) its routines and recent sessions; read returns latest items; send queues prompt (reportsBack: the turn result reports back here); cancel stops the turn; delete removes the session. create (bot conversations) starts a visible agent task session from brief (started, not done yet); unknown Project: list-projects first, still ambiguous: ask the user. compact runs after this turn; fork copies it without switching. merge-worktree merges into the Project’s checked-out branch; remove-worktree returns to the Project (repeat on WORKTREE_REMOVAL_INCOMPLETE); list-worktrees includes retaining sessions; prune-worktrees removes only clean, merged, unretained worktrees.',
106
107
  },
107
108
  title: { type: 'string', description: 'rename: new current-session title; create: task title.' },
108
109
  session_id: {
109
110
  type: 'string',
110
111
  description:
111
- 'ID from list; required for read/send/cancel/delete, optional for status. send/cancel/delete cannot target the caller.',
112
+ 'ID from list; required for read/send/cancel/delete, optional for status/bots. send/cancel/delete cannot target the caller.',
112
113
  },
113
114
  prompt: { type: 'string', description: 'send: message to enqueue; loads a cold session.' },
114
115
  limit: {
@@ -116,7 +117,7 @@ export const SESSION_TOOL = {
116
117
  minimum: 1,
117
118
  maximum: 100,
118
119
  description:
119
- 'list/read: maximum rows, default 25. Output: 8KiB per page, 12KiB for initial read tail; follow nextOffset/nextTextOffset for more.',
120
+ 'list/read: maximum rows, default 25; bots: sessions, default 10. Output: 8KiB per page, 12KiB for initial read tail; follow nextOffset/nextTextOffset for more.',
120
121
  },
121
122
  cursor: { type: 'string', description: 'list: returned nextCursor.' },
122
123
  offset: {
@@ -147,8 +148,18 @@ export const SESSION_TOOL = {
147
148
  value: { type: 'boolean', description: 'favorite/archive: on or off.' },
148
149
  brief: { type: 'string', description: 'create: self-contained instruction, the first message.' },
149
150
  project: { type: 'string', description: 'create: registered Project name or existing directory.' },
150
- model: { type: 'string', description: 'create: provider/model or preset; default route otherwise.' },
151
+ model: {
152
+ type: 'string',
153
+ description: 'create: provider/model or preset; default is the Lead model (else the last-used one).',
154
+ },
151
155
  notify: { type: 'boolean', description: 'create: report results back, default true.' },
156
+ attachments: {
157
+ type: 'array',
158
+ items: { type: 'string' },
159
+ maxItems: 8,
160
+ description:
161
+ 'send/create: files the target turn receives as real attachments (images, PDF/Office, text): an attachment of this conversation by its file name (a user upload, e.g. its [Image: source: name]) or id (a reported file’s [attachment:id]), or a file path inside this session’s folders. Secret files are refused.',
162
+ },
152
163
  },
153
164
  additionalProperties: false,
154
165
  },