mixdog 1.3.1 → 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 (377) hide show
  1. package/README.md +8 -4
  2. package/package.json +2 -2
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/src/defaults/shipped-history.json +5 -1
  5. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  6. package/src/defaults/skills/setup/SKILL.md +23 -1
  7. package/src/defaults/skills/setup/references/actions.md +50 -3
  8. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  9. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  10. package/src/defaults/skills/visualize/SKILL.md +9 -6
  11. package/src/defaults/skills/visualize/references/craft.md +8 -1
  12. package/src/help.mjs +1 -1
  13. package/src/lib/keychain-cjs.cjs +4 -1
  14. package/src/lib/rules-builder.cjs +14 -7
  15. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  16. package/src/rules/agent-mode/MIXY.md +6 -8
  17. package/src/rules/lead/LEAD.md +2 -2
  18. package/src/rules/shared/10-tool-workflow.md +4 -0
  19. package/src/rules/shared/20-research.md +2 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  23. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  24. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  25. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  26. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  27. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  28. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  29. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  30. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  31. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  32. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  33. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  34. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  35. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  36. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  39. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  40. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  41. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  45. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  46. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  47. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  48. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  49. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  50. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  51. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  55. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  56. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  57. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  59. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  60. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  61. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  66. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  67. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  69. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -5
  70. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  71. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  73. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  75. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  76. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  77. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +17 -2
  78. package/src/runtime/agent/orchestrator/session/agent-room.mjs +133 -38
  79. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  80. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  82. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  83. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  84. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  85. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  88. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  91. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  92. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +136 -32
  93. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  94. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  95. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  96. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  97. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  98. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  99. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  100. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  101. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  102. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
  103. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  104. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  105. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  108. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  109. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  110. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  113. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  114. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  115. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  116. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  117. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  119. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  120. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  121. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  122. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  123. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +3 -8
  124. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  125. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  126. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  127. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  128. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  129. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  130. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  131. package/src/runtime/agent/orchestrator/session/routine-result.mjs +120 -9
  132. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  134. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  135. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  136. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  137. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  138. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  139. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  140. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  141. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  142. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  143. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  144. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  145. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  146. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  147. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  149. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  150. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  151. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  152. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  154. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  156. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  157. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  158. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  159. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  160. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  161. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  162. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  163. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  164. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  165. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  166. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  167. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  168. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  169. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  170. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  171. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  173. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  174. package/src/runtime/attachments/limits.mjs +5 -1
  175. package/src/runtime/attachments/office-extract.mjs +3 -6
  176. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  177. package/src/runtime/attachments/store.mjs +49 -30
  178. package/src/runtime/browser-bridge/client.mjs +73 -20
  179. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  180. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  181. package/src/runtime/channels/lib/state-file.mjs +15 -15
  182. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  183. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  184. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  185. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  186. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  187. package/src/runtime/effort-judge/judge-client.mjs +5 -2
  188. package/src/runtime/effort-judge/model-install.mjs +16 -8
  189. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  190. package/src/runtime/local-provider/catalog.mjs +5 -2
  191. package/src/runtime/local-provider/registered-models.mjs +64 -27
  192. package/src/runtime/local-provider/server.mjs +2 -3
  193. package/src/runtime/media/adapters/image-api.mjs +89 -0
  194. package/src/runtime/media/auth.mjs +64 -1
  195. package/src/runtime/media/catalog.mjs +66 -4
  196. package/src/runtime/media/jobs.mjs +4 -0
  197. package/src/runtime/media/lanes.mjs +46 -5
  198. package/src/runtime/media/media-usage.mjs +17 -0
  199. package/src/runtime/media/renditions.mjs +15 -15
  200. package/src/runtime/media/store.mjs +36 -35
  201. package/src/runtime/media/tool.mjs +8 -1
  202. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  203. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  204. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  205. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  206. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  207. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  208. package/src/runtime/office/core/journal.mjs +7 -14
  209. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  210. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  211. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  212. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  213. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  214. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  215. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  216. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  217. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  218. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  219. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  220. package/src/runtime/shared/agent-store.mjs +24 -7
  221. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  222. package/src/runtime/shared/atomic-file.d.mts +50 -0
  223. package/src/runtime/shared/atomic-file.mjs +394 -132
  224. package/src/runtime/shared/background-tasks.mjs +7 -4
  225. package/src/runtime/shared/bot-hops.mjs +30 -0
  226. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  227. package/src/runtime/shared/bounded-download.mjs +4 -2
  228. package/src/runtime/shared/config.mjs +39 -9
  229. package/src/runtime/shared/defaults-separation.mjs +6 -2
  230. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  231. package/src/runtime/shared/err-text.mjs +4 -0
  232. package/src/runtime/shared/error-presentation.mjs +22 -4
  233. package/src/runtime/shared/file-has-data.mjs +28 -0
  234. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  235. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  236. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  237. package/src/runtime/shared/projects.mjs +50 -41
  238. package/src/runtime/shared/provider-accounts.mjs +17 -12
  239. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  240. package/src/runtime/shared/routine-trigger.mjs +14 -6
  241. package/src/runtime/shared/run-child.d.mts +3 -0
  242. package/src/runtime/shared/run-child.mjs +24 -3
  243. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  244. package/src/runtime/shared/singleton-owner.mjs +7 -6
  245. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  246. package/src/runtime/shared/staged-update.mjs +4 -2
  247. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  248. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  249. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  250. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  251. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  252. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  253. package/src/runtime/shared/user-cwd.mjs +9 -4
  254. package/src/runtime/shared/user-data-guard.mjs +41 -42
  255. package/src/runtime/shared/utf8-cut.mjs +10 -0
  256. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  257. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  258. package/src/runtime/tidy/core-install.mjs +26 -4
  259. package/src/runtime/tidy/install.mjs +91 -23
  260. package/src/runtime/web-search/lib/config.mjs +4 -16
  261. package/src/session-runtime/agent-home-delivery.mjs +555 -199
  262. package/src/session-runtime/agent-room-delivery.mjs +143 -36
  263. package/src/session-runtime/agent-task-sessions.mjs +667 -165
  264. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  265. package/src/session-runtime/boot/apis.mjs +45 -24
  266. package/src/session-runtime/boot/config.mjs +1 -0
  267. package/src/session-runtime/boot/facade.mjs +11 -1
  268. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  269. package/src/session-runtime/boot/providers.mjs +2 -2
  270. package/src/session-runtime/boot/shared.mjs +12 -6
  271. package/src/session-runtime/boot/tools.mjs +2 -2
  272. package/src/session-runtime/bot-status.mjs +432 -0
  273. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  274. package/src/session-runtime/config-lifecycle.mjs +60 -55
  275. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  276. package/src/session-runtime/held-requests.mjs +36 -33
  277. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  278. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  279. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +19 -4
  280. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  281. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  282. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  283. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  284. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  285. package/src/session-runtime/media-api.mjs +5 -2
  286. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  287. package/src/session-runtime/model-route/set-route.mjs +61 -1
  288. package/src/session-runtime/owned-automation-run.mjs +8 -1
  289. package/src/session-runtime/room-member-gate.mjs +16 -3
  290. package/src/session-runtime/route-resolution.mjs +16 -8
  291. package/src/session-runtime/route-state.mjs +1 -59
  292. package/src/session-runtime/routine-home-turn.mjs +5 -3
  293. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  294. package/src/session-runtime/runtime-paths.mjs +11 -5
  295. package/src/session-runtime/runtime-tunables.mjs +6 -0
  296. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  297. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  298. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  299. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  300. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  301. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  302. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  303. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  304. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  305. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  306. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  307. package/src/session-runtime/services/agent-tool.mjs +6 -0
  308. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  309. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  310. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  311. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  312. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  313. package/src/session-runtime/services/provider-admin.mjs +14 -4
  314. package/src/session-runtime/services/schedule-access.mjs +33 -4
  315. package/src/session-runtime/services/seeds.mjs +4 -3
  316. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  317. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  318. package/src/session-runtime/session-create/materialize.mjs +2 -3
  319. package/src/session-runtime/session-create/session-options.mjs +15 -2
  320. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  321. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  322. package/src/session-runtime/setup-tool/executor.mjs +8 -2
  323. package/src/session-runtime/setup-tool/settings-contract.mjs +31 -1
  324. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  325. package/src/session-runtime/skills-api.mjs +12 -5
  326. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  327. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  328. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  329. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  330. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  331. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  332. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  333. package/src/session-runtime/tool-policy-refresh.mjs +47 -369
  334. package/src/session-runtime/tool-surface.mjs +5 -3
  335. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  336. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  337. package/src/session-runtime/turn/turn-run.mjs +2 -1
  338. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  339. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +519 -333
  340. package/src/session-runtime/workflow-agents-api/agent-route.mjs +46 -46
  341. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  342. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  343. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  344. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  345. package/src/standalone/daemon-agent-control.mjs +4 -1
  346. package/src/standalone/daemon-session-control.mjs +51 -10
  347. package/src/standalone/daemon.mjs +28 -10
  348. package/src/standalone/local-session-runtime.mjs +22 -14
  349. package/src/standalone/session-client.mjs +7 -2
  350. package/src/standalone/session-protocol.mjs +7 -0
  351. package/src/standalone/session-service.mjs +11 -0
  352. package/src/standalone/session-state-projection.mjs +19 -9
  353. package/src/standalone/session-tool-output.mjs +35 -9
  354. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  355. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  356. package/src/tui/app/slash-commands.mjs +1 -1
  357. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  358. package/src/tui/dist/index.mjs +529 -333
  359. package/src/tui/prompt-history-store.mjs +16 -21
  360. package/src/tui/session/context-state.mjs +4 -0
  361. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  362. package/src/tui/session/session-api/media.mjs +2 -1
  363. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  364. package/src/tui/session/session-api/routes-write.mjs +203 -0
  365. package/src/tui/session/session-api/routes.mjs +23 -247
  366. package/src/tui/session/session-api/settings.mjs +14 -1
  367. package/src/tui/session/session-flow/drain.mjs +28 -3
  368. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  369. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  370. package/src/tui/session/session-flow/steering.mjs +8 -3
  371. package/src/tui/session/tool-approval.mjs +185 -20
  372. package/src/tui/session/transcript-intake.mjs +2 -0
  373. package/src/tui/session/turn.mjs +23 -14
  374. package/src/tui/transcript-tool-failures.mjs +8 -2
  375. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  376. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  377. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -8,6 +8,7 @@ import {
8
8
  bridgeSurfaceOutdated,
9
9
  sessionToolExposure,
10
10
  TOOL_EXPOSURE_BRIDGE,
11
+ TOOL_EXPOSURE_NATIVE,
11
12
  TOOL_SURFACE_MIGRATION_INTENT,
12
13
  toolExposureMigrationNeeded,
13
14
  toolExposureModeForProvider,
@@ -122,6 +123,17 @@ export function applyAgentToolExposure(session, { existing = false } = {}) {
122
123
  const bridge =
123
124
  !allowlisted && toolExposureModeForProvider(session.provider, session.wireRoute) === TOOL_EXPOSURE_BRIDGE;
124
125
  if (!bridge && before === null) return false;
126
+ // A native deferred surface (the session runtime builds one) stays one:
127
+ // re-selected over the tools just resolved, never stripped to the full
128
+ // array. Native → native is no exposure change, so nothing is tagged.
129
+ if (!bridge && before === TOOL_EXPOSURE_NATIVE) {
130
+ session.tools = session.tools.slice();
131
+ applyDeferredToolSurface(session, session.deferredSurfaceMode || 'full', [], {
132
+ provider: session.provider,
133
+ model: session.model,
134
+ });
135
+ return false;
136
+ }
125
137
  // A surface an earlier bridge revision built is rebuilt now.
126
138
  const outdated = bridgeSurfaceOutdated(session);
127
139
  // The tools just resolved are the whole authority: no earlier catalog, MCP
@@ -3,6 +3,7 @@
3
3
  // native tool-search payload parsing, bash-session routing, and approval.
4
4
  import { isMcpTool } from '../../mcp/client.mjs';
5
5
  import { unattendedApprovalPort } from '../human-gate/gate.mjs';
6
+ import { isGrantedRun, runAskOutcome } from '../human-gate/run-policy.mjs';
6
7
  import { isBuiltinTool } from '../../tools/builtin.mjs';
7
8
  import { isInternalTool } from '../../internal-tools.mjs';
8
9
  import { loadSkillToolDependencies } from '../../runtime-core/skill-tool-loading.mjs';
@@ -212,12 +213,13 @@ export async function resolvePreToolAskApproval({
212
213
  }) {
213
214
  const name = String(toolName || 'tool');
214
215
  const reason = String(askReason || 'approval requested by hook').trim();
215
- // Unattended runs never auto-approve and have no card: a schedule or webhook
216
- // run always holds the request for its owner; a room member does when no
216
+ // A schedule or webhook run never asks: saving it was the user's grant
217
+ // (human-gate/run-policy.mjs). A room member is held for its owner when no
217
218
  // approval UI is attached (session-runtime/held-requests.mjs).
218
219
  const unattended = unattendedApprovalPort();
219
220
  const run = unattended?.runOf?.(sessionId) || null;
220
- const held = run && (run.kind !== 'room' || typeof toolApprovalHook !== 'function');
221
+ if (isGrantedRun(run)) return runAskOutcome({ name, request });
222
+ const held = run && typeof toolApprovalHook !== 'function';
221
223
  const ask = held ? unattended.hold : toolApprovalHook;
222
224
  if (typeof ask !== 'function') {
223
225
  return { denial: formatMissingToolApprovalUiDenial(name, reason) };
@@ -238,8 +240,12 @@ export async function resolvePreToolAskApproval({
238
240
  return { denial: `Error: tool "${name}" denied by hook: ${detail}` };
239
241
  }
240
242
  if (!approvalGranted(approval)) {
241
- const detail = approvalReason(approval, reason || 'not approved');
242
- return { denial: `Error: tool "${name}" denied by hook: ${detail}`, approval };
243
+ // A person (or the hold for one) said no: never a policy hook to work around.
244
+ const detail = String(approvalReason(approval, reason || 'not approved')).replace(/[.\s]+$/, '');
245
+ return {
246
+ denial: `Error: tool "${name}" was not approved: ${detail}. Do not retry it or reach the same result another way; report it.`,
247
+ approval,
248
+ };
243
249
  }
244
250
  return { approval };
245
251
  }
@@ -11,6 +11,7 @@ import { autoEffortModel, turnEffortConfigurationMode } from '../../providers/ef
11
11
  import {
12
12
  autoEffortApplicable,
13
13
  autoEffortBase,
14
+ clampAutoEffortBase,
14
15
  normalizeAutoEffortMode,
15
16
  resolveAutoEffort,
16
17
  } from '../../providers/auto-effort.mjs';
@@ -58,6 +59,15 @@ export function autoEffortMode() {
58
59
  }
59
60
  }
60
61
 
62
+ /** The global "Auto reasoning base" setting; absent or unreadable is medium. */
63
+ export function autoEffortBaseSetting() {
64
+ try {
65
+ return autoEffortBase(readSection('agent')?.autoEffortBase);
66
+ } catch {
67
+ return autoEffortBase(undefined);
68
+ }
69
+ }
70
+
61
71
  /**
62
72
  * Starts the judge worker when a session is created or opened on a route that
63
73
  * Auto effort can judge, so the first judged turn does not wait for the cold
@@ -102,7 +112,9 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
102
112
  if (!request || request.startsWith('<')) return null;
103
113
  if (!turnEffortConfigurationMode(session, provider)) return null;
104
114
  const chosen = String(session.effort || '').toLowerCase();
105
- const base = autoEffortBase(chosen);
115
+ const options = effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model));
116
+ const setting = autoEffortBaseSetting();
117
+ const base = clampAutoEffortBase({ base: setting, options }) ?? autoEffortBase(setting);
106
118
  const record = {
107
119
  at: new Date().toISOString(),
108
120
  sessionId,
@@ -113,7 +125,6 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
113
125
  base,
114
126
  requestChars: request.length,
115
127
  };
116
- const options = effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model));
117
128
  // No judged outcome can apply when the base is off the model's ladder, so
118
129
  // the judge is not asked (in observe mode too: nothing would be logged).
119
130
  if (!autoEffortApplicable({ base, options })) {
@@ -19,12 +19,15 @@ import { retainedFailedTurnInput, rewindUnansweredPrompt } from './failed-turn-r
19
19
  import { buildAgentLoopOptions, trackedToolCallHook } from './ask-turn-loop-options.mjs';
20
20
  import { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
21
21
  import { backfillFallbackSessionTitle } from './session-fallback-title.mjs';
22
+ import { stampTurnHop } from '../../../../shared/bot-hops.mjs';
22
23
 
23
24
  /** Resolves the provider, builds the user turn and pre-commits it; returns
24
25
  * what the loop and the commit need from this preparation. */
25
26
  export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, transcriptMeta }) {
26
27
  const { turn, runtime, turnSignal, askGeneration, checkpoint, turnToken, startedAt, codexTurnId } = opened;
27
28
  const session = turn.session;
29
+ // Where the turn sits in a bot-to-bot chain (session-runtime/bot-hops.mjs).
30
+ stampTurnHop(session, transcriptMeta, input.promptSource);
28
31
  const retained = input.retryTurnId
29
32
  ? retainedFailedTurnInput(session.messages, input.retryTurnId, input.retryAttemptId)
30
33
  : null;
@@ -187,6 +190,7 @@ export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, tr
187
190
  deferredToolDelta: userTurn.deferredToolDelta,
188
191
  goalReminder: userTurn.goalReminder,
189
192
  workflowNotice: userTurn.workflowNotice,
193
+ orchestrationModeNotice: userTurn.orchestrationModeNotice,
190
194
  workspaceNotice: userTurn.workspaceNotice,
191
195
  setupNotice: userTurn.setupNotice,
192
196
  };
@@ -20,6 +20,7 @@ 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';
23
24
  import { codexWireSendOpts } from './session-id.mjs';
24
25
  import { _tryBridgeExplicitPrefetch } from './prefetch-bridge.mjs';
25
26
  import { filterModelVisibleSessionMessages } from './message-sanitize.mjs';
@@ -184,8 +185,12 @@ export async function buildUserTurn({
184
185
  // A mid-conversation workflow switch: the prompt and tools were rebuilt
185
186
  // for the new workflow; this turn tells the model once.
186
187
  const workflowNotice = snapshotPendingWorkflowNotice(session);
188
+ // A mid-conversation orchestration mode switch, after the workflow notice
189
+ // so the mode it names wins over the one a workflow reminder carries.
190
+ const orchestrationModeNotice = snapshotPendingOrchestrationModeNotice(session);
187
191
  const reminderBlock = [
188
192
  workflowNotice?.content || '',
193
+ orchestrationModeNotice?.content || '',
189
194
  workspaceNotice?.content || '',
190
195
  setupNotice?.content || '',
191
196
  currentTimeBlock ? `<system-reminder>\n# Current Time\n${currentTimeBlock}\n</system-reminder>` : '',
@@ -215,6 +220,7 @@ export async function buildUserTurn({
215
220
  deferredToolDelta,
216
221
  goalReminder,
217
222
  workflowNotice,
223
+ orchestrationModeNotice,
218
224
  workspaceNotice,
219
225
  setupNotice,
220
226
  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
  }
@@ -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 },
@@ -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)
@@ -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);