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
@@ -4,8 +4,22 @@ import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.
4
4
  import { normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
5
5
  import { ensureProviderEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
6
6
  import { resolveRouteContextState, selectionFastCapable } from '../route-state.mjs';
7
- import { canonicalizeAgentRouteStorage, withAgentDisabled } from '../../runtime/shared/agent-route-config.mjs';
8
- import { agentEditorId } from './shared.mjs';
7
+ import {
8
+ canonicalizeAgentRouteStorage,
9
+ isAgentDisabled,
10
+ withAgentDisabled,
11
+ } from '../../runtime/shared/agent-route-config.mjs';
12
+ import { agentEditorId, resolveDataDir } from './shared.mjs';
13
+ import {
14
+ isStoreBot,
15
+ readBotRoute,
16
+ readMixyRoute,
17
+ writeBotRoute,
18
+ writeMixyRoute,
19
+ } from '../../runtime/shared/agent-store.mjs';
20
+
21
+ /** Mixy's runtime id (agent-home.mjs MIXY_AGENT_ID). */
22
+ const MIXY_RUNTIME_ID = 'lead';
9
23
  import { routeChain, validateRouteChainProviders } from '../../runtime/shared/model-route-chain.mjs';
10
24
 
11
25
  // Agent route persistence. Route-scope isolation: an agent route owns its
@@ -15,16 +29,86 @@ import { routeChain, validateRouteChainProviders } from '../../runtime/shared/mo
15
29
  // lead preset, so an agent that reused it silently rewrote Main's effort/fast
16
30
  // for the next session. The agent's own stored route is the fallback instead,
17
31
  // and the bucket is never written here.
32
+ // Tuning-only edits keep the stored model's effort/fast/modelParameters;
33
+ // a model change starts from the resolved defaults. Fast is then clamped
34
+ // to what the selected model supports. The ONE normalizer of a chain entry:
35
+ // agents, bots, Mixy and the Lead's fallbacks all pass through it.
36
+ export async function resolveChainEntry(deps, requested, stored) {
37
+ const { getConfig, resolveRoute, lookupModelMeta, ensureProvidersReady } = deps;
38
+ let selectedRoute = resolveRoute(getConfig(), requested);
39
+ const sameModel =
40
+ clean(selectedRoute.provider) === clean(stored.provider) && clean(selectedRoute.model) === clean(stored.model);
41
+ const inheritedEffort = sameModel ? stored.effort || null : null;
42
+ const inheritedModelParameters = sameModel ? stored.modelParameters || {} : {};
43
+ // A requested percent replaces the stored exact window and requested
44
+ // tokens replace the stored percent.
45
+ const requestsPercent = requested.contextPercent !== undefined;
46
+ const requestsTokens = requested.contextTokens !== undefined;
47
+ const inheritedContextPercent = sameModel && !requestsTokens ? stored.contextPercent : undefined;
48
+ const inheritedContextTokens = sameModel && !requestsPercent ? stored.contextTokens : undefined;
49
+ selectedRoute = {
50
+ ...selectedRoute,
51
+ effort: requested.effort !== undefined ? selectedRoute.effort : inheritedEffort,
52
+ fast: requested.fast !== undefined ? selectedRoute.fast === true : sameModel && stored.fast === true,
53
+ // Main's modelSettings bucket must not decide an agent's Auto effort.
54
+ autoEffort:
55
+ requested.autoEffort !== undefined ? requested.autoEffort !== false : !sameModel || stored.autoEffort !== false,
56
+ modelParameters: requested.modelParameters !== undefined ? selectedRoute.modelParameters : inheritedModelParameters,
57
+ // resolveRoute reads contextPercent from Main's modelSettings bucket;
58
+ // an agent route owns its own.
59
+ contextPercent: requestsPercent ? requested.contextPercent : inheritedContextPercent,
60
+ contextTokens: requestsTokens ? requested.contextTokens : inheritedContextTokens,
61
+ };
62
+ await ensureProvidersReady(ensureProviderEnabled(getConfig(), selectedRoute.provider));
63
+ const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
64
+ // Stored clamped, and not at all at the default window, as for Main.
65
+ const contextState = resolveRouteContextState(selectedRoute, modelMeta);
66
+ if (contextState.contextDefaultPercent !== undefined) selectedRoute.contextTokens = contextState.contextTokens;
67
+ const fastCapable = selectionFastCapable(
68
+ selectedRoute.provider,
69
+ modelMeta,
70
+ selectedRoute.effort,
71
+ selectedRoute.modelParameters
72
+ );
73
+ return { ...selectedRoute, fast: fastCapable ? selectedRoute.fast === true : false };
74
+ }
75
+
18
76
  export function createAgentRouteApi(deps) {
19
- const { getConfig, resolveRoute, lookupModelMeta, saveConfigAndAdopt, ensureProvidersReady, agentRouteFromConfig } =
20
- deps;
77
+ const { getConfig, resolveRoute, saveConfigAndAdopt, agentRouteFromConfig } = deps;
21
78
 
22
79
  function saveDisabled(id, disabled) {
23
80
  saveConfigAndAdopt(canonicalizeAgentRouteStorage(withAgentDisabled(getConfig(), id, disabled)));
24
81
  }
25
82
 
83
+ // A bot of the store keeps its route in its own folder (agent-store.mjs);
84
+ // Mixy (`lead` here, its runtime id) keeps its own in bots/mixy — never the
85
+ // Lead's route, which the Main route setter owns.
86
+ const dataDir = () => (deps.cfgMod || deps.STANDALONE_DATA_DIR ? resolveDataDir(deps) : '');
87
+ const isMixy = (id) => id === MIXY_RUNTIME_ID && Boolean(dataDir());
88
+ const storeBot = (id) => isStoreBot(dataDir(), id);
89
+ function saveRoute(id, route, keepOff) {
90
+ if (isMixy(id)) {
91
+ writeMixyRoute(dataDir(), route);
92
+ return;
93
+ }
94
+ if (storeBot(id)) {
95
+ writeBotRoute(dataDir(), id, route);
96
+ saveDisabled(id, keepOff);
97
+ return;
98
+ }
99
+ const nextConfig = withAgentDisabled(getConfig(), id, keepOff);
100
+ nextConfig.agents = { ...(nextConfig.agents || {}), [id]: route };
101
+ saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
102
+ }
103
+
26
104
  // Only an explicitly empty provider removes an override: the agent inherits Main.
27
105
  function clearOverride(id) {
106
+ if (isMixy(id) || storeBot(id)) {
107
+ if (isMixy(id)) writeMixyRoute(dataDir(), null);
108
+ else writeBotRoute(dataDir(), id, null);
109
+ const inherited = normalizeWorkflowRoute(resolveRoute(getConfig(), {}));
110
+ return { ...(inherited || {}), inherited: true };
111
+ }
28
112
  const nextConfig = { ...getConfig() };
29
113
  const agents = { ...(nextConfig.agents || {}) };
30
114
  const hadOverride = Object.hasOwn(agents, id);
@@ -35,55 +119,25 @@ export function createAgentRouteApi(deps) {
35
119
  return { ...(inherited || {}), inherited: true };
36
120
  }
37
121
 
38
- // Tuning-only edits keep the stored model's effort/fast/modelParameters;
39
- // a model change starts from the resolved defaults. Fast is then clamped
40
- // to what the selected model supports.
41
- async function resolveSelectedRoute(requested, stored) {
42
- let selectedRoute = resolveRoute(getConfig(), requested);
43
- const sameModel =
44
- clean(selectedRoute.provider) === clean(stored.provider) && clean(selectedRoute.model) === clean(stored.model);
45
- const inheritedEffort = sameModel ? stored.effort || null : null;
46
- const inheritedModelParameters = sameModel ? stored.modelParameters || {} : {};
47
- // A requested percent replaces the stored exact window and requested
48
- // tokens replace the stored percent.
49
- const requestsPercent = requested.contextPercent !== undefined;
50
- const requestsTokens = requested.contextTokens !== undefined;
51
- const inheritedContextPercent = sameModel && !requestsTokens ? stored.contextPercent : undefined;
52
- const inheritedContextTokens = sameModel && !requestsPercent ? stored.contextTokens : undefined;
53
- selectedRoute = {
54
- ...selectedRoute,
55
- effort: requested.effort !== undefined ? selectedRoute.effort : inheritedEffort,
56
- fast: requested.fast !== undefined ? selectedRoute.fast === true : sameModel && stored.fast === true,
57
- // Main's modelSettings bucket must not decide an agent's Auto effort.
58
- autoEffort:
59
- requested.autoEffort !== undefined ? requested.autoEffort !== false : !sameModel || stored.autoEffort !== false,
60
- modelParameters:
61
- requested.modelParameters !== undefined ? selectedRoute.modelParameters : inheritedModelParameters,
62
- // resolveRoute reads contextPercent from Main's modelSettings bucket;
63
- // an agent route owns its own.
64
- contextPercent: requestsPercent ? requested.contextPercent : inheritedContextPercent,
65
- contextTokens: requestsTokens ? requested.contextTokens : inheritedContextTokens,
66
- };
67
- await ensureProvidersReady(ensureProviderEnabled(getConfig(), selectedRoute.provider));
68
- const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
69
- // Stored clamped, and not at all at the default window, as for Main.
70
- const contextState = resolveRouteContextState(selectedRoute, modelMeta);
71
- if (contextState.contextDefaultPercent !== undefined) selectedRoute.contextTokens = contextState.contextTokens;
72
- const fastCapable = selectionFastCapable(
73
- selectedRoute.provider,
74
- modelMeta,
75
- selectedRoute.effort,
76
- selectedRoute.modelParameters
77
- );
78
- return { ...selectedRoute, fast: fastCapable ? selectedRoute.fast === true : false };
79
- }
80
-
81
122
  async function setAgentRoute(agentId, next) {
82
123
  const id = agentEditorId(agentId);
83
124
  if (!id) throw new Error(`unknown agent "${agentId}"`);
84
125
  if (isHiddenAgent(id)) throw new Error(`agent "${id}" is internal and has no configurable route`);
85
126
  const requested = { ...(next || {}) };
86
- const stored = agentRouteFromConfig(getConfig(), id) || {};
127
+ // Compare-and-set: fill the route only while the agent has none. A route
128
+ // picked while the model resolved below stays (answered with `skipped`).
129
+ const onlyIfEmpty = requested.onlyIfEmpty === true;
130
+ delete requested.onlyIfEmpty;
131
+ const readStored = () => {
132
+ if (isMixy(id)) return readMixyRoute(dataDir()) || {};
133
+ if (storeBot(id)) return readBotRoute(dataDir(), id) || {};
134
+ return agentRouteFromConfig(getConfig(), id) || {};
135
+ };
136
+ const kept = () => ({ ...readStored(), id, skipped: true });
137
+ const stored = readStored();
138
+ if (onlyIfEmpty && clean(stored.provider)) return kept();
139
+ // Mixy is never off: its route request is a model pick alone.
140
+ if (isMixy(id)) delete requested.disabled;
87
141
  // Off is an explicit state, stored apart from the route so the model the
88
142
  // user picked survives and comes back when the agent is switched on. An
89
143
  // off request that also carries a model saves that model and stays off.
@@ -121,16 +175,22 @@ export function createAgentRouteApi(deps) {
121
175
  const previous = storedChain.find(
122
176
  (candidate) => candidate.provider === entry.provider && candidate.model === entry.model
123
177
  );
124
- resolved.push(await resolveSelectedRoute(entry, previous || {}));
178
+ resolved.push(await resolveChainEntry(deps, entry, previous || {}));
125
179
  }
126
180
  const [primary, ...fallbacks] = resolved;
127
181
  const routeToSave = normalizeWorkflowRoute({ ...primary, fallbacks });
128
182
  if (!routeToSave) throw new Error('agent route requires provider and model');
183
+ // No await from here to the write: the emptiness checked is the emptiness
184
+ // written over. An off agent stays off when it was only back-filled.
185
+ // A store bot's or Mixy's route is read from its folder on disk right here.
186
+ // An agent routed in the config file may have been written by another
187
+ // process whose change notification is still pending: re-read it first.
188
+ if (onlyIfEmpty && !isMixy(id) && !storeBot(id)) deps.reloadFullConfig?.();
189
+ if (onlyIfEmpty && clean(readStored().provider)) return kept();
190
+ const stayOff = onlyIfEmpty ? isAgentDisabled(getConfig(), id) : keepOff;
129
191
  // Picking a model is also the "on" switch unless the request keeps it off.
130
- const nextConfig = withAgentDisabled(getConfig(), id, keepOff);
131
- nextConfig.agents = { ...(nextConfig.agents || {}), [id]: routeToSave };
132
- saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
133
- return keepOff ? { ...routeToSave, id, disabled: true } : routeToSave;
192
+ saveRoute(id, routeToSave, stayOff);
193
+ return stayOff ? { ...routeToSave, id, disabled: true } : routeToSave;
134
194
  }
135
195
 
136
196
  return { setAgentRoute };
@@ -25,6 +25,7 @@ function applyWorkflowSlots(workflowInput, draft) {
25
25
  if (!normalized) continue;
26
26
  draft.presets = upsertWorkflowPreset(draft.presets, 'lead', normalized);
27
27
  draft.defaultPreset = workflowPresetId('lead');
28
+ draft.leadRoute = normalized;
28
29
  } else if (slot === 'agent') {
29
30
  if (normalized) draft.agentRoutes.worker = normalized;
30
31
  else delete draft.agentRoutes.worker;
@@ -72,10 +73,13 @@ export function createOnboardingApi(deps) {
72
73
  presets: Array.isArray(nextConfig.presets) ? nextConfig.presets.slice() : [],
73
74
  defaultPreset: nextConfig.default,
74
75
  agentRoutes: { ...(nextConfig.agents || {}) },
76
+ // The Lead route this save writes, from `defaultRoute` or `workflowRoutes.lead`.
77
+ leadRoute: null,
75
78
  };
76
79
  if (defaultRoute) {
77
80
  draft.presets = upsertWorkflowPreset(draft.presets, 'lead', defaultRoute);
78
81
  draft.defaultPreset = workflowPresetId('lead');
82
+ draft.leadRoute = defaultRoute;
79
83
  }
80
84
  const workflowInput =
81
85
  payload.workflowRoutes && typeof payload.workflowRoutes === 'object' ? payload.workflowRoutes : {};
@@ -104,12 +108,15 @@ export function createOnboardingApi(deps) {
104
108
  }
105
109
 
106
110
  saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
107
- if (defaultRoute) {
111
+ // Whatever wrote the Lead route also moves the runtime Main route, in the
112
+ // same synchronous step as the save: a pending conditional backfill then
113
+ // finds a route that is not empty instead of overwriting this choice.
114
+ if (draft.leadRoute) {
108
115
  setRouteState(
109
116
  resolveRoute(getConfig(), {
110
- provider: defaultRoute.provider,
111
- model: defaultRoute.model,
112
- effort: defaultRoute.effort,
117
+ provider: draft.leadRoute.provider,
118
+ model: draft.leadRoute.model,
119
+ effort: draft.leadRoute.effort,
113
120
  })
114
121
  );
115
122
  deps.invalidatePreSessionToolSurface?.();
@@ -1,5 +1,9 @@
1
1
  import { oneLine } from '../../runtime/shared/clean.mjs';
2
- import { normalizeAgentId, normalizeWorkflowId, normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
2
+ import {
3
+ normalizeAgentId,
4
+ normalizeWorkflowId,
5
+ normalizeWorkflowRoute,
6
+ } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
3
7
 
4
8
  export function resolveDataDir({ cfgMod, STANDALONE_DATA_DIR }) {
5
9
  return cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
@@ -35,10 +35,23 @@ export function createStyleAndModeApi(deps) {
35
35
  return { ...freshStatus, appliedToCurrentSession };
36
36
  }
37
37
 
38
- async function setOrchestrationMode(mode) {
38
+ // `{ scope: 'session' }` (the composer's selector in a session), like
39
+ // setWorkflow: a session with a conversation switches alone
40
+ // (switchSessionOrchestrationMode; deferred to its next turn while one
41
+ // runs) and the configured mode — the default for new sessions — stays.
42
+ // Otherwise the choice becomes the configured mode and an empty session
43
+ // picks it up in place.
44
+ async function setOrchestrationMode(mode, options = {}) {
39
45
  if (!ORCHESTRATION_MODES.includes(mode)) {
40
46
  throw new Error(`orchestration mode must be one of ${ORCHESTRATION_MODES.join(', ')}`);
41
47
  }
48
+ if (options?.scope === 'session') {
49
+ const switched = await deps.switchSessionOrchestrationMode?.(mode);
50
+ if (switched) {
51
+ invalidateContextStatusCache();
52
+ return switched;
53
+ }
54
+ }
42
55
  saveConfigAndAdopt({ ...getConfig(), orchestrationMode: mode });
43
56
  const applied = await deps.refreshEmptySessionToolPolicy?.();
44
57
  deps.invalidatePreSessionToolSurface?.();
@@ -1,5 +1,6 @@
1
1
  import { join } from 'node:path';
2
- import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
3
+ import { writeFileAtomicSync } from '../../runtime/shared/atomic-file.mjs';
3
4
  import { serializeFrontmatterDoc } from '../../runtime/shared/markdown-frontmatter.mjs';
4
5
  import {
5
6
  normalizeWorkflowId,
@@ -44,6 +45,7 @@ export function createWorkflowPacksApi(deps) {
44
45
  name: workflow.name,
45
46
  description: workflow.description,
46
47
  source: workflow.source,
48
+ ...(workflow.overridesBuiltIn ? { overridesBuiltIn: true } : {}),
47
49
  active: workflow.id === active,
48
50
  }));
49
51
  }
@@ -106,9 +108,10 @@ export function createWorkflowPacksApi(deps) {
106
108
  if (!absorbed) {
107
109
  const dir = join(dataDir, 'workflows', id);
108
110
  mkdirSync(dir, { recursive: true });
109
- writeFileSync(
111
+ writeFileAtomicSync(
110
112
  join(dir, 'WORKFLOW.md'),
111
- serializeFrontmatterDoc({ id, name, ...(description ? { description } : {}) }, body)
113
+ serializeFrontmatterDoc({ id, name, ...(description ? { description } : {}) }, body),
114
+ { backup: true, mode: 0o644 }
112
115
  );
113
116
  }
114
117
  return getWorkflowPack(id);
@@ -4,8 +4,9 @@
4
4
  * resume (missing worktree → recovery), and carrying the Project's memory
5
5
  * identity into a new worktree.
6
6
  */
7
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
8
- import { dirname, join } from 'node:path';
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { holdsNoData, writeFileAtomicSync } from '../../runtime/shared/atomic-file.mjs';
9
10
  import { isWorktreeBound, nextRevision, normalizeSessionWorkspace } from '../../runtime/shared/worktrees/contract.mjs';
10
11
  import { isWithin, samePath } from '../../runtime/shared/worktrees/paths.mjs';
11
12
  import { resolveProjectId } from '../../runtime/memory/lib/project-id-resolver.mjs';
@@ -103,10 +104,10 @@ export function restoreWorkspaceAfterResume(session) {
103
104
  */
104
105
  export function copyProjectIdentity(projectPath, worktreePath) {
105
106
  const target = join(worktreePath, '.mixdog', 'project.id');
106
- if (existsSync(target)) return false;
107
+ // An id file with no data (empty, or NUL-filled by a crash) is missing.
108
+ if (existsSync(target) && !holdsNoData(readFileSync(target))) return false;
107
109
  const id = resolveProjectId(projectPath);
108
- if (!id) return false;
109
- mkdirSync(dirname(target), { recursive: true });
110
- writeFileSync(target, `${id}\n`, 'utf8');
110
+ if (!id || holdsNoData(id)) return false;
111
+ writeFileAtomicSync(target, `${id}\n`, { mode: 0o644 });
111
112
  return true;
112
113
  }
@@ -54,7 +54,10 @@ export function createCanonicalAgentControl({ getSessionService, getSessionRunti
54
54
  ownerSessionId: sessionService.rootOwnerSessionId(parentSessionId),
55
55
  };
56
56
  if (String(args?.type || '') === '__close_all') {
57
- await tool.closeAll?.(String(args?.reason || 'agent owner closed'), { callerSessionId: parentSessionId || null });
57
+ await tool.closeAll?.(String(args?.reason || 'agent owner closed'), {
58
+ callerSessionId: parentSessionId || null,
59
+ processExit: args?.processExit === true,
60
+ });
58
61
  await sessionService.cancelAgentDescendants(parentSessionId, String(args?.reason || 'agent owner closed'));
59
62
  return 'agent close all: ok';
60
63
  }
@@ -1,18 +1,25 @@
1
1
  import { SESSION_ID_PATTERN } from '../runtime/agent/orchestrator/runtime-core/session-id.mjs';
2
2
  import { sessionListOutput, sessionReadOutput, SESSION_TOOL_OUTPUT_BYTES } from './session-tool-output.mjs';
3
3
  import { createAgentTaskSessions } from '../session-runtime/agent-task-sessions.mjs';
4
+ import { createBotStatus, scanCatalog, scopedBot, sessionBotOf } from '../session-runtime/bot-status.mjs';
5
+ import { handoffContent } from '../session-runtime/handoff-attachments.mjs';
6
+ import { BOT_HOP_LIMIT_ERROR, MAX_BOT_HOPS } from '../runtime/shared/bot-hops.mjs';
7
+ import { rebindDelegation } from '../runtime/agent/orchestrator/session/human-gate/delegation.mjs';
4
8
 
5
9
  const TARGET_ACTIONS = new Set(['status', 'read', 'send', 'cancel', 'delete']);
6
10
  const MUTATING_ACTIONS = new Set(['send', 'cancel', 'delete']);
11
+ // Why a send from a conversation that is no bot's reports nothing back.
12
+ const NOT_BOT_REASON = 'only a bot conversation gets session results reported back; check that session for the result';
7
13
 
8
14
  /** Model-facing session operations over the same service used by desktop and TUI. */
9
15
  export function createSessionControl({
10
16
  getSessionService,
11
17
  agentTasks = createAgentTaskSessions({ getSessionService }),
18
+ botStatus = createBotStatus({ getSessionService }),
12
19
  }) {
13
20
  return async function executeSessionControl(args = {}, context = {}) {
14
21
  const action = args.action;
15
- if (action !== 'list' && action !== 'create' && !TARGET_ACTIONS.has(action)) {
22
+ if (action !== 'list' && action !== 'create' && action !== 'bots' && !TARGET_ACTIONS.has(action)) {
16
23
  throw new Error(`session: unsupported remote action "${action}"`);
17
24
  }
18
25
  const caller = context.callerSessionId;
@@ -21,8 +28,10 @@ export function createSessionControl({
21
28
  }
22
29
  // `callerAgentId`: set only for an attended agent conversation (workspace-tools.mjs).
23
30
  if (action === 'create') return agentTasks.create(args, context);
31
+ // `callerScope`: the caller's schedule-access scope (workspace-tools.mjs).
32
+ if (action === 'bots') return botStatus(args, context);
24
33
  const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
25
- if (TARGET_ACTIONS.has(action) && (typeof sessionId !== 'string' || !SESSION_ID_PATTERN.test(sessionId))) {
34
+ if (TARGET_ACTIONS.has(action) && !SESSION_ID_PATTERN.test(sessionId)) {
26
35
  throw new Error(`session: session_id is required for action=${action}`);
27
36
  }
28
37
  if (MUTATING_ACTIONS.has(action) && sessionId === caller) {
@@ -44,22 +53,63 @@ export function createSessionControl({
44
53
  const service = getSessionService();
45
54
  if (!service) throw new Error('session: session service is unavailable');
46
55
  if (action === 'list') {
56
+ // Scoped like bots (`callerScope`, bot-status.mjs): the human's sessions
57
+ // list everything, a bot (or its unattended run) only its own sessions,
58
+ // anything else the plain rows without previews.
59
+ const own = scopedBot(context.callerScope);
60
+ if (own) {
61
+ const scan = await scanCatalog(service, (row) => sessionBotOf(row) === own, {
62
+ cursor: args.cursor || null,
63
+ want: limit,
64
+ });
65
+ return sessionListOutput({ sessions: scan.rows, nextCursor: scan.nextCursor }, limit);
66
+ }
47
67
  const page = await service.listSessionCatalog({ page: { limit, cursor: args.cursor || null } });
48
- return sessionListOutput(page, limit);
68
+ return sessionListOutput(page, limit, { rich: own === '' });
49
69
  }
50
70
  if (action === 'send') {
51
71
  if (typeof args.prompt !== 'string' || !args.prompt.trim()) {
52
72
  throw new Error('session: prompt is required for action=send');
53
73
  }
54
- // The agent's follow-up in a task session it created reports back too.
55
- const origin = await agentTasks.originFor(sessionId, context.callerAgentId);
56
- if (origin) return { sessionId, accepted: await agentTasks.send(sessionId, args.prompt, origin) };
74
+ // An agent conversation's send reports its turn back to the agent's
75
+ // home: a task session it created by its stored origin, any other
76
+ // session by an origin for this send only.
77
+ // Forwarded files ride along as stored attachment parts (workspace-tools.mjs).
78
+ const prompt = handoffContent(args.prompt, context.attachmentParts);
79
+ let origin = await agentTasks.reportOrigin(sessionId, context.callerAgentId, caller);
80
+ // A send made in a bot-descended turn (`callerHop` > 0), or by a bot to
81
+ // another session, extends the chain by one; a bot's own task session
82
+ // only continues it. Refused past the limit, whatever the target.
83
+ const turnHop = Number(context.callerHop) || 0;
84
+ const chained = (hop) => {
85
+ if (hop > MAX_BOT_HOPS) throw new Error(BOT_HOP_LIMIT_ERROR);
86
+ return hop;
87
+ };
88
+ if (origin) {
89
+ const hop = chained(origin.target === 'session' ? turnHop + 1 : turnHop);
90
+ if (hop > 0) origin = { ...origin, hop };
91
+ // The task session a bot created acts as the sending conversation does
92
+ // from now on (attended chat or run); another's session keeps its own.
93
+ if (origin.target !== 'session') await rebindDelegation(sessionId, caller);
94
+ const sent = await agentTasks.send(sessionId, prompt, origin);
95
+ return { sessionId, ...sent, ...(hop > 0 ? { hop } : {}) };
96
+ }
97
+ const hop = turnHop > 0 ? chained(turnHop + 1) : 0;
98
+ // New work from this sender: a delegated target acts as the sender does.
99
+ await rebindDelegation(sessionId, caller);
57
100
  const result = await service.submitSession({
58
101
  sessionId,
59
- prompt: args.prompt,
60
- options: { mode: 'prompt', transcriptMeta: { sender: 'lead' } },
102
+ prompt,
103
+ options: { mode: 'prompt', transcriptMeta: { sender: 'lead', ...(hop > 0 ? { hop } : {}) } },
61
104
  });
62
- return { sessionId, accepted: result.accepted === true };
105
+ const accepted = result.accepted === true;
106
+ return {
107
+ sessionId,
108
+ accepted,
109
+ reportsBack: false,
110
+ ...(accepted ? { reportsBackReason: NOT_BOT_REASON } : {}),
111
+ ...(hop > 0 ? { hop } : {}),
112
+ };
63
113
  }
64
114
  if (action === 'cancel') {
65
115
  const result = await service.abortSession({ sessionId });
@@ -83,7 +133,7 @@ export function createSessionControl({
83
133
  });
84
134
  const snapshot = result.full;
85
135
  if (!snapshot) throw new Error(`session: ${sessionId} is unavailable`);
86
- const status = {
136
+ return {
87
137
  sessionId,
88
138
  title: snapshot.title ?? null,
89
139
  cwd: snapshot.cwd ?? null,
@@ -92,6 +142,5 @@ export function createSessionControl({
92
142
  busy: snapshot.busy === true,
93
143
  quotaResume: snapshot.quotaResume ?? null,
94
144
  };
95
- return status;
96
145
  };
97
146
  }
@@ -96,6 +96,7 @@ import { createChannelsRuntimeLoader } from './daemon-channels-loader.mjs';
96
96
  import { createDesktopRuntime } from './daemon-desktop-runtime.mjs';
97
97
  import { createCanonicalAgentControl } from './daemon-agent-control.mjs';
98
98
  import { createSessionControl } from './daemon-session-control.mjs';
99
+ import { createAgentTaskSessions } from '../session-runtime/agent-task-sessions.mjs';
99
100
  import { flushAllIndexWrites } from '../session-runtime/services/agent-tool/index-write-queue.mjs';
100
101
  import {
101
102
  compareRuntimeVersions,
@@ -926,6 +927,15 @@ async function main() {
926
927
  cwd: CWD,
927
928
  });
928
929
  canonicalAgentControl = agentControl;
930
+ // Agents' cross-session sends and task sessions, and their report watches.
931
+ const agentTasks = createAgentTaskSessions({
932
+ getSessionService: () => sessionService,
933
+ // A task session re-attached after a restart takes its agents' queued
934
+ // results through the owner runtime's notification bus.
935
+ deliverCompletion: (sessionId, text, meta) =>
936
+ sessionRuntimeHost?.notifySessionCompletion?.(sessionId, text, meta) === true,
937
+ settleAgentResults: async () => (await agentControl.canonicalAgentTool()).settleRestartFailures(),
938
+ });
929
939
  const requestedRuntimeMode = String(process.env.MIXDOG_SESSION_RUNTIME_MODE || '')
930
940
  .trim()
931
941
  .toLowerCase();
@@ -939,7 +949,7 @@ async function main() {
939
949
  log,
940
950
  measureBootPhase: bootPhases.measure,
941
951
  executeAgentControl: agentControl.execute,
942
- executeSessionControl: createSessionControl({ getSessionService: () => sessionService }),
952
+ executeSessionControl: createSessionControl({ getSessionService: () => sessionService, agentTasks }),
943
953
  });
944
954
  // Codex-style runtime: daemon routing and independent async session actors
945
955
  // share one V8 isolate and module graph. CPU-heavy work stays in bounded
@@ -986,9 +996,27 @@ async function main() {
986
996
  setAgentHomeLiveSurface({
987
997
  busy: (sessionId) => sessionService.liveSessionBusy(sessionId),
988
998
  notice: (sessionId, notice) => sessionService.pushLiveSessionNotice(sessionId, notice),
989
- // A task session's result (session create): the agent relays it in one turn.
990
- wake: (sessionId, prompt) =>
991
- sessionService.submitSession({ sessionId, prompt, options: { mode: 'prompt', kickoff: true } }),
999
+ // A task session's result (session create): the agent relays it in one
1000
+ // turn. Resolves false when the turn was refused, else `{ settled }`, the
1001
+ // turn's end status ('done' when it finished).
1002
+ wake: async (sessionId, prompt, { id, hop = 0 } = {}) => {
1003
+ let settle = () => {};
1004
+ const settled = new Promise((resolveSettled) => {
1005
+ settle = resolveSettled;
1006
+ });
1007
+ const result = await sessionService.submitSession({
1008
+ sessionId,
1009
+ prompt,
1010
+ options: {
1011
+ mode: 'prompt',
1012
+ kickoff: true,
1013
+ ...(id ? { id } : {}),
1014
+ ...(hop > 0 ? { transcriptMeta: { hop } } : {}),
1015
+ onSettled: (detail) => settle(String(detail?.status || 'failed')),
1016
+ },
1017
+ });
1018
+ return result?.accepted === true ? { settled } : false;
1019
+ },
992
1020
  // A routine fire: one queued turn in its bot's thread (routine-home-turn.mjs).
993
1021
  submit: async (sessionId, prompt, options) =>
994
1022
  (await sessionService.submitSession({ sessionId, prompt, options }))?.accepted === true,
@@ -997,6 +1025,8 @@ async function main() {
997
1025
  // Results queued before a restart are posted now; results a finished run
998
1026
  // could not queue (kept on its routine's record) are queued again.
999
1027
  recoverScheduleResults().catch((error) => log(`routine results recovery failed: ${error?.message || error}`));
1028
+ // Agents' sends whose report an earlier process had not queued yet.
1029
+ agentTasks.recover().catch((error) => log(`agent task report recovery failed: ${error?.message || error}`));
1000
1030
  recoverRoutineDeliveries().catch((error) => log(`routine delivery recovery failed: ${error?.message || error}`));
1001
1031
  sessionTransport = createSessionTransport({
1002
1032
  // ctx carries the CLIENT token: the session service refcounts views across
@@ -12,7 +12,7 @@ import { performance } from 'node:perf_hooks';
12
12
  import { isDeepStrictEqual } from 'node:util';
13
13
  import { bootProfile } from '../tui/session/boot-profile.mjs';
14
14
  import { createGoalContinuation } from '../tui/session/goal-continuation.mjs';
15
- import { createToolApproval } from '../tui/session/tool-approval.mjs';
15
+ import { createToolApproval, resolveToolApprovalTimeoutMs } from '../tui/session/tool-approval.mjs';
16
16
  import { createToolCardResults } from '../tui/session/tool-card-results.mjs';
17
17
  import { createAgentJobFeed } from '../tui/session/agent-job-feed.mjs';
18
18
  import { createWorktreeSetupNotices } from '../tui/session/worktree-setup-notice.mjs';
@@ -83,10 +83,9 @@ export function setSessionRuntimeImporterForTest(importer) {
83
83
  sessionRuntimeModulePromise = null;
84
84
  }
85
85
 
86
- const TOOL_APPROVAL_TIMEOUT_MS = (() => {
87
- const value = Number(process.env.MIXDOG_TOOL_APPROVAL_TIMEOUT_MS);
88
- return Number.isFinite(value) && value > 0 ? Math.max(1000, Math.round(value)) : 120_000;
89
- })();
86
+ // An approval waits until the user answers (Deny, Stop or a closing runtime
87
+ // settle it); MIXDOG_TOOL_APPROVAL_TIMEOUT_MS opts into a limit for tests/ops.
88
+ const TOOL_APPROVAL_TIMEOUT_MS = resolveToolApprovalTimeoutMs();
90
89
 
91
90
  export {
92
91
  cleanupStaleTranscriptSpillDirs,
@@ -306,15 +305,22 @@ export async function createLocalSessionRuntime({
306
305
  pushItem,
307
306
  replaceItems,
308
307
  });
309
- const { presentNextToolApproval, finishToolApproval, denyAllToolApprovals, requestToolApproval } = createToolApproval(
310
- {
311
- getState,
312
- set,
313
- nextId,
314
- getDisposed: () => flags.disposed,
315
- timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
316
- }
317
- );
308
+ // A sub-agent's approvals also show in the top-level chat (human-gate/gate.mjs).
309
+ const { approvalSurface } = await import('../runtime/agent/orchestrator/session/human-gate/gate.mjs');
310
+ const {
311
+ presentNextToolApproval,
312
+ finishToolApproval,
313
+ denyAllToolApprovals,
314
+ requestToolApproval,
315
+ adoptDelegatedApprovals,
316
+ } = createToolApproval({
317
+ getState,
318
+ set,
319
+ nextId,
320
+ getDisposed: () => flags.disposed,
321
+ timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
322
+ surfaceOf: approvalSurface,
323
+ });
318
324
  const runtimePulse = createRuntimePulse({
319
325
  flags,
320
326
  getState,
@@ -336,6 +342,8 @@ export async function createLocalSessionRuntime({
336
342
  lifecycle.runtimePulseTimer = setInterval(() => {
337
343
  headWatch.retarget();
338
344
  runtimePulse();
345
+ // A sub-agent's approval made before this session loaded (or bound its id).
346
+ adoptDelegatedApprovals();
339
347
  }, 2000);
340
348
  lifecycle.runtimePulseTimer.unref?.();
341
349
 
@@ -148,12 +148,12 @@ function request({
148
148
  }
149
149
  );
150
150
  req.on('timeout', () => {
151
- req.destroy(new Error(`session timeout: ${method} ${urlPath}`));
151
+ req.destroy(sessionTimeoutError(method, urlPath));
152
152
  });
153
153
  req.on('error', reject);
154
154
  if (deadlineMs > 0) {
155
155
  deadlineTimer = setTimeout(() => {
156
- const error = new Error(`session timeout: ${method} ${urlPath}`);
156
+ const error = sessionTimeoutError(method, urlPath);
157
157
  reject(error);
158
158
  req.destroy(error);
159
159
  }, deadlineMs);
@@ -163,6 +163,11 @@ function request({
163
163
  });
164
164
  }
165
165
 
166
+ /** No answer within the request's bound: the daemon may be busy with it, not gone. */
167
+ function sessionTimeoutError(method, urlPath) {
168
+ return Object.assign(new Error(`session timeout: ${method} ${urlPath}`), { code: 'ESESSIONTIMEOUT' });
169
+ }
170
+
166
171
  /** /health within `timeoutMs` of total duration; null when it does not answer. */
167
172
  export async function probeSessionHealth({ port, token, timeoutMs = 800 } = {}) {
168
173
  try {