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
@@ -0,0 +1,99 @@
1
+ // manager/orchestration-mode-switch-notice.mjs
2
+ // A mid-conversation delegation (orchestration) mode switch
3
+ // (session-runtime/tool-policy-refresh.mjs switchSessionOrchestrationMode),
4
+ // on the workflow switch's pattern (workflow-switch-notice.mjs): the request
5
+ // prefix — system blocks and tool schemas — stays byte-identical, so the
6
+ // provider cache survives. The switch lives in two pieces of session state:
7
+ // session.orchestrationModeOverlay — while the session's mode differs from
8
+ // the one its system prompt carries: the prompt's mode (`prompt`), the
9
+ // new mode, the tools the model must load before using them
10
+ // (`loadableTools`) and the self-contained reminder (`content`) with the
11
+ // new mode's rules (the same block BP3 carries). It is folded into the
12
+ // prompt (and dropped) at the next point where the cache is cold anyway.
13
+ // session.orchestrationModeSwitch — the notice the next user turn carries
14
+ // in its reminder block: snapshot when the turn is built, acknowledge once
15
+ // the provider accepted it. `from` is the mode the model was last told
16
+ // about, `to` the session's mode now.
17
+ // Every notice says it replaces the earlier ones, so the latest one wins.
18
+ import { normalizeOrchestrationMode } from '../../../../shared/orchestration.mjs';
19
+ import { orchestrationInstructions } from '../../runtime-core/orchestration.mjs';
20
+
21
+ /** The mode's name as its BP3 block titles it (`# Orchestration Mode: …`). */
22
+ export function orchestrationModeName(value) {
23
+ const mode = normalizeOrchestrationMode(value);
24
+ return mode === 'none' ? 'Solo' : `${mode[0].toUpperCase()}${mode.slice(1)}`;
25
+ }
26
+
27
+ /** The mode the session's system prompt and tool surface were built with. */
28
+ export function promptOrchestrationModeOf(session) {
29
+ if (session?.workflowOverlay) return session.workflowOverlay.promptOrchestrationMode;
30
+ if (session?.orchestrationModeOverlay) return session.orchestrationModeOverlay.prompt;
31
+ return session?.orchestrationMode;
32
+ }
33
+
34
+ const listed = (names) => names.map((name) => `\`${name}\``).join(', ');
35
+
36
+ /** The reminder that makes the Lead follow `mode`: self-contained (the mode's
37
+ * rules) and superseding every earlier one. `carried`: the system prompt
38
+ * carries this mode itself (a switch back, or the overlay folded in). */
39
+ export function orchestrationModeNoticeContent({ mode, carried = false, loadableTools = [] }) {
40
+ const name = orchestrationModeName(mode);
41
+ const lead = carried
42
+ ? `This conversation now uses the ${name} orchestration mode, the one the system prompt carries.`
43
+ : `This conversation now uses the ${name} orchestration mode. The system prompt's Orchestration Mode no longer applies; follow ${name}'s below instead.`;
44
+ const pronoun = loadableTools.length === 1 ? 'it' : 'them';
45
+ return [
46
+ '<system-reminder>',
47
+ '# Orchestration Mode Changed',
48
+ `${lead} This replaces every earlier Orchestration Mode Changed reminder in this conversation.`,
49
+ ...(loadableTools.length
50
+ ? [
51
+ `${listed(loadableTools)}: not among your directly defined tools yet; load ${pronoun} with \`load_tool\` before the first call.`,
52
+ ]
53
+ : []),
54
+ `Agents already started keep running with their own instructions; new delegation follows ${name}.`,
55
+ '',
56
+ orchestrationInstructions(mode),
57
+ '</system-reminder>',
58
+ ].join('\n');
59
+ }
60
+
61
+ /** Stage the notice of a switch from `from` to `to`; a switch back to what the
62
+ * model was last told drops it. Returns the staged state or null. */
63
+ export function stageOrchestrationModeSwitchNotice(session, from, to) {
64
+ const prior = session.orchestrationModeSwitch;
65
+ const told = prior ? prior.from : from;
66
+ if (!to || told === to) {
67
+ delete session.orchestrationModeSwitch;
68
+ return null;
69
+ }
70
+ session.orchestrationModeSwitch = {
71
+ version: 1,
72
+ revision: (Number(prior?.revision) || 0) + 1,
73
+ from: told,
74
+ to,
75
+ };
76
+ return session.orchestrationModeSwitch;
77
+ }
78
+
79
+ /**
80
+ * The orchestration mode notice the next user turn carries, or null.
81
+ * @returns {{ revision: number, content: string } | null}
82
+ */
83
+ export function snapshotPendingOrchestrationModeNotice(session) {
84
+ const pending = session?.orchestrationModeSwitch;
85
+ if (!pending?.to) return null;
86
+ const overlay = session.orchestrationModeOverlay;
87
+ const content =
88
+ overlay?.mode === pending.to && overlay.content
89
+ ? overlay.content
90
+ : orchestrationModeNoticeContent({ mode: pending.to, carried: true });
91
+ return { revision: pending.revision, content };
92
+ }
93
+
94
+ /** The notice of `revision` reached the model: nothing is due any more. */
95
+ export function acknowledgePendingOrchestrationModeNotice(session, revision) {
96
+ if (!session?.orchestrationModeSwitch || session.orchestrationModeSwitch.revision !== Number(revision)) return false;
97
+ delete session.orchestrationModeSwitch;
98
+ return true;
99
+ }
@@ -0,0 +1,42 @@
1
+ // The fallback models a Lead-class session starts with. Agents get theirs from
2
+ // their spawn preset (agent-child.mjs); a Lead-class session gets its OWNER's:
3
+ // - a coding Lead (new task, task a bot created): the Lead route, i.e. the
4
+ // preset of `config.default` (workflow-lead);
5
+ // - an agent-mode Lead (a bot's home chat, a room turn, a routine run, or
6
+ // Mixy's own thread): that bot's own route in its folder (agent.json), so
7
+ // Mixy and the Lead keep separate chains.
8
+ // The chain applies only while the session runs on the owner's configured
9
+ // primary model. A session the user put on another model (composer picker,
10
+ // New Task) is a deliberate choice and gets none. Route changes recompute it
11
+ // (updateSessionRoute `routeFallbacks`); a fallback hop does not.
12
+ import { loadConfig } from '../../config.mjs';
13
+ import { findPreset } from '../../runtime-core/config-helpers.mjs';
14
+ import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
15
+ import { canonicalRouteProvider } from '../../../../shared/model-route-chain.mjs';
16
+ import { storedBotRoute } from '../../../../shared/agent-store.mjs';
17
+ import { configuredAgentRouteCandidates } from '../../../../shared/agent-route-config.mjs';
18
+ import { CODING_MODE } from '../agent-mode.mjs';
19
+
20
+ export function ownerRouteFallbacks({ sessionMode, agentId, provider, model, config = null }) {
21
+ if (!agentId) return [];
22
+ const cfg = () => config || loadConfig({ secrets: false });
23
+ let stored;
24
+ if (sessionMode === CODING_MODE) {
25
+ stored = findPreset(cfg(), cfg()?.default);
26
+ } else {
27
+ // Before the bot split a bot's route is still in the config (Mixy's never).
28
+ stored = storedBotRoute(agentId) || (agentId === 'lead' ? null : configuredAgentRouteCandidates(cfg(), agentId)[0]);
29
+ }
30
+ if (!stored?.fallbacks?.length) return [];
31
+ let route;
32
+ try {
33
+ route = normalizeWorkflowRoute(stored);
34
+ } catch {
35
+ return [];
36
+ }
37
+ const onPrimary =
38
+ route &&
39
+ canonicalRouteProvider(route.provider) === canonicalRouteProvider(provider) &&
40
+ String(route.model) === String(model);
41
+ return onPrimary ? route.fallbacks || [] : [];
42
+ }
@@ -1,5 +1,5 @@
1
1
  // Steering / pending-message queue with sync buffering and atomic persistence.
