mixdog 1.3.1 → 1.3.3

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 (468) hide show
  1. package/README.md +8 -4
  2. package/package.json +8 -8
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/scripts/verify-runtime-dependency-versions.mjs +158 -0
  5. package/src/agents/reviewer/AGENT.md +5 -7
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +9 -1
  8. package/src/defaults/skills/browser-use/SKILL.md +6 -3
  9. package/src/defaults/skills/local-provider/SKILL.md +33 -6
  10. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  11. package/src/defaults/skills/setup/SKILL.md +23 -1
  12. package/src/defaults/skills/setup/references/actions.md +50 -3
  13. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  14. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  15. package/src/defaults/skills/visualize/SKILL.md +9 -6
  16. package/src/defaults/skills/visualize/references/craft.md +8 -1
  17. package/src/help.mjs +1 -1
  18. package/src/lib/keychain-cjs.cjs +4 -1
  19. package/src/lib/rules-builder.cjs +14 -7
  20. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  21. package/src/rules/agent-mode/MIXY.md +6 -8
  22. package/src/rules/lead/LEAD.md +7 -10
  23. package/src/rules/shared/10-tool-workflow.md +4 -0
  24. package/src/rules/shared/20-research.md +2 -0
  25. package/src/rules/shared/45-destructive.md +5 -1
  26. package/src/rules/shared/60-verification.md +6 -4
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  29. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  30. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  31. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  32. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  33. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  35. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  37. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  38. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  39. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  40. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  41. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
  45. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  46. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
  48. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
  50. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  51. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  52. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  54. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  55. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
  56. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  57. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  59. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  60. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  62. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  64. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  65. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  69. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  70. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  72. package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
  73. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
  74. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  75. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
  78. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  79. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
  81. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  82. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  83. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
  85. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
  86. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  87. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
  90. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  92. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  93. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  94. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
  95. package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
  96. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  97. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
  99. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
  100. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  101. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
  102. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
  103. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
  107. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
  108. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  109. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  110. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
  111. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
  112. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  113. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
  114. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  115. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  116. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  117. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  118. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
  119. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
  120. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
  121. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
  123. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
  124. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  125. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  126. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
  127. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  128. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
  129. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
  130. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
  131. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  132. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  134. package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
  135. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
  136. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
  137. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  138. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
  139. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  143. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
  144. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
  145. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  146. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  148. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  150. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
  151. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
  155. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  156. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
  157. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
  158. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  160. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  161. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
  163. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
  166. package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
  167. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
  168. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  169. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  170. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  171. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  172. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
  178. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
  179. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  180. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  181. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
  182. package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
  183. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
  184. package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
  185. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  186. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  189. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  190. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  196. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
  200. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  201. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  202. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  203. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  204. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
  206. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  207. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  208. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  209. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  210. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  211. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  212. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  213. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  214. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  215. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  216. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  217. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  218. package/src/runtime/attachments/legacy-office.mjs +110 -0
  219. package/src/runtime/attachments/limits.mjs +5 -1
  220. package/src/runtime/attachments/office-extract.mjs +3 -6
  221. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  222. package/src/runtime/attachments/prompt-restore.mjs +70 -0
  223. package/src/runtime/attachments/store.mjs +109 -33
  224. package/src/runtime/browser-bridge/client.mjs +73 -20
  225. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  226. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  227. package/src/runtime/channels/lib/state-file.mjs +15 -15
  228. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  230. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  231. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  232. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  233. package/src/runtime/effort-judge/judge-client.mjs +66 -16
  234. package/src/runtime/effort-judge/model-install.mjs +47 -7
  235. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  236. package/src/runtime/local-provider/catalog.mjs +5 -2
  237. package/src/runtime/local-provider/registered-models.mjs +64 -27
  238. package/src/runtime/local-provider/server.mjs +2 -3
  239. package/src/runtime/media/adapters/image-api.mjs +89 -0
  240. package/src/runtime/media/auth.mjs +64 -1
  241. package/src/runtime/media/catalog.mjs +66 -4
  242. package/src/runtime/media/jobs.mjs +4 -0
  243. package/src/runtime/media/lanes.mjs +46 -5
  244. package/src/runtime/media/media-usage.mjs +17 -0
  245. package/src/runtime/media/renditions.mjs +15 -15
  246. package/src/runtime/media/store.mjs +36 -35
  247. package/src/runtime/media/tool.mjs +8 -1
  248. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  249. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  250. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  251. package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
  252. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  253. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  254. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  255. package/src/runtime/office/core/journal.mjs +7 -14
  256. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  257. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  258. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  259. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  260. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  261. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  262. package/src/runtime/office/portable/portable-soffice.mjs +135 -29
  263. package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
  264. package/src/runtime/shared/agent-avatar.mjs +3 -2
  265. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  266. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  267. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  268. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  269. package/src/runtime/shared/agent-room-host.d.mts +31 -0
  270. package/src/runtime/shared/agent-room-host.mjs +58 -0
  271. package/src/runtime/shared/agent-store.mjs +24 -7
  272. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  273. package/src/runtime/shared/atomic-file.d.mts +50 -0
  274. package/src/runtime/shared/atomic-file.mjs +394 -132
  275. package/src/runtime/shared/background-tasks.mjs +7 -4
  276. package/src/runtime/shared/bot-hops.mjs +35 -0
  277. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  278. package/src/runtime/shared/bounded-download.mjs +4 -2
  279. package/src/runtime/shared/config.mjs +39 -9
  280. package/src/runtime/shared/defaults-separation.mjs +6 -2
  281. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  282. package/src/runtime/shared/err-text.mjs +4 -0
  283. package/src/runtime/shared/error-presentation.mjs +22 -4
  284. package/src/runtime/shared/file-has-data.mjs +28 -0
  285. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  286. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  287. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  288. package/src/runtime/shared/projects.mjs +50 -41
  289. package/src/runtime/shared/provider-accounts.mjs +17 -12
  290. package/src/runtime/shared/redact-directories.mjs +18 -0
  291. package/src/runtime/shared/remove-directory.mjs +28 -0
  292. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  293. package/src/runtime/shared/routine-trigger.mjs +14 -6
  294. package/src/runtime/shared/run-child.d.mts +3 -0
  295. package/src/runtime/shared/run-child.mjs +24 -3
  296. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  297. package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
  298. package/src/runtime/shared/session-sidebar-host.mjs +16 -3
  299. package/src/runtime/shared/singleton-owner.mjs +7 -6
  300. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  301. package/src/runtime/shared/staged-update.mjs +4 -2
  302. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  303. package/src/runtime/shared/tool-execution-contract.mjs +10 -0
  304. package/src/runtime/shared/tool-work-units.mjs +12 -9
  305. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  306. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  307. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  308. package/src/runtime/shared/turn-snapshot.mjs +277 -124
  309. package/src/runtime/shared/user-cwd.mjs +9 -4
  310. package/src/runtime/shared/user-data-guard.mjs +92 -61
  311. package/src/runtime/shared/utf8-cut.mjs +10 -0
  312. package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
  313. package/src/runtime/shared/worktrees/contract.mjs +11 -3
  314. package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
  315. package/src/runtime/shared/worktrees/manager.mjs +419 -52
  316. package/src/runtime/shared/worktrees/registry.mjs +29 -27
  317. package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
  318. package/src/runtime/tidy/core-install.mjs +26 -4
  319. package/src/runtime/tidy/install.mjs +91 -23
  320. package/src/runtime/tidy/process.mjs +41 -8
  321. package/src/runtime/tidy/resolve.mjs +10 -7
  322. package/src/runtime/web-search/lib/config.mjs +4 -16
  323. package/src/session-runtime/agent-home-delivery.mjs +542 -204
  324. package/src/session-runtime/agent-profile-changes.mjs +16 -0
  325. package/src/session-runtime/agent-room-delivery.mjs +81 -35
  326. package/src/session-runtime/agent-task-sessions.mjs +811 -179
  327. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  328. package/src/session-runtime/boot/apis.mjs +48 -24
  329. package/src/session-runtime/boot/config.mjs +1 -0
  330. package/src/session-runtime/boot/facade.mjs +16 -7
  331. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  332. package/src/session-runtime/boot/providers.mjs +2 -2
  333. package/src/session-runtime/boot/shared.mjs +12 -6
  334. package/src/session-runtime/boot/tools.mjs +23 -3
  335. package/src/session-runtime/bot-status.mjs +432 -0
  336. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  337. package/src/session-runtime/config-lifecycle.mjs +60 -55
  338. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  339. package/src/session-runtime/held-requests.mjs +31 -33
  340. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  341. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  342. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
  343. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  344. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  345. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  346. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  347. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  348. package/src/session-runtime/media-api.mjs +5 -2
  349. package/src/session-runtime/model-recency.mjs +1 -0
  350. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  351. package/src/session-runtime/model-route/set-route.mjs +61 -1
  352. package/src/session-runtime/owned-automation-run.mjs +8 -1
  353. package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
  354. package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
  355. package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
  356. package/src/session-runtime/route-resolution.mjs +16 -8
  357. package/src/session-runtime/route-state.mjs +1 -59
  358. package/src/session-runtime/routine-home-turn.mjs +5 -3
  359. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  360. package/src/session-runtime/runtime-paths.mjs +11 -5
  361. package/src/session-runtime/runtime-review-api.mjs +4 -1
  362. package/src/session-runtime/runtime-tunables.mjs +6 -0
  363. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  364. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  365. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  366. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  367. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
  368. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  369. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  370. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
  371. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  372. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  373. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  374. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  375. package/src/session-runtime/services/agent-tool.mjs +6 -0
  376. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  377. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  378. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  379. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  380. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  381. package/src/session-runtime/services/provider-admin.mjs +26 -6
  382. package/src/session-runtime/services/schedule-access.mjs +33 -4
  383. package/src/session-runtime/services/seeds.mjs +4 -3
  384. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  385. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  386. package/src/session-runtime/services/worktree-access.mjs +27 -0
  387. package/src/session-runtime/session-create/materialize.mjs +2 -3
  388. package/src/session-runtime/session-create/session-options.mjs +15 -2
  389. package/src/session-runtime/session-hooks.mjs +9 -17
  390. package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
  391. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  392. package/src/session-runtime/setup-tool/executor.mjs +12 -2
  393. package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
  394. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  395. package/src/session-runtime/skills-api.mjs +12 -5
  396. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
  397. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  398. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  399. package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
  400. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  401. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  402. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  403. package/src/session-runtime/tool-policy-refresh.mjs +48 -369
  404. package/src/session-runtime/tool-surface.mjs +10 -4
  405. package/src/session-runtime/turn/session-ops.mjs +8 -0
  406. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  407. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  408. package/src/session-runtime/turn/turn-run.mjs +7 -2
  409. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  410. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
  411. package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
  412. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  413. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  414. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  415. package/src/session-runtime/workspace/transition-steps.mjs +1 -18
  416. package/src/session-runtime/workspace/transitions.mjs +279 -67
  417. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  418. package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
  419. package/src/standalone/daemon-agent-control.mjs +4 -1
  420. package/src/standalone/daemon-lag-profiler.mjs +3 -1
  421. package/src/standalone/daemon-session-control.mjs +173 -15
  422. package/src/standalone/daemon-telemetry.mjs +9 -0
  423. package/src/standalone/daemon.mjs +33 -10
  424. package/src/standalone/local-session-runtime.mjs +33 -16
  425. package/src/standalone/session-client.mjs +7 -2
  426. package/src/standalone/session-protocol.mjs +8 -0
  427. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
  428. package/src/standalone/session-service/agent-route-follow.mjs +69 -0
  429. package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
  430. package/src/standalone/session-service.mjs +21 -0
  431. package/src/standalone/session-state-projection.mjs +19 -9
  432. package/src/standalone/session-tool-output.mjs +36 -9
  433. package/src/standalone/staged-import.mjs +47 -0
  434. package/src/tui/App.jsx +2 -0
  435. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  436. package/src/tui/app/message-selector.mjs +15 -2
  437. package/src/tui/app/model-options.mjs +16 -2
  438. package/src/tui/app/model-picker.mjs +2 -1
  439. package/src/tui/app/route-pickers.mjs +3 -1
  440. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  441. package/src/tui/app/slash-commands.mjs +1 -1
  442. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  443. package/src/tui/dist/index.mjs +617 -369
  444. package/src/tui/paste-attachments.mjs +0 -1
  445. package/src/tui/paste-text-policy.mjs +4 -5
  446. package/src/tui/prompt-history-store.mjs +16 -21
  447. package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
  448. package/src/tui/session/context-state.mjs +4 -0
  449. package/src/tui/session/notice-surface.mjs +3 -0
  450. package/src/tui/session/session-api/intake/abort.mjs +52 -18
  451. package/src/tui/session/session-api/intake/rewind.mjs +39 -4
  452. package/src/tui/session/session-api/media.mjs +2 -1
  453. package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
  454. package/src/tui/session/session-api/routes-write.mjs +203 -0
  455. package/src/tui/session/session-api/routes.mjs +23 -247
  456. package/src/tui/session/session-api/settings.mjs +14 -1
  457. package/src/tui/session/session-flow/drain.mjs +28 -3
  458. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  459. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  460. package/src/tui/session/session-flow/steering.mjs +8 -3
  461. package/src/tui/session/tool-approval.mjs +142 -14
  462. package/src/tui/session/transcript-intake.mjs +2 -0
  463. package/src/tui/session/turn.mjs +23 -14
  464. package/src/tui/transcript-tool-failures.mjs +8 -2
  465. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  466. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  467. package/src/rules/agent-mode/ROUTINE.md +0 -6
  468. package/src/session-runtime/room-member-gate.mjs +0 -48