2
- import { readFileSync } from 'node:fs';
2
+ import { readJsonStateSync } from '../../../../shared/atomic-file.mjs';
3
3
  import { loadSession } from '../store.mjs';
4
4
  import {
5
5
  currentPendingLifecycleToken,
@@ -602,14 +602,8 @@ export function _getPendingMessagesForSession(sessionId) {
602
602
  const queued = [...(_sessionPendingMessages.get(sessionId) || []), ...(_pendingPersistBuffers.get(sessionId) || [])];
603
603
  // The not-yet-migrated global spool can still hold this session's rows.
604
604
  for (const path of [pendingMessagesPath(sessionId), legacyPendingMessagesPath()]) {
605
- let raw;
606
- try {
607
- raw = readFileSync(path, 'utf8');
608
- } catch (err) {
609
- if (err?.code === 'ENOENT') continue;
610
- throw err;
611
- }
612
- const persisted = normalizePendingStore(JSON.parse(raw)).sessions[sessionId];
605
+ // A damaged spool file is kept aside and reads as empty; it never throws.
606
+ const persisted = normalizePendingStore(readJsonStateSync(path, { fallback: null })).sessions[sessionId];
613
607
  if (Array.isArray(persisted)) queued.push(...persisted);
614
608
  }
615
609
  return queued;
@@ -17,7 +17,7 @@ export const _pendingPersistTails = new Map();
17
17
  export { pendingMessagesPath, touchPendingSessionEntry } from './pending-spool-path.mjs';
18
18
 
19
19
  // Single spool transaction shape: every mutation touches only `sessionId`'s
20
- // own shard file, locked, compact and non-fsync; `extra` only ever relaxes the
20
+ // own shard file, locked, compact and durable; `extra` only ever relaxes the
21
21
  // lock timeout.
22
22
  export function updateSpool(sessionId, mutate, extra = null) {
23
23
  return updatePendingShard(sessionId, mutate, extra);
@@ -21,7 +21,8 @@ function storeIsEmpty(store) {
21
21
  );
22
22
  }
23
23
 
24
- // One shard transaction: locked, compact, non-fsync. A shard emptied by the
24
+ // One shard transaction: locked, compact, durable (queued user messages must
25
+ // survive a crash; a damaged shard is kept aside by updateJsonAtomic). A shard emptied by the
25
26
  // mutation (or found empty) is deleted under the same lock, so the directory
26
27
  // holds only sessions that actually have queued rows.
27
28
  async function updateShardFile(key, mutate, extra = null) {
@@ -33,7 +34,6 @@ async function updateShardFile(key, mutate, extra = null) {
33
34
  compact: true,
34
35
  lock: true,
35
36
  mode: PENDING_MESSAGES_MODE,
36
- fsync: false,
37
37
  ...extra,
38
38
  });
39
39
  if (storeIsEmpty(result)) {
@@ -50,7 +50,9 @@ async function updateShardFile(key, mutate, extra = null) {
50
50
  }
51
51
 
52
52
  function rowKey(row) {
53
- return row && typeof row === 'object' && typeof row.id === 'string' && row.id ? `id:${row.id}` : `json:${JSON.stringify(row)}`;
53
+ return row && typeof row === 'object' && typeof row.id === 'string' && row.id
54
+ ? `id:${row.id}`
55
+ : `json:${JSON.stringify(row)}`;
54
56
  }
55
57
 
56
58
  // Idempotent merge of one legacy bucket into its shard: legacy rows (older)
@@ -0,0 +1,50 @@
1
+ // Provider-native replay state on transcript messages: reasoning items,
2
+ // thinking blocks with their signatures, ordered native blocks, Gemini thought
3
+ // signatures on tool calls. It belongs to the provider (and the account) that
4
+ // produced it — another provider rejects it or, worse, a gateway of the same
5
+ // family validates it against its own key. A route change drops it; what the
6
+ // next provider needs (text, tool calls and their results, in order) stays,
7
+ // and each adapter rebuilds its own wire shape from that.
8
+
9
+ // Every message field a provider adapter replays or re-reads as native state.
10
+ const PROVIDER_REPLAY_FIELDS = [
11
+ 'providerReplay',
12
+ 'reasoning',
13
+ 'thinkingBlocks',
14
+ 'assistantBlocks',
15
+ 'reasoningItems',
16
+ 'reasoningContent',
17
+ 'providerMetadata',
18
+ ];
19
+ // Opaque provider tokens a tool call may carry (Gemini thought signatures).
20
+ const TOOL_CALL_SIGNATURE_FIELDS = ['thoughtSignature', 'thought_signature'];
21
+
22
+ const hasAny = (value, fields) => fields.some((field) => field in value);
23
+
24
+ function plainToolCalls(toolCalls) {
25
+ if (!Array.isArray(toolCalls) || !toolCalls.some((call) => call && hasAny(call, TOOL_CALL_SIGNATURE_FIELDS))) {
26
+ return toolCalls;
27
+ }
28
+ return toolCalls.map((call) => {
29
+ if (!call || !hasAny(call, TOOL_CALL_SIGNATURE_FIELDS)) return call;
30
+ const plain = { ...call };
31
+ for (const field of TOOL_CALL_SIGNATURE_FIELDS) delete plain[field];
32
+ return plain;
33
+ });
34
+ }
35
+
36
+ /** `message` without provider-native replay state; the same object when it carries none. */
37
+ export function stripProviderReplayMessage(message) {
38
+ if (!message || typeof message !== 'object') return message;
39
+ const toolCalls = plainToolCalls(message.toolCalls);
40
+ if (!hasAny(message, PROVIDER_REPLAY_FIELDS) && toolCalls === message.toolCalls) return message;
41
+ const plain = { ...message };
42
+ for (const field of PROVIDER_REPLAY_FIELDS) delete plain[field];
43
+ if (toolCalls !== message.toolCalls) plain.toolCalls = toolCalls;
44
+ return plain;
45
+ }
46
+
47
+ /** Replace, never mutate: delta saves see rewritten messages as changed. */
48
+ export function stripProviderReplayMessages(messages) {
49
+ return Array.isArray(messages) ? messages.map(stripProviderReplayMessage) : messages;
50
+ }
@@ -1,9 +1,15 @@
1
1
  // Prompt-layer rules builders. Source revision tracking lives separately so
2
2
  // every layer observes deletions, replacements and timestamp rollbacks alike.
3
+ import { createHash } from 'node:crypto';
3
4
  import { createRequire } from 'node:module';
4
5
  import { join } from 'node:path';
5
6
  import { resolvePluginData, mixdogRoot } from '../../../../shared/plugin-paths.mjs';
6
7
  import { createRulesSourceCache } from './rules-source-cache.mjs';
8
+ import {
9
+ AGENT_MODE_DELEGATION,
10
+ MIXY_CONTACTS_INTRO,
11
+ orchestrationInstructions,
12
+ } from '../../runtime-core/orchestration.mjs';
7
13
 
8
14
  // Phase B: Pool B Tier 2 content builder (common rules only).
9
15
  // Loaded once per process via createRequire so the CJS module reaches us.
@@ -129,7 +135,7 @@ export function _buildLeadRules({ includeLeadBrief = true, part = 'common' } = {
129
135
  );
130
136
  }
131
137
 
132
- // One agent-mode rule file (rules/agent-mode): 'conduct', 'mixy' or 'routine'.
138
+ // One agent-mode rule file (rules/agent-mode): 'conduct' or 'mixy'.
133
139
  export function _buildAgentModeRules({ part = 'conduct' } = {}) {
134
140
  const PLUGIN_ROOT = mixdogRoot();
135
141
  return buildCachedRules(
@@ -141,6 +147,30 @@ export function _buildAgentModeRules({ part = 'conduct' } = {}) {
141
147
  );
142
148
  }
143
149
 
150
+ // --- Rules revision ---
151
+ // The shipped rule texts a bot thread's prompt embeds — the shared tool
152
+ // policy (canonical variant), every LEAD.md part, the agent-mode files and
153
+ // the orchestration/delegation texts — as one hash. A bot thread stamps it
154
+ // at creation; another value (a release changed a rule) rebuilds its prompt
155
+ // once (session-runtime/tool-policy-refresh.mjs). Built from the cached
156
+ // builders, so a check costs a few stats.
157
+ export function promptRulesRevision() {
158
+ const hash = createHash('sha256');
159
+ const texts = [
160
+ _buildSharedRules(),
161
+ ...['common', 'coding', 'agent', 'identity'].flatMap((part) =>
162
+ [true, false].map((includeLeadBrief) => _buildLeadRules({ includeLeadBrief, part }))
163
+ ),
164
+ _buildAgentModeRules({ part: 'conduct' }),
165
+ _buildAgentModeRules({ part: 'mixy' }),
166
+ AGENT_MODE_DELEGATION,
167
+ MIXY_CONTACTS_INTRO,
168
+ ...['none', 'focused', 'balanced', 'swarm'].map(orchestrationInstructions),
169
+ ];
170
+ for (const text of texts) hash.update(`${text}\0`);
171
+ return hash.digest('hex').slice(0, 24);
172
+ }
173
+
144
174
  export function _buildLeadMetaContext({ includeOutputStyle = true } = {}) {
145
175
  const PLUGIN_ROOT = mixdogRoot();
146
176
  const DATA_DIR = resolvePluginData();
@@ -162,6 +192,15 @@ export function _buildLeadMetaContext({ includeOutputStyle = true } = {}) {
162
192
  // Trailing BP3 block: the configured response language. Kept out of BP2 so
163
193
  // it is the last instruction before the conversation (see
164
194
  // rules-builder.cjs buildLeadLanguageContent).
195
+ // The configured response language as {id, name}, or null (a rule's `language`
196
+ // condition compares the id; its text's {language} is the name).
197
+ export function _buildLeadLanguage() {
198
+ const DATA_DIR = resolvePluginData();
199
+ return buildCachedRules('buildLeadLanguage', 'lead language', [join(DATA_DIR, 'mixdog-config.json')], {
200
+ DATA_DIR,
201
+ });
202
+ }
203
+
165
204
  export function _buildLeadLanguageContext() {
166
205
  const DATA_DIR = resolvePluginData();
167
206
  return buildCachedRules('buildLeadLanguageContent', 'lead language context', [join(DATA_DIR, 'mixdog-config.json')], {
@@ -298,6 +298,16 @@ export function markSessionToolOutputTail(id, tail) {
298
298
  entry.toolOutputTail = text ? text.slice(-4000) : null;
299
299
  entry.toolOutputTailAt = Date.now();
300
300
  }
301
+ // An approval has no wait limit, and the progress watchdog treats a turn with
302
+ // `awaitingUser` as waiting on the user, not stalled. The user's answer is
303
+ // progress: a wait begins and ends with a fresh progress stamp, so the hours
304
+ // spent waiting are never counted as silence once the turn runs again.
305
+ function _stampApprovalProgress(entry) {
306
+ if (entry.closed || entry.controller?.signal?.aborted || TERMINAL_STAGES.has(entry.stage)) return;
307
+ const now = Date.now();
308
+ entry.lastProgressAt = now;
309
+ entry.updatedAt = now;
310
+ }
301
311
  // A turn blocked on the user: a PreToolUse `ask` waiting for the tool
302
312
  // approval only the user can give. Published as `awaitingUser: 'approval'` so
303
313
  // status surfaces stop presenting the session as working. Counted, because one
@@ -309,6 +319,7 @@ export function beginSessionApprovalWait(id) {
309
319
  if (!entry) return () => {};
310
320
  entry.approvalWaits = (entry.approvalWaits || 0) + 1;
311
321
  entry.awaitingUser = 'approval';
322
+ _stampApprovalProgress(entry);
312
323
  // The waiting turn: the end transition carries the begin's turn even when
313
324
  // the turn has settled (askStartedAt cleared) by the time it releases.
314
325
  const meta = { turnAt: entry.askStartedAt || 0 };
@@ -321,6 +332,7 @@ export function beginSessionApprovalWait(id) {
321
332
  entry.approvalWaits = Math.max(0, (entry.approvalWaits || 0) - 1);
322
333
  if (entry.approvalWaits > 0) return;
323
334
  entry.awaitingUser = null;
335
+ _stampApprovalProgress(entry);
324
336
  emitSessionApprovalWaitChange(id, null, meta);
325
337
  };
326
338
  }
@@ -19,6 +19,25 @@ export function isTurnInFlight(entry) {
19
19
  return IN_FLIGHT_STAGES.has(entry.stage);
20
20
  }
21
21
 
22
+ // Sessions whose turn was handed to the runtime but has not claimed its
23
+ // controller yet (spawn dispatched, askSession still loading the session).
24
+ // The row already declares work; without this the pool cannot tell that
25
+ // first-turn gap from a worker it lost track of, and reads `unknown`.
26
+ const dispatchedTurns = new Map();
27
+
28
+ /** Runs `run` (the turn's dispatch) with `id` counted as working until it
29
+ * settles, covering the gap before the turn's own runtime entry is in flight. */
30
+ export async function whileTurnDispatched(id, run) {
31
+ dispatchedTurns.set(id, (dispatchedTurns.get(id) || 0) + 1);
32
+ try {
33
+ return await run();
34
+ } finally {
35
+ const left = dispatchedTurns.get(id) - 1;
36
+ if (left > 0) dispatchedTurns.set(id, left);
37
+ else dispatchedTurns.delete(id);
38
+ }
39
+ }
40
+
22
41
  const cleanLower = (value) =>
23
42
  String(value || '')
24
43
  .trim()
@@ -32,6 +51,7 @@ const cleanLower = (value) =>
32
51
  export function inFlightSessionActivity() {
33
52
  const working = new Set();
34
53
  const agentWorking = new Set();
54
+ for (const id of dispatchedTurns.keys()) working.add(id);
35
55
  for (const [id, entry] of runtimeState) {
36
56
  if (!isTurnInFlight(entry)) continue;
37
57
  working.add(id);
@@ -15,10 +15,12 @@ import { loadConfig } from '../../config.mjs';
15
15
  import {
16
16
  buildProviderCacheOpts,
17
17
  cacheCapabilityForProvider,
18
+ isLeadCacheClass,
18
19
  roleProviderCacheOpts,
19
20
  } from '../../agent-runtime/cache-strategy.mjs';
20
21
  import { normalizeAutoClearConfig, resolveAutoClearIdleMs } from '../../runtime-core/config-helpers.mjs';
21
- import { _buildBaseRules } from './rules-cache.mjs';
22
+ import { _buildBaseRules, promptRulesRevision } from './rules-cache.mjs';
23
+ import { stripProviderReplayMessages } from './provider-replay.mjs';
22
24
  import { composeSessionSystem, seedSessionMessages } from './session-prompt-composition.mjs';
23
25
  import { agentProfileRevision, leadRosterRevision } from '../../context/role-instructions.mjs';
24
26
  import {
@@ -40,6 +42,7 @@ import { ensureCodexWireSessionId, mintSessionId } from './session-id.mjs';
40
42
  import { providerCacheKey } from './provider-cache-key.mjs';
41
43
  import { buildSessionRecord, normalizeDesktopSessionMetadata, sessionOriginFields } from './session-record.mjs';
42
44
  import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
45
+ import { ownerRouteFallbacks } from './owner-route-fallbacks.mjs';
43
46
  import { applyPendingWorkspaceReroot, refreshSessionBp3Environment } from './prompt-utils.mjs';
44
47
  import { isWorktreeBound } from '../../../../shared/worktrees/contract.mjs';
45
48
  import { applyAgentToolExposure } from '../loop/tool-exposure-migration.mjs';
@@ -52,17 +55,20 @@ export { recoverSessionAfterProcessRestart } from './turn-checkpoint-recovery.mj
52
55
  // re-exported so prior importers of this module stay unchanged.
53
56
  export { prefetchSession, prepareSessionProjection } from './prepared-resume-cache.mjs';
54
57
 
55
- function buildSessionProviderCacheOpts(providerName, sessionId, agent = null, config = null) {
58
+ // `session` is the record or create options; it classifies agent-mode Leads
59
+ // (bot home threads, routine runs) into the Lead cache class.
60
+ function buildSessionProviderCacheOpts(providerName, sessionId, agent = null, config = null, session = null) {
56
61
  // Keep this in sync with createSession's provider-cache policy: only
57
- // explicit-breakpoint providers get BP cache opts here; OpenAI/key-prefix
58
- // providers use promptCacheKey and request-time strategy instead, plus the
59
- // shared prompt-cache scope of a one-shot role.
62
+ // explicit-breakpoint providers get BP cache opts here; key-prefix
63
+ // providers use promptCacheKey and request-time strategy instead (plus the
64
+ // OpenAI API's Lead-class 12h prefix TTL and a one-shot role's shared
65
+ // prompt-cache scope).
60
66
  if (cacheCapabilityForProvider(providerName) !== 'explicit-breakpoint') {
61
- return roleProviderCacheOpts(providerName, agent);
67
+ return roleProviderCacheOpts(providerName, agent, { session });
62
68
  }
63
69
  try {
64
70
  let autoClear = null;
65
- if (!agent || agent === 'lead') {
71
+ if (isLeadCacheClass(agent, session)) {
66
72
  const loadedConfig = config || loadConfig({ secrets: false });
67
73
  const normalizedAutoClear = normalizeAutoClearConfig(loadedConfig?.autoClear);
68
74
  autoClear = {
@@ -70,20 +76,23 @@ function buildSessionProviderCacheOpts(providerName, sessionId, agent = null, co
70
76
  idleMs: resolveAutoClearIdleMs(loadedConfig, providerName),
71
77
  };
72
78
  }
73
- return buildProviderCacheOpts(providerName, sessionId, agent, { autoClear });
79
+ return buildProviderCacheOpts(providerName, sessionId, agent, { autoClear, session });
74
80
  } catch {
75
81
  return null;
76
82
  }
77
83
  }
78
84
 
79
- // Re-derive a Lead session's builder-made cache policy (messages TTL follows
80
- // the auto-clear idle window). Explicit caller/profile policies, non-Lead
81
- // sessions and non-explicit-breakpoint providers are left untouched.
85
+ // Re-derive a Lead-class session's builder-made cache policy (Anthropic
86
+ // messages TTL follows the auto-clear idle window; the OpenAI API prefix TTL
87
+ // follows the cache class). Explicit caller/profile policies, sub-agents and
88
+ // other providers are left untouched.
82
89
  export function refreshSessionProviderCacheOpts(session, config = null) {
83
90
  if (!session || session.providerCacheOptsOverride === true) return false;
84
- if (session.agent && session.agent !== 'lead') return false;
85
- if (cacheCapabilityForProvider(session.provider) !== 'explicit-breakpoint') return false;
86
- const next = buildSessionProviderCacheOpts(session.provider, session.id, session.agent, config);
91
+ if (!isLeadCacheClass(session.agent, session)) return false;
92
+ if (cacheCapabilityForProvider(session.provider) !== 'explicit-breakpoint' && session.provider !== 'openai') {
93
+ return false;
94
+ }
95
+ const next = buildSessionProviderCacheOpts(session.provider, session.id, session.agent, config, session);
87
96
  if (!next) return false;
88
97
  session.providerCacheOpts = next;
89
98
  return true;
@@ -224,24 +233,35 @@ export function createSession(createOpts) {
224
233
  sessionMode === AGENT_MODE && !createOpts.agent && actingAgent
225
234
  ? { ...createOpts, agent: actingAgent, sessionMode }
226
235
  : { ...createOpts, sessionMode };
227
- const routeFallbacks = normalizeWorkflowRoute({
228
- provider: providerName,
229
- model: modelName,
230
- fallbacks: Object.hasOwn(opts, 'routeFallbacks') ? opts.routeFallbacks : presetObj?.fallbacks,
231
- })?.fallbacks;
236
+ // A Lead-class session (actingAgent set) takes its owner's chain while it
237
+ // runs on the owner's primary model; an agent keeps its preset's.
238
+ const routeFallbacks = actingAgent
239
+ ? Object.hasOwn(opts, 'routeFallbacks')
240
+ ? opts.routeFallbacks
241
+ : ownerRouteFallbacks({ sessionMode, agentId: actingAgent, provider: providerName, model: modelName })
242
+ : normalizeWorkflowRoute({
243
+ provider: providerName,
244
+ model: modelName,
245
+ fallbacks: Object.hasOwn(opts, 'routeFallbacks') ? opts.routeFallbacks : presetObj?.fallbacks,
246
+ })?.fallbacks;
232
247
  // Provider cache strategy — agentRuntime.resolveSync() above is a
233
248
  // best-effort injection point (setAgentRuntime() has no live caller
234
249
  // today, so that branch never fires); build it directly here so every
235
- // session still gets a cache strategy. Lead sessions (opts.agent ===
236
- // 'lead', or no agent at all — raw/CLI callers) get their BP4 message
237
- // tail TTL linked to the user's autoClear idle-sweep config; hidden and
238
- // public agents keep the flat 5m default (see cache-strategy.mjs docs).
239
- // Scoped to explicit-breakpoint (Anthropic-family) providers only.
240
- const providerCacheOpts = resolved.providerCacheOpts || buildSessionProviderCacheOpts(providerName, id, opts.agent);
250
+ // session still gets a cache strategy. Lead-class sessions (the Lead, raw
251
+ // callers, bot home threads and other agent-mode Leads) get their BP4
252
+ // message tail TTL linked to the user's autoClear idle-sweep config and,
253
+ // on the OpenAI API, the 12h stable-prefix TTL; sub-agents and hidden roles
254
+ // keep the agent defaults (see cache-strategy.mjs docs).
255
+ const providerCacheOpts =
256
+ resolved.providerCacheOpts || buildSessionProviderCacheOpts(providerName, id, opts.agent, null, { ...opts, id });
241
257
  const surface = resolveSessionToolSurface(opts, { profile, toolPreset, modelName });
242
258
  // The agent profile the prompt is composed from, read first: an edit landing
243
259
  // during composition leaves the stamp behind, never ahead (refreshAgentProfile).
244
- const agentProfile = surface.ownerIsAgent ? '' : agentProfileRevision(surface.resolvedAgent);
260
+ const agentProfile = surface.ownerIsAgent
261
+ ? ''
262
+ : agentProfileRevision(surface.resolvedAgent, { mixy: sessionMode === AGENT_MODE });
263
+ // A bot thread lives on across releases: the rules it was built from.
264
+ const rulesRevision = agentProfile && sessionMode === AGENT_MODE ? promptRulesRevision() : '';
245
265
  const prompt = composeSessionSystem(opts, { profile, providerName, modelName, surface, sessionMode });
246
266
  const messages = seedSessionMessages(prompt, opts.files);
247
267
  logSessionSurface(surface);
@@ -265,6 +285,7 @@ export function createSession(createOpts) {
265
285
  session.agentProfileRevision = agentProfile;
266
286
  // The bot roster its prompt lists (a Lead's catalog slices).
267
287
  session.agentRosterRevision = leadRosterRevision(opts.leadWorkflowParts);
288
+ if (rulesRevision) session.rulesRevision = rulesRevision;
268
289
  }
269
290
  if (routeFallbacks?.length) session.routeFallbacks = routeFallbacks;
270
291
  // A non-native agent sends core + bridge, built from its filtered tools.
@@ -337,6 +358,13 @@ function applyRouteFields(session, route) {
337
358
  else delete session.autoEffort;
338
359
  }
339
360
  if (Object.hasOwn(route, 'effort')) session.effort = route.effort || null;
361
+ // A deliberate route choice restarts the chain (a fallback hop never passes this).
362
+ if (Object.hasOwn(route, 'routeFallbacks')) {
363
+ if (route.routeFallbacks?.length) session.routeFallbacks = route.routeFallbacks;
364
+ else delete session.routeFallbacks;
365
+ delete session.routeFallbackIndex;
366
+ delete session.routeFallbackFailures;
367
+ }
340
368
  ensureCodexWireSessionId(session);
341
369
  if (Object.hasOwn(route, 'modelParameters')) {
342
370
  session.modelParameters =
@@ -384,7 +412,8 @@ function applyRouteContextWindow(session, routeChanged, selectedContextWindowPro
384
412
  // surface: the previous route's readings and prepared tools no longer apply.
385
413
  function resetSessionForRouteChange(id, session, previousModel, previousProvider) {
386
414
  session.promptCacheKey = providerCacheKey(session.provider);
387
- session.providerCacheOpts = buildSessionProviderCacheOpts(session.provider, session.id, session.agent) || null;
415
+ session.providerCacheOpts =
416
+ buildSessionProviderCacheOpts(session.provider, session.id, session.agent, null, session) || null;
388
417
  session.providerCacheOptsOverride = false;
389
418
  session.lastInputTokens = 0;
390
419
  session.lastOutputTokens = 0;
@@ -421,27 +450,11 @@ function resetSessionForRouteChange(id, session, previousModel, previousProvider
421
450
  _forgetPreparedResume(id);
422
451
  }
423
452
 
424
- // Every message field a provider adapter replays or re-reads as native state.
425
- const PROVIDER_REPLAY_FIELDS = [
426
- 'providerReplay',
427
- 'reasoning',
428
- 'thinkingBlocks',
429
- 'assistantBlocks',
430
- 'reasoningItems',
431
- 'reasoningContent',
432
- 'providerMetadata',
433
- ];
434
-
435
- // Provider-native replay (reasoning items, thinking blocks) belongs to the
436
- // provider that produced it. Replace, never mutate, so delta saves rewrite.
453
+ // Provider-native replay (reasoning items, thinking blocks, signatures)
454
+ // belongs to the provider that produced it (see provider-replay.mjs).
437
455
  function stripProviderReplay(session) {
438
456
  if (!Array.isArray(session.messages)) return;
439
- session.messages = session.messages.map((message) => {
440
- if (!message || !PROVIDER_REPLAY_FIELDS.some((field) => field in message)) return message;
441
- const plain = { ...message };
442
- for (const field of PROVIDER_REPLAY_FIELDS) delete plain[field];
443
- return plain;
444
- });
457
+ session.messages = stripProviderReplayMessages(session.messages);
445
458
  }
446
459
 
447
460
  // `options.crossProvider` is the explicit in-place continuation of an
@@ -450,7 +463,9 @@ function stripProviderReplay(session) {
450
463
  // auto-compacts) against the new model's window.
451
464
  export function updateSessionRoute(id, route = {}, options = {}) {
452
465
  if (!id) return null;
453
- const session = loadSession(id);
466
+ // options.session: the live object of a running turn, changed in place; the id's
467
+ // stored copy (a disk reload after another process took the generation) is not.
468
+ const session = options.session?.id === id ? options.session : loadSession(id);
454
469
  if (!session || session.closed === true) return null;
455
470
  const previousProvider = session.provider || null;
456
471
  const previousModel = session.model || null;
@@ -5,7 +5,7 @@
5
5
  import { buildSkillManifest, composeSystemPrompt } from '../../context/collect.mjs';
6
6
  import { configuredInstructionFilesMode, loadInstructionFilesContext } from '../../context/instruction-files.mjs';
7
7
  import { agentModeIdentityLine } from '../../context/role-instructions.mjs';
8
- import { AGENT_MODE, isRoutineSource, sessionModeOf } from '../agent-mode.mjs';
8
+ import { AGENT_MODE, sessionModeOf } from '../agent-mode.mjs';
9
9
  import { MIXY_AGENT_ID } from '../agent-home.mjs';
10
10
  import {
11
11
  _buildBaseRules,
@@ -98,12 +98,9 @@ export function buildWorkspacePromptContext(session, cwd) {
98
98
  // A Lead's BP3 rule slices, ordered for prefix sharing (composeSystemPrompt):
99
99
  // LEAD.md's common lines first; then the mode block (coding: LEAD.md's two
100
100
  // passages; agent mode: their conversational variants and AGENT_MODE.md);
101
- // then Mixy's MIXY.md; last the per-session tail — routine line, identity.
101
+ // then Mixy's MIXY.md; last the per-session tail — identity.
102
102
  // Agent mode is a Lead created as an agent (agent-mode.mjs).
103
- export function sessionRolePromptFields(
104
- identity,
105
- { ownerIsAgent, toolFreeRole, delegationFree, sessionMode, routine = false }
106
- ) {
103
+ export function sessionRolePromptFields(identity, { ownerIsAgent, toolFreeRole, delegationFree, sessionMode }) {
107
104
  const skipRules = identity.skipAgentRules || toolFreeRole;
108
105
  const lead = !ownerIsAgent && !skipRules;
109
106
  const agentMode = lead && sessionMode === AGENT_MODE;
@@ -131,7 +128,6 @@ export function sessionRolePromptFields(
131
128
  ? {
132
129
  agentModeRules: _buildAgentModeRules({ part: 'conduct' }),
133
130
  ...(mixy ? { mixyRules: _buildAgentModeRules({ part: 'mixy' }) } : {}),
134
- ...(routine ? { routineRules: _buildAgentModeRules({ part: 'routine' }) } : {}),
135
131
  }
136
132
  : {}),
137
133
  userPrompt: identity.systemPrompt,
@@ -139,6 +135,9 @@ export function sessionRolePromptFields(
139
135
  // session running as that agent) carries its own AGENT.md role, and Mixy
140
136
  // in agent mode its optional agents/lead/AGENT.md.
141
137
  skipRoleCatalog: !ownerIsAgent && (!identity.agent || identity.agent === 'lead') && !(agentMode && mixy),
138
+ // Mixy's persona (bots/mixy/PERSONA.md) is Mixy's alone: keyed on Mixy's
139
+ // own agent-mode thread, never on the `lead` role a coding Lead shares.
140
+ skipPersona: !ownerIsAgent && (!identity.agent || identity.agent === 'lead') && !(agentMode && mixy),
142
141
  agent: identity.agent,
143
142
  profile: { skip: identity.profileSkip },
144
143
  };
@@ -173,7 +172,6 @@ export function composeSessionSystem(opts, { profile, providerName, modelName, s
173
172
  toolFreeRole,
174
173
  delegationFree,
175
174
  sessionMode,
176
- routine: isRoutineSource(opts.sourceType),
177
175
  });
178
176
  const leadOnly = !skipAgentRules && !ownerIsAgent;
179
177
  const metaContext = leadOnly ? _buildLeadMetaContext({ includeOutputStyle: !agentMode }) : '';