@@ -20,6 +20,8 @@ import {
20
20
  snapshotPendingSetupNotice,
21
21
  } from './prompt-utils.mjs';
22
22
  import { snapshotPendingWorkflowNotice } from './workflow-switch-notice.mjs';
23
+ import { snapshotPendingOrchestrationModeNotice } from './orchestration-mode-switch-notice.mjs';
24
+ import { buildAgentListingNotice } from './agent-listing-notice.mjs';
23
25
  import { codexWireSendOpts } from './session-id.mjs';
24
26
  import { _tryBridgeExplicitPrefetch } from './prefetch-bridge.mjs';
25
27
  import { filterModelVisibleSessionMessages } from './message-sanitize.mjs';
@@ -149,6 +151,7 @@ export async function buildUserTurn({
149
151
  transcriptMeta,
150
152
  turnPromptSource,
151
153
  effortConfiguration,
154
+ currentAgents,
152
155
  }) {
153
156
  const explicitPrefetchResult = await _tryBridgeExplicitPrefetch(session, explicitPrefetch, turnSignal);
154
157
  let contextBlock = '';
@@ -184,8 +187,15 @@ export async function buildUserTurn({
184
187
  // A mid-conversation workflow switch: the prompt and tools were rebuilt
185
188
  // for the new workflow; this turn tells the model once.
186
189
  const workflowNotice = snapshotPendingWorkflowNotice(session);
190
+ // A mid-conversation orchestration mode switch, after the workflow notice
191
+ // so the mode it names wins over the one a workflow reminder carries.
192
+ const orchestrationModeNotice = snapshotPendingOrchestrationModeNotice(session);
193
+ // Agents enabled or switched off since the conversation was last told.
194
+ const agentListingNotice = buildAgentListingNotice(session, currentAgents);
187
195
  const reminderBlock = [
188
196
  workflowNotice?.content || '',
197
+ orchestrationModeNotice?.content || '',
198
+ agentListingNotice?.content || '',
189
199
  workspaceNotice?.content || '',
190
200
  setupNotice?.content || '',
191
201
  currentTimeBlock ? `<system-reminder>\n# Current Time\n${currentTimeBlock}\n</system-reminder>` : '',
@@ -200,6 +210,8 @@ export async function buildUserTurn({
200
210
  ...(transcriptMeta ? { transcript: transcriptMeta } : {}),
201
211
  ...(turnPromptSource || {}),
202
212
  ...(effortConfiguration ? { effortConfiguration } : {}),
213
+ // What its reminder told, read back by later turns (agent-listing-notice.mjs).
214
+ ...(agentListingNotice ? { agentListing: agentListingNotice.meta } : {}),
203
215
  };
204
216
  const userTurnMessage = withRuntimeUserContext(
205
217
  {
@@ -215,6 +227,8 @@ export async function buildUserTurn({
215
227
  deferredToolDelta,
216
228
  goalReminder,
217
229
  workflowNotice,
230
+ orchestrationModeNotice,
231
+ agentListingNotice,
218
232
  workspaceNotice,
219
233
  setupNotice,
220
234
  userTurnMessage,
@@ -10,6 +10,7 @@ import { acknowledgePendingDeferredToolDelta } from '../../runtime-core/deferred
10
10
  import { acknowledgePendingGoalReminder } from '../../runtime-core/goal-reminder.mjs';
11
11
  import { acknowledgePendingSetupNotice, acknowledgePendingWorkspaceNotice } from './prompt-utils.mjs';
12
12
  import { acknowledgePendingWorkflowNotice } from './workflow-switch-notice.mjs';
13
+ import { acknowledgePendingOrchestrationModeNotice } from './orchestration-mode-switch-notice.mjs';
13
14
  import {
14
15
  finalizePendingMessageDelivery,
15
16
  drainPendingMessages,
@@ -141,6 +142,9 @@ export async function commitAskTurn({ sessionId, opened, prepared, result, rawTr
141
142
  acknowledgePendingGoalReminder(session, prepared.goalReminder.revision);
142
143
  }
143
144
  if (prepared.workflowNotice) acknowledgePendingWorkflowNotice(session, prepared.workflowNotice.revision);
145
+ if (prepared.orchestrationModeNotice) {
146
+ acknowledgePendingOrchestrationModeNotice(session, prepared.orchestrationModeNotice.revision);
147
+ }
144
148
  if (prepared.workspaceNotice) {
145
149
  acknowledgePendingWorkspaceNotice(session, prepared.workspaceNotice.revision);
146
150
  }
@@ -59,6 +59,7 @@ export function createAskSteeringDrain({ sessionId, turn, askOpts }) {
59
59
  ids: group.ids,
60
60
  mode: group.mode,
61
61
  ...(group.execution ? { execution: group.execution } : {}),
62
+ ...(group.hop ? { transcriptMeta: { hop: group.hop } } : {}),
62
63
  });
63
64
  }
64
65
  } else {
@@ -110,17 +110,12 @@ function withoutLegacyCompactFields(value) {
110
110
  delete next[key];
111
111
  return next;
112
112
  }
113
- // Handoff-summary timeout scales with transcript size (clear/manual path):
114
- // default max(60s, ~10s per 25k estimated message tokens) capped at 300s.
115
- // The summary may run on a slow reasoning model — the session's own model is
116
- // used whenever the maintenance route is unavailable — where even a small
117
- // transcript outlives the old 30s floor and a large one the old 120s cap.
118
- // session.compaction.timeoutMs still overrides.
119
- function handoffSummaryTimeoutMs(session, messageTokens) {
120
- const override = positiveInt(session?.compaction?.timeoutMs);
121
- if (override) return override;
122
- const scaled = Math.ceil((messageTokens || 0) / 25_000) * 10_000;
123
- return Math.min(HANDOFF_TIMEOUT_MAX_MS, Math.max(60_000, scaled));
113
+ // Handoff-summary timeout (clear/manual path): the same 300s as automatic
114
+ // compaction, whatever the transcript size — the summary may run on a slow
115
+ // reasoning model or a stalled provider connection, and a timeout fails the
116
+ // compaction. session.compaction.timeoutMs still overrides.
117
+ function handoffSummaryTimeoutMs(session) {
118
+ return positiveInt(session?.compaction?.timeoutMs) || HANDOFF_TIMEOUT_MAX_MS;
124
119
  }
125
120
  /**
126
121
  * ONE wiring for the fresh-context handoff pass: provider, summary model,
@@ -144,13 +139,8 @@ export async function runHandoffCompaction({
144
139
  provider = null,
145
140
  model = null,
146
141
  config,
147
- messageTokensEst = null,
148
142
  requireReduction = false,
149
143
  } = {}) {
150
- const messageList = Array.isArray(messages) ? messages : [];
151
- const transcriptTokens = Number.isFinite(Number(messageTokensEst))
152
- ? Number(messageTokensEst)
153
- : estimateMessagesTokens(messageList);
154
144
  return runFreshContextCompact({
155
145
  sessionRef: session,
156
146
  messages,
@@ -159,7 +149,7 @@ export async function runHandoffCompaction({
159
149
  reserveTokens,
160
150
  contextWindow: positiveInt(contextWindow) || positiveInt(session?.contextWindow) || boundaryTokens,
161
151
  boundaryTokens,
162
- handoffTimeoutMs: handoffSummaryTimeoutMs(session, transcriptTokens),
152
+ handoffTimeoutMs: handoffSummaryTimeoutMs(session),
163
153
  },
164
154
  sessionId,
165
155
  signal,
@@ -336,7 +326,6 @@ async function runSessionHandoff(plan, opts) {
336
326
  provider: opts.provider || getProvider(session.provider) || null,
337
327
  model: opts.model,
338
328
  config: opts.config,
339
- messageTokensEst: plan.beforeMessageTokens,
340
329
  requireReduction: plan.requireReduction,
341
330
  });
342
331
  if (Array.isArray(run.freshContextResult?.messages)) {
@@ -609,7 +598,43 @@ function withAppendedMessages(plan, numbers, appended) {
609
598
  const TRANSCRIPT_MOVED_ERROR =
610
599
  'compact: the conversation changed while compacting; compaction not applied, run it again';
611
600
 
601
+ // Why a compaction took as long as it did: persisted next to lastSummaryModel
602
+ // and echoed as one daemon.log line. Wall time runs start → end of commit; the
603
+ // summary call's own time comes from the handoff pipeline, so the remainder is
604
+ // planning, transcript comparison and the commit below. The archive write is
605
+ // scheduled by the commit and runs later, off it.
606
+ function recordCompactionTiming(plan, freshContextResult, { startedAt, commitStartedAt, afterTokens }) {
607
+ const { session, resolvedSessionId } = plan;
608
+ const committedAt = Date.now();
609
+ const pipeline = freshContextResult?.diagnostics?.pipeline;
610
+ const summaryMs = pipeline?.generated?.durationMs ?? null;
611
+ // How the summary ran: a fork of the cached request or the isolated
612
+ // request, and on which route (session / maintenance / lighter).
613
+ let summaryRun = null;
614
+ if (pipeline?.summaryRoute) {
615
+ const kind = pipeline.generated?.mode === 'shared-prefix' ? 'fork' : 'isolated';
616
+ summaryRun = `${kind}:${pipeline.summaryRoute}`;
617
+ }
618
+ const timing = {
619
+ lastStartedAt: startedAt,
620
+ lastCommittedAt: committedAt,
621
+ lastDurationMs: committedAt - startedAt,
622
+ lastPipelineMs: pipeline?.totalMs ?? null,
623
+ lastSummaryMs: summaryMs,
624
+ lastCommitMs: committedAt - commitStartedAt,
625
+ lastCompactMode: summaryRun ? `${pipeline.mode}:${summaryRun}` : pipeline?.mode || null,
626
+ };
627
+ Object.assign(session.compaction, timing);
628
+ writeStderrBestEffort(
629
+ `compaction session=${resolvedSessionId || 'unknown'} source=${freshContextResult?.handoffSource || 'session-local'} ` +
630
+ `model=${freshContextResult?.summaryModel || '-'} mode=${summaryRun || '-'} total=${timing.lastDurationMs}ms ` +
631
+ `summary=${summaryMs ?? 0}ms commit=${timing.lastCommitMs}ms ` +
632
+ `in=${plan.beforeTokens} out=${afterTokens}\n`
633
+ );
634
+ }
635
+
612
636
  async function commitSessionCompaction(plan, run, compactStartedAt) {
637
+ const commitStartedAt = Date.now();
613
638
  const { session, messages, mode, force, resolvedSessionId, reserveTokens } = plan;
614
639
  const { freshContextResult } = run;
615
640
  const numbers = await afterCompactionNumbers(plan, run.compacted);
@@ -688,6 +713,7 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
688
713
  if (changed) {
689
714
  session.pendingCacheBreakIntent = mode === 'auto' ? 'post_turn_compaction' : 'manual_compaction';
690
715
  }
716
+ recordCompactionTiming(plan, freshContextResult, { startedAt: compactStartedAt, commitStartedAt, afterTokens });
691
717
  return compactionResult(
692
718
  plan,
693
719
  { changed, reason: unchangedReason },
@@ -2,8 +2,14 @@
2
2
  // Periodic idle-session + tombstone sweep.
3
3
  // Drives sweepStaleSessions on an unref'd interval; closeSession is imported
4
4
  // from session-close.mjs (one-way dependency, no cycle).
5
- import { sweepStaleSessions, sweepStaleSessionsCooperative, evictIdleLiveSessions } from '../store.mjs';
5
+ import {
6
+ applySubAgentRetention,
7
+ sweepStaleSessions,
8
+ sweepStaleSessionsCooperative,
9
+ evictIdleLiveSessions,
10
+ } from '../store.mjs';
6
11
  import { sweepOrphanedPendingMessages } from './pending-messages.mjs';
12
+ import { applySessionSidebarChange, readSessionSidebarState } from '../../../../shared/session-sidebar-host.mjs';
7
13
  import {
8
14
  IN_FLIGHT_STAGES,
9
15
  _getRuntimeEntry,
@@ -83,6 +89,29 @@ function _logTombstoneSweep(tombstonesCleaned, tombstoneDetails, tombstoneErrors
83
89
  }
84
90
  }
85
91
 
92
+ // Sub-agent trees the sweep found idle: archived after 3 days (through the
93
+ // desktop's archive flag, when one is attached), deleted after 30
94
+ // (store/sweep/sub-agent-retention.mjs).
95
+ async function _retainSubAgents(members) {
96
+ if (!members?.length) return;
97
+ try {
98
+ const flags = await readSessionSidebarState();
99
+ const applied = await applySubAgentRetention(members, {
100
+ isSessionLive: _isSessionLive,
101
+ isArchived: flags && ((id) => Object.hasOwn(flags.archived, id)),
102
+ setSidebar: applySessionSidebarChange,
103
+ });
104
+ for (const id of applied.deleted) if (!_isSessionLive(id)) _clearSessionRuntime(id);
105
+ if (applied.archived.length || applied.deleted.length) {
106
+ _sweepLog(
107
+ `[session-sweep] sub-agent retention: archived ${applied.archived.length}${_previewIds(applied.archived.map((id) => ({ id })))}, deleted ${applied.deleted.length}${_previewIds(applied.deleted.map((id) => ({ id })))}\n`
108
+ );
109
+ }
110
+ } catch (e) {
111
+ process.stderr.write(`[session-sweep] sub-agent retention error: ${e?.message || e}\n`);
112
+ }
113
+ }
114
+
86
115
  async function sweepIdleSessions({ includeTombstones = true, sweepIdle = true } = {}) {
87
116
  const startedAt = Date.now();
88
117
  try {
@@ -90,8 +119,10 @@ async function sweepIdleSessions({ includeTombstones = true, sweepIdle = true }
90
119
  sweepIdle,
91
120
  tombstoneMaxAgeMs: includeTombstones ? TOMBSTONE_MAX_AGE_MS : 0,
92
121
  isSessionLive: _isSessionLive,
122
+ retainSubAgents: sweepIdle,
93
123
  });
94
124
  const { cleaned, remaining, details, tombstonesCleaned = 0, tombstoneDetails = [], tombstoneErrors = [] } = result;
125
+ await _retainSubAgents(result.subAgents);
95
126
  if (cleaned > 0) {
96
127
  for (const d of details) {
97
128
  if (!_finalizeSweptSessionRuntime(d)) continue;
@@ -0,0 +1,115 @@
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, ORCHESTRATION_MODES } 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
+ /**
35
+ * Read a shipped coding-mode slice from the actual BP3 message, not the mode
36
+ * metadata: older profile refreshes could rewrite one without the other.
37
+ * Only a complete, unambiguous generated slice is evidence. Custom rules,
38
+ * headings quoted in another slice, and unrecognized legacy layouts are not.
39
+ */
40
+ export function orchestrationModeFromPrompt(session) {
41
+ const core = session?.messages?.find(
42
+ (message) => message?.role === 'system' && message.cacheTier === 'tier3'
43
+ )?.content;
44
+ if (typeof core !== 'string') return null;
45
+ const slices = core.split('\n\n---\n\n');
46
+ const modes = ORCHESTRATION_MODES.filter((mode) => slices.includes(orchestrationInstructions(mode)));
47
+ return modes.length === 1 ? modes[0] : null;
48
+ }
49
+
50
+ const listed = (names) => names.map((name) => `\`${name}\``).join(', ');
51
+
52
+ /** The reminder that makes the Lead follow `mode`: self-contained (the mode's
53
+ * rules) and superseding every earlier one. `carried`: the system prompt
54
+ * carries this mode itself (a switch back, or the overlay folded in). */
55
+ export function orchestrationModeNoticeContent({ mode, carried = false, loadableTools = [] }) {
56
+ const name = orchestrationModeName(mode);
57
+ const lead = carried
58
+ ? `This conversation now uses the ${name} orchestration mode, the one the system prompt carries.`
59
+ : `This conversation now uses the ${name} orchestration mode. The system prompt's Orchestration Mode no longer applies; follow ${name}'s below instead.`;
60
+ const pronoun = loadableTools.length === 1 ? 'it' : 'them';
61
+ return [
62
+ '<system-reminder>',
63
+ '# Orchestration Mode Changed',
64
+ `${lead} This replaces every earlier Orchestration Mode Changed reminder in this conversation.`,
65
+ ...(loadableTools.length
66
+ ? [
67
+ `${listed(loadableTools)}: not among your directly defined tools yet; load ${pronoun} with \`load_tool\` before the first call.`,
68
+ ]
69
+ : []),
70
+ `Agents already started keep running with their own instructions; new delegation follows ${name}.`,
71
+ '',
72
+ orchestrationInstructions(mode),
73
+ '</system-reminder>',
74
+ ].join('\n');
75
+ }
76
+
77
+ /** Stage the notice of a switch from `from` to `to`; a switch back to what the
78
+ * model was last told drops it. Returns the staged state or null. */
79
+ export function stageOrchestrationModeSwitchNotice(session, from, to) {
80
+ const prior = session.orchestrationModeSwitch;
81
+ const told = prior ? prior.from : from;
82
+ if (!to || told === to) {
83
+ delete session.orchestrationModeSwitch;
84
+ return null;
85
+ }
86
+ session.orchestrationModeSwitch = {
87
+ version: 1,
88
+ revision: (Number(prior?.revision) || 0) + 1,
89
+ from: told,
90
+ to,
91
+ };
92
+ return session.orchestrationModeSwitch;
93
+ }
94
+
95
+ /**
96
+ * The orchestration mode notice the next user turn carries, or null.
97
+ * @returns {{ revision: number, content: string } | null}
98
+ */
99
+ export function snapshotPendingOrchestrationModeNotice(session) {
100
+ const pending = session?.orchestrationModeSwitch;
101
+ if (!pending?.to) return null;
102
+ const overlay = session.orchestrationModeOverlay;
103
+ const content =
104
+ overlay?.mode === pending.to && overlay.content
105
+ ? overlay.content
106
+ : orchestrationModeNoticeContent({ mode: pending.to, carried: true });
107
+ return { revision: pending.revision, content };
108
+ }
109
+
110
+ /** The notice of `revision` reached the model: nothing is due any more. */
111
+ export function acknowledgePendingOrchestrationModeNotice(session, revision) {
112
+ if (!session?.orchestrationModeSwitch || session.orchestrationModeSwitch.revision !== Number(revision)) return false;
113
+ delete session.orchestrationModeSwitch;
114
+ return true;
115
+ }
@@ -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
+ }
@@ -3,6 +3,7 @@ import { mergeNormalizedContentEntries } from '../loop/steering.mjs';
3
3
  import { isDeliveredCompletion, logDuplicateSkip } from './delivered-completions.mjs';
4
4
  import { promptContentText } from './prompt-utils.mjs';
5
5
  import { parseTaskNotification, taskNotificationId } from '../../../../shared/task-notification-envelope.mjs';
6
+ import { hopOf } from '../../../../shared/bot-hops.mjs';
6
7
 
7
8
  const STALE_USER_INJECTION_TTL_MS = 30 * 60 * 1000;
8
9
  const PENDING_PROCESS_START_MS = Date.now();
@@ -95,11 +96,13 @@ function executionFromCompletionMeta(meta) {
95
96
  function completionEntryFields(entry) {
96
97
  const executionId = completionExecutionId(entry);
97
98
  const execution = normalizeExecution(entry?.execution, executionId);
99
+ const hop = hopOf(entry?.hop);
98
100
  return {
99
101
  notificationKind: COMPLETION_NOTIFICATION_KIND,
100
102
  mode: PENDING_MODE_TASK_NOTIFICATION,
101
103
  ...(executionId ? { executionId } : {}),
102
104
  ...(execution ? { execution } : {}),
105
+ ...(hop ? { hop } : {}),
103
106
  };
104
107
  }
105
108
 
@@ -141,6 +144,8 @@ export function markCompletionEntry(text, options = {}) {
141
144
  mode: PENDING_MODE_TASK_NOTIFICATION,
142
145
  ...(executionId ? { executionId } : {}),
143
146
  ...(execution ? { execution } : {}),
147
+ // The bot-chain hop of the turn that launched the work (bot-hops.mjs).
148
+ ...(hopOf(options?.meta?.hop) ? { hop: options.meta.hop } : {}),
144
149
  enqueuedAt: Date.now(),
145
150
  };
146
151
  }
@@ -337,6 +342,7 @@ export function _groupPendingMessageEntries(entries) {
337
342
  ...merged,
338
343
  mode: PENDING_MODE_TASK_NOTIFICATION,
339
344
  ...(normalized.execution ? { execution: normalized.execution } : {}),
345
+ ...(normalized.hop ? { hop: normalized.hop } : {}),
340
346
  ids: idsOf([entry]),
341
347
  entries: [entry],
342
348
  });
@@ -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)
@@ -329,6 +329,42 @@ export function withPresentedWorkspaceSlices(session, nextHead) {
329
329
  return [nextHead, instructionFiles, memory].filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
330
330
  }
331
331
 
332
+ const AGENT_CATALOG_HEADING = '# Available Agents';
333
+ // One generated catalog line (workflow/context.mjs agentCatalog):
334
+ // `- <name> (<id>)` with an optional `: <description>`.
335
+ const AGENT_CATALOG_ENTRY = /^- (.+?) \(([^()\s]+)\)(?:: .*)?$/;
336
+
337
+ /** The entries of a generated agent list slice, or null when `slice` is not
338
+ * one (only the exact generated format is evidence). */
339
+ export function agentListEntries(slice) {
340
+ const text = String(slice || '').trim();
341
+ if (!text.startsWith(`${AGENT_CATALOG_HEADING}\n\n`)) return null;
342
+ const entries = [];
343
+ for (const line of text.slice(AGENT_CATALOG_HEADING.length + 2).split('\n')) {
344
+ const match = AGENT_CATALOG_ENTRY.exec(line);
345
+ if (!match) return null;
346
+ entries.push({ id: match[2], name: match[1], line });
347
+ }
348
+ return entries;
349
+ }
350
+
351
+ /**
352
+ * The "# Available Agents" slice the session's prompt presents: '' when it
353
+ * carries none, null when what it carries under that heading is not one
354
+ * complete generated list (custom text, or more than one). A coding Lead's
355
+ * rebuilt core keeps a presented list, as it keeps the workspace slices: the
356
+ * list the model saw first stays the baseline every later agent list
357
+ * reminder applies to (agent-listing-notice.mjs).
358
+ */
359
+ export function presentedAgentCatalogSlice(session) {
360
+ const core = session.bp3EnvSplit === true ? textOf(bp3SystemMessage(session)) : String(session.bp3CoreContext || '');
361
+ const slices = core
362
+ .split(BP3_PART_SEPARATOR)
363
+ .filter((slice) => slice === AGENT_CATALOG_HEADING || slice.startsWith(`${AGENT_CATALOG_HEADING}\n`));
364
+ if (!slices.length) return '';
365
+ return slices.length === 1 && agentListEntries(slices[0]) ? slices[0] : null;
366
+ }
367
+
332
368
  const WORKSPACE_SLICES = ['environmentContext', 'coreMemoryContext', 'instructionFilesContext'];
333
369
 
334
370
  /**
@@ -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
  }