mixdog 1.0.12 → 1.1.1

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 (356) hide show
  1. package/README.md +83 -182
  2. package/package.json +4 -2
  3. package/scripts/build-shipped-history.mjs +131 -0
  4. package/scripts/latest-guard.mjs +63 -0
  5. package/scripts/lib/run-node-tests.mjs +330 -32
  6. package/scripts/lib/test-failure-records.mjs +5 -0
  7. package/scripts/lib/test-failure-summary.mjs +14 -3
  8. package/scripts/lib/test-spawn-ledger.mjs +19 -0
  9. package/scripts/merge-mac-update-feed.mjs +107 -0
  10. package/scripts/mobile-release.mjs +210 -0
  11. package/scripts/post-release-check.mjs +353 -0
  12. package/scripts/prune-embedding-runtime.mjs +9 -5
  13. package/scripts/publish-asset-release.mjs +43 -0
  14. package/scripts/release-guard.mjs +63 -0
  15. package/scripts/test-direct.mjs +16 -1
  16. package/scripts/test.mjs +29 -7
  17. package/src/agents/advisor/AGENT.md +38 -0
  18. package/src/agents/advisor/agent.json +6 -0
  19. package/src/agents/reviewer/AGENT.md +5 -2
  20. package/src/agents/reviewer/agent.json +1 -1
  21. package/src/defaults/shipped-history.json +383 -0
  22. package/src/defaults/skills/computer-use/SKILL.md +1 -1
  23. package/src/defaults/skills/goal-management/SKILL.md +11 -5
  24. package/src/defaults/skills/setup/SKILL.md +5 -8
  25. package/src/defaults/skills/setup/references/actions.md +62 -31
  26. package/src/defaults/skills/setup/references/surfaces.md +12 -10
  27. package/src/lib/keychain-cjs.cjs +582 -17
  28. package/src/lib/rules-builder.cjs +1 -1
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +3 -1
  30. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  31. package/src/runtime/agent/orchestrator/config-presets.d.mts +1 -0
  32. package/src/runtime/agent/orchestrator/config-presets.mjs +44 -0
  33. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +9 -1
  34. package/src/runtime/agent/orchestrator/config-storage.mjs +32 -4
  35. package/src/runtime/agent/orchestrator/config.mjs +120 -21
  36. package/src/runtime/agent/orchestrator/context/instruction-files.mjs +130 -0
  37. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -0
  38. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +77 -26
  39. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +44 -14
  40. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +2 -1
  41. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +11 -2
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +2 -0
  43. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -3
  44. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +388 -0
  45. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +92 -109
  46. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +107 -0
  47. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +51 -9
  48. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +11 -0
  50. package/src/runtime/agent/orchestrator/providers/lib/open-login-url.mjs +9 -0
  51. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +254 -0
  52. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +218 -0
  53. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +239 -0
  54. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +233 -0
  55. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-login.mjs +247 -0
  56. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +225 -0
  57. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +542 -0
  58. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +25 -0
  59. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +18 -10
  60. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +51 -11
  62. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +6 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +7 -5
  64. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +12 -0
  65. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +37 -9
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +12 -2
  67. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
  68. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +99 -0
  69. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +4 -8
  70. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +3 -7
  71. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +8 -0
  72. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +70 -0
  73. package/src/runtime/agent/orchestrator/providers/registry.mjs +42 -3
  74. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +19 -35
  75. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +7 -0
  76. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +0 -17
  77. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +0 -3
  78. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +0 -1
  79. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +16 -2
  81. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -2
  82. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +18 -4
  83. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/session-route-policy.mjs +16 -0
  85. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +2 -0
  86. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +7 -7
  88. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -0
  89. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -1
  90. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +28 -3
  91. package/src/runtime/agent/orchestrator/session/loop/tool-call-ids.mjs +90 -0
  92. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +19 -11
  93. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +1 -0
  94. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +24 -11
  95. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +2 -6
  96. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +28 -0
  97. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +63 -3
  98. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +8 -0
  99. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  100. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +4 -4
  101. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +2 -0
  103. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -0
  104. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +23 -0
  105. package/src/runtime/agent/orchestrator/session/turn-effort.mjs +18 -0
  106. package/src/runtime/agent/orchestrator/stall-policy.mjs +0 -1
  107. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +11 -8
  108. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +27 -12
  109. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +245 -0
  110. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +13 -1
  111. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +3 -1
  112. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  113. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +49 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +3 -3
  115. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -7
  116. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +19 -20
  117. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +67 -19
  118. package/src/runtime/agent/orchestrator/tools/builtin/file-version-test-helpers.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/tools/builtin/file-version.mjs +13 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +28 -3
  121. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-single-file-rescue.mjs +8 -5
  122. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +25 -12
  123. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +73 -1
  124. package/src/runtime/agent/orchestrator/tools/builtin/raw-content-publication.mjs +17 -0
  125. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +136 -76
  126. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +2 -2
  127. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +15 -17
  128. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -2
  129. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +11 -10
  130. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -3
  131. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +30 -16
  132. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +17 -6
  134. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +23 -5
  135. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +93 -43
  136. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +4 -11
  137. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +7 -3
  138. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-validation.mjs +16 -19
  139. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +32 -6
  140. package/src/runtime/agent/orchestrator/tools/builtin.mjs +9 -2
  141. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +3 -1
  142. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  143. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +12 -15
  144. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +6 -3
  145. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +14 -4
  146. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +4 -2
  147. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +58 -8
  148. package/src/runtime/agent/orchestrator/tools/patch/content-guard.mjs +47 -0
  149. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +155 -0
  150. package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-proofs.mjs +6 -2
  151. package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-snapshots.mjs +6 -2
  152. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +19 -16
  153. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +13 -44
  154. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +2 -2
  155. package/src/runtime/agent/orchestrator/tools/patch/post-patch-excerpts.mjs +5 -2
  156. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +47 -7
  157. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +72 -8
  158. package/src/runtime/agent/orchestrator/tools/patch/section-coalesce.mjs +3 -2
  159. package/src/runtime/agent/orchestrator/tools/patch/sequence/run.mjs +9 -1
  160. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +9 -1
  161. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +6 -6
  162. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +16 -4
  163. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +35 -14
  164. package/src/runtime/agent/orchestrator/tools/patch/wave.mjs +42 -4
  165. package/src/runtime/agent/orchestrator/tools/shell-codepage.mjs +296 -0
  166. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +124 -81
  167. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +75 -24
  168. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +19 -4
  169. package/src/runtime/channels/lib/config.mjs +3 -8
  170. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +7 -3
  171. package/src/{tui → runtime/channels}/lib/voice-setup.mjs +38 -22
  172. package/src/runtime/channels/lib/webhook/request-handler.mjs +42 -8
  173. package/src/runtime/computer-bridge/actions.mjs +2 -0
  174. package/src/runtime/computer-bridge/client.mjs +25 -1
  175. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +84 -0
  176. package/src/runtime/computer-bridge/linux-readiness.d.mts +52 -0
  177. package/src/runtime/computer-bridge/linux-readiness.mjs +411 -0
  178. package/src/runtime/computer-bridge/test-linux-display.mjs +5 -0
  179. package/src/runtime/effort-judge/compact-tokenizer.mjs +13 -8
  180. package/src/runtime/effort-judge/judge-client.mjs +12 -3
  181. package/src/runtime/effort-judge/judge-input.mjs +5 -2
  182. package/src/runtime/effort-judge/model-install.mjs +9 -3
  183. package/src/runtime/effort-judge/model-manifest.json +8 -8
  184. package/src/runtime/effort-judge/previous-human-request.mjs +162 -0
  185. package/src/runtime/effort-judge/verified-context-input.mjs +10 -0
  186. package/src/runtime/memory/lib/embedding-provider.mjs +12 -0
  187. package/src/runtime/memory/lib/embedding-worker.mjs +9 -3
  188. package/src/runtime/memory/lib/http-router/session-routes.mjs +2 -1
  189. package/src/runtime/memory/lib/memory-config-flags.mjs +3 -2
  190. package/src/runtime/memory/lib/memory-embed.mjs +5 -0
  191. package/src/runtime/memory/lib/pg/supervisor.mjs +3 -0
  192. package/src/runtime/shared/agent-route-config.mjs +50 -2
  193. package/src/runtime/shared/bounded-file-read.mjs +166 -0
  194. package/src/runtime/shared/builtin-grandfathering.mjs +37 -0
  195. package/src/runtime/shared/config.mjs +74 -6
  196. package/src/runtime/shared/defaults-separation.mjs +263 -0
  197. package/src/runtime/shared/developer-options.mjs +7 -0
  198. package/src/runtime/shared/instruction-files-mode.mjs +9 -0
  199. package/src/runtime/shared/llm/anthropic-effort.mjs +4 -0
  200. package/src/runtime/shared/llm/model-catalog.mjs +25 -1
  201. package/src/runtime/shared/llm/openai-daybreak.mjs +79 -0
  202. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +105 -1
  203. package/src/runtime/shared/llm/usage-ledger-worker.mjs +2 -1
  204. package/src/runtime/shared/llm/usage-ledger.mjs +72 -5
  205. package/src/runtime/shared/onnx-runtime-support.mjs +6 -0
  206. package/src/runtime/shared/orchestration.mjs +14 -6
  207. package/src/runtime/shared/pristine-execution-contract.json +9 -0
  208. package/src/runtime/shared/pristine-execution.mjs +1 -1
  209. package/src/runtime/shared/projects.mjs +65 -48
  210. package/src/runtime/shared/provider-accounts.mjs +49 -6
  211. package/src/runtime/shared/quota-resume-store.mjs +255 -0
  212. package/src/runtime/shared/resource-admission.mjs +10 -3
  213. package/src/runtime/shared/schedule-model-ref.mjs +11 -0
  214. package/src/runtime/shared/session-approval-wait.mjs +35 -0
  215. package/src/runtime/shared/shell-display.mjs +4 -2
  216. package/src/runtime/shared/shipped-definitions.mjs +275 -0
  217. package/src/runtime/shared/tool-card-model.golden.json +3 -3
  218. package/src/runtime/shared/tool-result-summary.mjs +86 -15
  219. package/src/runtime/shared/transcript-metadata.mjs +2 -0
  220. package/src/runtime/shared/turn-review-reply.mjs +2 -0
  221. package/src/runtime/shared/turn-snapshot.mjs +65 -6
  222. package/src/runtime/shared/turn-worktree-snapshot.mjs +208 -27
  223. package/src/session-runtime/boot/apis.mjs +24 -0
  224. package/src/session-runtime/boot/config.mjs +4 -0
  225. package/src/session-runtime/boot/facade.mjs +4 -1
  226. package/src/session-runtime/boot/infrastructure.mjs +21 -2
  227. package/src/session-runtime/boot/session.mjs +3 -0
  228. package/src/session-runtime/channel-config-api.mjs +5 -0
  229. package/src/session-runtime/config-lifecycle/config-writers.mjs +3 -0
  230. package/src/session-runtime/config-lifecycle.mjs +31 -6
  231. package/src/session-runtime/goal-facade-api.mjs +4 -0
  232. package/src/session-runtime/lifecycle/inheritance.mjs +28 -6
  233. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +3 -0
  234. package/src/session-runtime/lifecycle/session-switch/resume.mjs +7 -1
  235. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +6 -0
  236. package/src/session-runtime/lifecycle/teardown/session-close.mjs +14 -2
  237. package/src/session-runtime/media-api.mjs +32 -22
  238. package/src/session-runtime/model-recency.mjs +17 -0
  239. package/src/session-runtime/model-route/route-tuning.mjs +3 -5
  240. package/src/session-runtime/model-route/set-route.mjs +49 -21
  241. package/src/session-runtime/model-route-api.mjs +2 -1
  242. package/src/session-runtime/notification-bus.mjs +31 -6
  243. package/src/session-runtime/provider-auth/login.mjs +16 -10
  244. package/src/session-runtime/provider-auth/setup.mjs +22 -2
  245. package/src/session-runtime/provider-models/catalog-load.mjs +3 -1
  246. package/src/session-runtime/provider-models/row-hydration.mjs +26 -17
  247. package/src/session-runtime/provider-readiness.mjs +4 -0
  248. package/src/session-runtime/quick-model-rows/model-meta.mjs +26 -0
  249. package/src/session-runtime/quick-model-rows/provider-rows.mjs +40 -14
  250. package/src/session-runtime/resource-api.mjs +8 -1
  251. package/src/session-runtime/runtime-core.mjs +4 -0
  252. package/src/session-runtime/runtime-facade.mjs +25 -1
  253. package/src/session-runtime/runtime-review-api.mjs +12 -1
  254. package/src/session-runtime/schedule-session-run.mjs +3 -1
  255. package/src/session-runtime/services/agent-tool/helpers.mjs +4 -0
  256. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +63 -27
  257. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +15 -3
  258. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +2 -0
  259. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +11 -3
  260. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +2 -0
  261. package/src/session-runtime/services/agent-tool/worker-index/approval-wait.mjs +97 -0
  262. package/src/session-runtime/services/agent-tool/worker-index/mutation-batch.mjs +4 -2
  263. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -2
  264. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +3 -0
  265. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +58 -2
  266. package/src/session-runtime/services/agent-tool/worker-index.mjs +69 -15
  267. package/src/session-runtime/services/agent-tool/worker-rows.mjs +2 -0
  268. package/src/session-runtime/services/channel-admin.mjs +91 -15
  269. package/src/session-runtime/services/provider-admin.mjs +336 -80
  270. package/src/session-runtime/services/seeds.mjs +80 -152
  271. package/src/session-runtime/services/usage-dashboard-model.mjs +35 -0
  272. package/src/session-runtime/services/usage-dashboard.mjs +7 -0
  273. package/src/session-runtime/session-address.mjs +125 -0
  274. package/src/session-runtime/session-create.mjs +57 -2
  275. package/src/session-runtime/settings-builtin-tools-api.mjs +23 -0
  276. package/src/session-runtime/settings-compaction-api.mjs +13 -0
  277. package/src/session-runtime/setup-tool/executor.mjs +97 -40
  278. package/src/session-runtime/setup-tool/extended-actions.mjs +64 -5
  279. package/src/session-runtime/setup-tool/settings-contract.mjs +44 -1
  280. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -7
  281. package/src/session-runtime/tool-policy-refresh.mjs +3 -0
  282. package/src/session-runtime/tool-surface.mjs +16 -9
  283. package/src/session-runtime/turn/session-ops.mjs +3 -0
  284. package/src/session-runtime/turn/share-ops.mjs +4 -1
  285. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +6 -0
  286. package/src/session-runtime/usage-stats-api.mjs +15 -0
  287. package/src/session-runtime/webhook-session-run.mjs +3 -1
  288. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +31 -5
  289. package/src/session-runtime/workflow-agents-api/agent-route.mjs +4 -0
  290. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +27 -7
  291. package/src/standalone/daemon-boot-coordinator.mjs +10 -0
  292. package/src/standalone/daemon-drain-policy.mjs +9 -0
  293. package/src/standalone/daemon-stored-session-views.mjs +7 -8
  294. package/src/standalone/daemon.mjs +21 -5
  295. package/src/standalone/local-session-runtime.mjs +11 -0
  296. package/src/standalone/session-client.mjs +52 -7
  297. package/src/standalone/session-protocol.mjs +9 -0
  298. package/src/standalone/session-service/agent-tree/agent-child.mjs +2 -0
  299. package/src/standalone/session-service/entries/lifecycle.mjs +2 -0
  300. package/src/standalone/session-service/entries/session-loads.mjs +9 -0
  301. package/src/standalone/session-service/projection/revisions.mjs +28 -4
  302. package/src/standalone/session-service/projection.mjs +1 -0
  303. package/src/standalone/session-service/quota-resume.mjs +140 -0
  304. package/src/standalone/session-service/session-calls/session-action.mjs +2 -2
  305. package/src/standalone/session-service/session-calls.mjs +10 -2
  306. package/src/standalone/session-service/stored-reader.mjs +26 -1
  307. package/src/standalone/session-service.mjs +21 -0
  308. package/src/standalone/session-transport/routes.mjs +2 -0
  309. package/src/tui/app/doctor.mjs +7 -11
  310. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +3 -5
  311. package/src/tui/app/model-options.mjs +13 -1
  312. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +10 -4
  313. package/src/tui/app/provider-setup/oauth-actions.mjs +5 -0
  314. package/src/tui/app/settings-picker/settings-rows.mjs +4 -6
  315. package/src/tui/app/settings-picker/settings-toggles/store-toggles.mjs +1 -1
  316. package/src/tui/app/slash-dispatch/route-commands.mjs +3 -2
  317. package/src/tui/components/ContextPanel.jsx +5 -1
  318. package/src/tui/components/Spinner.jsx +34 -319
  319. package/src/tui/components/SpinnerByline.jsx +256 -0
  320. package/src/tui/components/use-spinner-progress.mjs +113 -0
  321. package/src/tui/dist/index.mjs +15421 -14283
  322. package/src/tui/session/agent-job-feed/execution-delivery.mjs +13 -1
  323. package/src/tui/session/agent-job-feed/notification-router.mjs +3 -0
  324. package/src/tui/session/agent-job-feed/pending-resume.mjs +14 -1
  325. package/src/tui/session/agent-job-feed.mjs +4 -0
  326. package/src/tui/session/context-state.mjs +15 -1
  327. package/src/tui/session/initial-state.mjs +6 -0
  328. package/src/tui/session/live-share/owner.mjs +17 -0
  329. package/src/tui/session/oauth-flows.mjs +2 -0
  330. package/src/tui/session/quota-resume.mjs +263 -0
  331. package/src/tui/session/session-api/intake/abort.mjs +39 -35
  332. package/src/tui/session/session-api/intake/submission.mjs +13 -21
  333. package/src/tui/session/session-api/intake.mjs +3 -3
  334. package/src/tui/session/session-api/integrations.mjs +13 -9
  335. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  336. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -2
  337. package/src/tui/session/session-api/lifecycle/session-reset.mjs +4 -0
  338. package/src/tui/session/session-api/lifecycle.mjs +71 -2
  339. package/src/tui/session/session-api/media.mjs +2 -2
  340. package/src/tui/session/session-api/quota-resume.mjs +21 -0
  341. package/src/tui/session/session-api/routes.mjs +4 -2
  342. package/src/tui/session/session-api/settings.mjs +2 -0
  343. package/src/tui/session/session-api.mjs +2 -0
  344. package/src/tui/session/session-flow/auto-clear.mjs +14 -6
  345. package/src/tui/session/session-flow/drain.mjs +52 -5
  346. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -3
  347. package/src/tui/session/session-flow/queue.mjs +3 -0
  348. package/src/tui/session/session-flow/steering.mjs +11 -0
  349. package/src/tui/session/session-flow.mjs +1 -0
  350. package/src/tui/session/tool-approval.mjs +7 -2
  351. package/src/tui/session/tool-card-results/grouped-result.mjs +11 -2
  352. package/src/tui/session/transcript-intake.mjs +1 -0
  353. package/src/tui/session/transcript-spill/spill-dir.mjs +22 -6
  354. package/src/tui/session/turn.mjs +40 -3
  355. package/src/tui/spinner-meta.mjs +26 -10
  356. package/src/vendor/statusline/bin/statusline-route.mjs +3 -2
@@ -30,7 +30,9 @@
30
30
  * Non-breakpoint providers:
31
31
  * - OpenAI (public): prompt_cache_key plus model-specific retention when
32
32
  * response storage is enabled (30m cache options or 24h retention).
33
- * - OpenAI OAuth: prompt_cache_key only (server in-memory 5-10min)
33
+ * - OpenAI OAuth: prompt_cache_key only; the Codex backend keeps the
34
+ * prompt cache at least 30 minutes on GPT-5.6+ models, refreshed on use
35
+ * (the openai-oauth auto-clear window matches it)
34
36
  * - Gemini: provider-managed explicit cachedContents with 5m default TTL, plus
35
37
  * implicit caching as a fallback when the prefix is below cache minimums.
36
38
  * - xAI: conversation routing for chat; Responses omits prompt_cache_key
@@ -76,6 +76,8 @@ export function prepareAgentSession({
76
76
  schemaAllowedTools,
77
77
  sessionId,
78
78
  mcpScopeId,
79
+ contextPercent,
80
+ selectedContextWindow,
79
81
  }) {
80
82
  const effectivePermission = resolveAgentSessionPermission(agent, permission);
81
83
  // Pass cwd through verbatim — null is the fixed agent sentinel meaning
@@ -112,6 +114,8 @@ export function prepareAgentSession({
112
114
  compaction: compaction || undefined,
113
115
  mcpScopeId: mcpScopeId || null,
114
116
  };
117
+ if (contextPercent) sessionOpts.contextPercent = contextPercent;
118
+ if (selectedContextWindow) sessionOpts.selectedContextWindow = selectedContextWindow;
115
119
  if (agentTag) sessionOpts.agentTag = agentTag;
116
120
  if (effectivePermission) sessionOpts.permission = effectivePermission;
117
121
  if (permissionMode) sessionOpts.permissionMode = permissionMode;
@@ -0,0 +1 @@
1
+ export function withDefaultPresets(stored: unknown): unknown[];
@@ -1,4 +1,6 @@
1
1
  // Preset normalization and identity are independent of config storage.
2
+ import { isDeepStrictEqual } from 'node:util';
3
+
2
4
  const ANTHROPIC_FAMILY_MODEL = Object.freeze({
3
5
  opus: 'claude-opus-4-8',
4
6
  sonnet: 'claude-sonnet-4-6',
@@ -64,6 +66,48 @@ export const DEFAULT_PRESETS = Object.freeze([
64
66
  }),
65
67
  ]);
66
68
 
69
+ const DEFAULT_PRESET_BY_ID = new Map(DEFAULT_PRESETS.map((preset) => [preset.id, preset]));
70
+
71
+ /** Effective presets: the stored entries in their stored order, then the
72
+ * shipped presets missing from storage in shipped order. */
73
+ export function withDefaultPresets(stored) {
74
+ const list = Array.isArray(stored) ? stored : [];
75
+ const present = new Set(list.map(presetKey));
76
+ return [...list, ...DEFAULT_PRESETS.filter((preset) => !present.has(preset.id)).map((preset) => ({ ...preset }))];
77
+ }
78
+
79
+ /** Stored form of `presets`: only entries that differ from the shipped default of the same id. */
80
+ export function presetsDelta(presets) {
81
+ return (Array.isArray(presets) ? presets : []).filter((preset) => {
82
+ const shipped = DEFAULT_PRESET_BY_ID.get(presetKey(preset));
83
+ return !shipped || !isDeepStrictEqual(normalizePreset(preset), { ...shipped });
84
+ });
85
+ }
86
+
87
+ /** Stored form of `maintenance`: only slots that differ from the shipped route. */
88
+ export function maintenanceDelta(maintenance) {
89
+ const source = maintenance && typeof maintenance === 'object' ? maintenance : {};
90
+ return Object.fromEntries(
91
+ Object.entries(source).filter(([slot, route]) => !isDeepStrictEqual(route, DEFAULT_MAINTENANCE[slot]))
92
+ );
93
+ }
94
+
95
+ /** Drop shipped presets and maintenance routes that equal their default; loadConfig restores them. */
96
+ export function stripStoredRouteDefaults(agent) {
97
+ const next = { ...agent };
98
+ if (Object.hasOwn(next, 'presets')) {
99
+ const presets = presetsDelta(next.presets);
100
+ if (presets.length) next.presets = presets;
101
+ else delete next.presets;
102
+ }
103
+ if (Object.hasOwn(next, 'maintenance')) {
104
+ const maintenance = maintenanceDelta(next.maintenance);
105
+ if (Object.keys(maintenance).length) next.maintenance = maintenance;
106
+ else delete next.maintenance;
107
+ }
108
+ return next;
109
+ }
110
+
67
111
  const AGENT_PROVIDER_ALIASES = Object.freeze({
68
112
  'openai-api': 'openai',
69
113
  'gemini-api': 'gemini',
@@ -50,9 +50,17 @@ export function mergeStoredProviders({ raw, defaults, includeSecrets }) {
50
50
  }
51
51
  }
52
52
  // Custom providers: the keychain account is the generated custom-<uuid> id.
53
+ // A keyless (auth 'none') custom provider never carries a key, not even a
54
+ // hand-edited plaintext one.
55
+ for (const [name, val] of Object.entries(mergedProviders)) {
56
+ if (val?.type === 'custom' && val.auth === 'none' && Object.hasOwn(val, 'apiKey')) {
57
+ const { apiKey: _dropped, ...rest } = val;
58
+ mergedProviders[name] = rest;
59
+ }
60
+ }
53
61
  if (includeSecrets) {
54
62
  for (const [name, val] of Object.entries(mergedProviders)) {
55
- if (val?.type !== 'custom' || !name.startsWith('custom-')) continue;
63
+ if (val?.type !== 'custom' || !name.startsWith('custom-') || val.auth === 'none') continue;
56
64
  const kc = getAgentApiKey(name);
57
65
  if (kc) mergedProviders[name] = { ...val, apiKey: kc, enabled: raw.providers?.[name]?.enabled !== false };
58
66
  }
@@ -2,6 +2,11 @@ import { hasExtensionScopes, normalizeExtensionScopes } from '../../shared/exten
2
2
  import { agentRouteStorageNeedsMigration, canonicalizeAgentRouteStorage } from '../../shared/agent-route-config.mjs';
3
3
  import profileConfig from '../../shared/profile-config.cjs';
4
4
  import { normalizeAgentProviderId, normalizePreset } from './config-presets.mjs';
5
+ import {
6
+ loadSavedCodexCatalog,
7
+ migrateCodexRoute,
8
+ supersededCodexSettingsKey,
9
+ } from './providers/openai-oauth-catalog-store.mjs';
5
10
 
6
11
  const { normalizeProfileConfig } = profileConfig;
7
12
  const RETIRED_LOCAL_PROVIDER_IDS = new Set(['ollama', 'lmstudio']);
@@ -205,7 +210,20 @@ function retiredLocalProviderStoragePresent(value = {}) {
205
210
  );
206
211
  }
207
212
 
213
+ // Saved routes on a model the provider catalog has superseded (see
214
+ // openai-oauth-catalog-store.mjs) — read from the last fetched catalog.
215
+ function supersededRouteStoragePresent(value = {}, catalog = loadSavedCodexCatalog()) {
216
+ const superseded = (route) => migrateCodexRoute(route, catalog) !== route;
217
+ return (
218
+ (Array.isArray(value?.presets) && value.presets.some(superseded)) ||
219
+ Object.values(configObject(value?.agents)).some(superseded) ||
220
+ Object.values(configObject(value?.maintenance)).some(superseded) ||
221
+ Object.keys(configObject(value?.modelSettings)).some((key) => supersededCodexSettingsKey(key, catalog))
222
+ );
223
+ }
224
+
208
225
  export function canonicalizeAgentStorage(value = {}) {
226
+ const catalog = loadSavedCodexCatalog();
209
227
  const next = canonicalizeAgentRouteStorage(value);
210
228
  const removedPresetIds = new Set(
211
229
  (Array.isArray(next.presets) ? next.presets : [])
@@ -216,17 +234,26 @@ export function canonicalizeAgentStorage(value = {}) {
216
234
  next.providers = configObject(next.providers);
217
235
  for (const id of RETIRED_LOCAL_PROVIDER_IDS) delete next.providers[id];
218
236
  next.presets = Array.isArray(next.presets)
219
- ? next.presets.map((preset) => normalizePreset(preset)).filter((preset) => preset && !isRetiredLocalRoute(preset))
237
+ ? next.presets
238
+ .map((preset) => normalizePreset(migrateCodexRoute(preset, catalog)))
239
+ .filter((preset) => preset && !isRetiredLocalRoute(preset))
220
240
  : [];
221
241
  if (removedPresetIds.has(String(next.default || '').trim())) next.default = null;
222
242
  next.agents = Object.fromEntries(
223
- Object.entries(configObject(next.agents)).filter(([, route]) => !isRetiredLocalRoute(route))
243
+ Object.entries(configObject(next.agents))
244
+ .filter(([, route]) => !isRetiredLocalRoute(route))
245
+ .map(([id, route]) => [id, migrateCodexRoute(route, catalog)])
224
246
  );
225
247
  next.maintenance = Object.fromEntries(
226
- Object.entries(configObject(next.maintenance)).filter(([, route]) => !isRetiredLocalRoute(route))
248
+ Object.entries(configObject(next.maintenance))
249
+ .filter(([, route]) => !isRetiredLocalRoute(route))
250
+ .map(([slot, route]) => [slot, migrateCodexRoute(route, catalog)])
227
251
  );
252
+ // A superseded model's own settings have no model left to apply to.
228
253
  next.modelSettings = Object.fromEntries(
229
- Object.entries(normalizedModelSettings(value)).filter(([key]) => !isRetiredLocalModelKey(key))
254
+ Object.entries(normalizedModelSettings(value)).filter(
255
+ ([key]) => !isRetiredLocalModelKey(key) && !supersededCodexSettingsKey(key, catalog)
256
+ )
230
257
  );
231
258
  setOrDelete(next, 'autoClear', canonicalizeAutoClearStorage(next.autoClear));
232
259
  setOrDelete(next, 'compaction', canonicalizeCompactionStorage(next.compaction));
@@ -266,6 +293,7 @@ export function agentConfigStorageNeedsMigration(value = {}) {
266
293
  return (
267
294
  agentRouteStorageNeedsMigration(value) ||
268
295
  retiredLocalProviderStoragePresent(value) ||
296
+ supersededRouteStoragePresent(value) ||
269
297
  RETIRED_AGENT_FIELDS.some((key) => key !== 'guide' && Object.hasOwn(value || {}, key)) ||
270
298
  presets.some((preset) => !normalizePreset(preset)) ||
271
299
  (modules && typeof modules === 'object' && Object.hasOwn(modules, 'memory')) ||
@@ -4,9 +4,33 @@ import { mergeStoredProviders } from './config-providers-merge.mjs';
4
4
  import { normalizeExtensionScopes } from '../../shared/extension-scopes.mjs';
5
5
  import { applyConfigPatch, diffConfig } from '../../shared/config-patch.mjs';
6
6
  import { normalizeWorkflowSelection } from '../../shared/orchestration.mjs';
7
- import { DEFAULT_DISABLED_AGENT_IDS, canonicalizeAgentRouteStorage } from '../../shared/agent-route-config.mjs';
7
+ import {
8
+ DEFAULT_INSTRUCTION_FILES_MODE,
9
+ normalizeInstructionFilesMode,
10
+ } from '../../shared/instruction-files-mode.mjs';
11
+ import {
12
+ DEFAULT_DISABLED_AGENT_ROSTER,
13
+ canonicalizeAgentRouteStorage,
14
+ disabledAgentDelta,
15
+ disabledAgentIds,
16
+ effectiveDisabledAgents,
17
+ withEffectiveDisabledAgents,
18
+ withStoredDisabledAgents,
19
+ } from '../../shared/agent-route-config.mjs';
20
+ import {
21
+ DEFAULTS_VERSION,
22
+ ensureDefaultsSeparated,
23
+ storedDefaultsVersion,
24
+ stripAgentSectionDefaults,
25
+ } from '../../shared/defaults-separation.mjs';
8
26
  import profileConfig from '../../shared/profile-config.cjs';
9
- import { DEFAULT_MAINTENANCE, DEFAULT_PRESETS, normalizePreset } from './config-presets.mjs';
27
+ import {
28
+ DEFAULT_MAINTENANCE,
29
+ DEFAULT_PRESETS,
30
+ normalizePreset,
31
+ stripStoredRouteDefaults,
32
+ withDefaultPresets,
33
+ } from './config-presets.mjs';
10
34
  import {
11
35
  agentConfigStorageNeedsMigration,
12
36
  canonicalizeAgentStorage,
@@ -87,9 +111,13 @@ export function buildDefaultConfig(options = {}) {
87
111
  // stored in mixdog-config.json — enabled at runtime from the presence of
88
112
  // Mixdog-owned credentials.
89
113
  providers['grok-oauth'] = oauthEntry('grok-oauth');
114
+ // Muse Code subscription (Meta Muse Spark), enabled from its stored key.
115
+ providers['muse-code-oauth'] = oauthEntry('muse-code-oauth');
90
116
  // Dev-only providers (Settings → Developer → Providers, one option each):
91
117
  // omitted entirely from the default config while their option is off so
92
118
  // they never surface in settings.
119
+ // Experimental MiMo subscription: enabled from a stored platform API key.
120
+ if (isOAuthProviderAvailable('mimo-oauth')) providers['mimo-oauth'] = oauthEntry('mimo-oauth');
93
121
  // Experimental direct Cursor wire provider. It remains disabled unless a
94
122
  // Mixdog-owned login or CURSOR_ACCESS_TOKEN is present.
95
123
  if (isOAuthProviderAvailable('cursor-oauth')) providers['cursor-oauth'] = oauthEntry('cursor-oauth');
@@ -100,11 +128,10 @@ export function buildDefaultConfig(options = {}) {
100
128
  providers['mixdog-local'] = { enabled: false };
101
129
  return {
102
130
  providers,
103
- // The Maintainer also starts off: background upkeep spends model calls, so
104
- // it runs only once the user turns it on and picks its model.
105
- disabledAgents: [...DEFAULT_DISABLED_AGENT_IDS, 'maintainer'],
131
+ // Effective roster; the file stores only the user's delta against it.
132
+ disabledAgents: [...DEFAULT_DISABLED_AGENT_ROSTER],
106
133
  workflow: { active: 'default' },
107
- orchestrationMode: 'none',
134
+ orchestrationMode: 'balanced',
108
135
  };
109
136
  }
110
137
 
@@ -168,11 +195,26 @@ function normalizeRecapConfig(rawRecap) {
168
195
  return recap;
169
196
  }
170
197
 
198
+ // `instructionFiles` (instruction-files-mode.mjs): only a valid non-default
199
+ // mode is kept; absent means the default.
200
+ function storedInstructionFiles(agent) {
201
+ const mode = normalizeInstructionFilesMode(agent?.instructionFiles);
202
+ return mode === DEFAULT_INSTRUCTION_FILES_MODE ? {} : { instructionFiles: mode };
203
+ }
204
+
171
205
  export function loadConfig(options = {}) {
172
206
  const includeSecrets = options.secrets !== false;
207
+ try {
208
+ ensureDefaultsSeparated({ stripRouteDefaults: separateStoredAgent });
209
+ } catch (err) {
210
+ process.stderr.write(`[config] defaults separation failed: ${err?.message}\n`);
211
+ }
173
212
  const sectionRaw = readSection('agent');
174
213
  if (hasKeys(sectionRaw)) {
175
214
  try {
215
+ // A file that could not be separated still holds the legacy form:
216
+ // before version 1 `disabledAgents` is the full list, not a delta.
217
+ const legacyDisabledAgents = storedDefaultsVersion() < 1;
176
218
  let raw = sectionRaw;
177
219
  const storageNeedsMigration = agentConfigStorageNeedsMigration(raw);
178
220
  raw = canonicalizeAgentStorage(raw);
@@ -206,7 +248,7 @@ export function loadConfig(options = {}) {
206
248
  const loaded = canonicalizeAgentRouteStorage({
207
249
  providers: mergedProviders,
208
250
  mcpServers,
209
- presets: normalizedPresets,
251
+ presets: withDefaultPresets(normalizedPresets),
210
252
  default: raw.default || null,
211
253
  maintenance: { ...DEFAULT_MAINTENANCE, ...normalizedMaint },
212
254
  workflowRoutes,
@@ -214,9 +256,11 @@ export function loadConfig(options = {}) {
214
256
  modelSettings,
215
257
  onboarding: raw.onboarding && typeof raw.onboarding === 'object' ? raw.onboarding : {},
216
258
  agents: raw.agents && typeof raw.agents === 'object' ? raw.agents : {},
217
- // Explicit "off" roster. canonicalizeAgentRouteStorage normalizes
218
- // and drops it when empty, so an all-enabled config stays clean.
219
- disabledAgents: Array.isArray(raw.disabledAgents) ? raw.disabledAgents : [],
259
+ // Effective "off" roster = code defaults + the stored user delta.
260
+ // canonicalizeAgentRouteStorage drops it when empty.
261
+ disabledAgents: legacyDisabledAgents ? disabledAgentIds(raw) : effectiveDisabledAgents(raw),
262
+ ...(raw.memoryTools && typeof raw.memoryTools === 'object' ? { memoryTools: { ...raw.memoryTools } } : {}),
263
+ ...storedInstructionFiles(raw),
220
264
  ...normalizeWorkflowSelection(raw),
221
265
  profile: normalizeProfileConfig(raw.profile),
222
266
  skills: normalizeSkillsConfig(raw.skills),
@@ -241,7 +285,7 @@ export function loadConfig(options = {}) {
241
285
  // Retired cross-section fields are no longer migrated by
242
286
  // the shared config layer. Normalize only the locked
243
287
  // current section; stale reads must not undo deletions.
244
- persistAgentConfig(canonicalizeAgentStorage);
288
+ persistAgentConfig((current) => storedAgentForm(canonicalizeAgentStorage(current)));
245
289
  } catch (err) {
246
290
  process.stderr.write(`[config] persist canonical agent config failed: ${err?.message}\n`);
247
291
  }
@@ -263,7 +307,7 @@ export function loadConfig(options = {}) {
263
307
  onboarding: {},
264
308
  agents: {},
265
309
  workflow: { active: 'default' },
266
- orchestrationMode: 'none',
310
+ orchestrationMode: 'balanced',
267
311
  profile: normalizeProfileConfig(null),
268
312
  skills: normalizeSkillsConfig(null),
269
313
  extensionScopes: normalizeExtensionScopes(null),
@@ -383,28 +427,83 @@ function buildAgentSaveBuilder(config) {
383
427
  // Developer options (developer-options.mjs); an empty set stays absent.
384
428
  if (Object.keys(developer).length) next.developer = developer;
385
429
  else if (Object.hasOwn(config, 'developer')) delete next.developer;
386
- if (canonicalRoutes.disabledAgents) {
387
- next.disabledAgents = canonicalRoutes.disabledAgents;
388
- } else {
389
- delete next.disabledAgents;
390
- }
430
+ // Only the user's delta against the default roster is stored.
431
+ delete next.disabledAgents;
432
+ delete next.enabledAgents;
433
+ // An unseparated file keeps the legacy full list until the pass succeeds.
434
+ if (storedDefaultsVersion() < 1) {
435
+ if (canonicalRoutes.disabledAgents?.length) next.disabledAgents = canonicalRoutes.disabledAgents;
436
+ } else Object.assign(next, disabledAgentDelta(canonicalRoutes.disabledAgents));
437
+ if (config.memoryTools && typeof config.memoryTools === 'object') next.memoryTools = { ...config.memoryTools };
438
+ else delete next.memoryTools;
439
+ delete next.instructionFiles;
440
+ Object.assign(next, storedInstructionFiles(config));
391
441
  delete next.workflowRoutes;
392
- return removeRetiredAgentFields(next);
442
+ // Defaults live in code: values equal to the current default are not stored.
443
+ return storedAgentForm(removeRetiredAgentFields(next));
393
444
  };
394
445
  }
446
+
447
+ /** A numeric `default` is a preset index; store the preset id it points at (index into the effective list). */
448
+ function resolveNumericDefault(agent) {
449
+ const value = agent?.default;
450
+ // The loader reads a falsy default (including 0) as "none".
451
+ if (!value || !(typeof value === 'number' || /^\d+$/.test(String(value)))) return agent;
452
+ const stored = (Array.isArray(agent.presets) ? agent.presets : [])
453
+ .map((preset) => normalizePreset(preset))
454
+ .filter(Boolean)
455
+ .filter((preset) => preset.id !== 'workflow-search');
456
+ const { presets } = canonicalizeAgentRouteStorage({ presets: withDefaultPresets(stored) });
457
+ const preset = presets[Number(value)];
458
+ return preset ? { ...agent, default: preset.id } : agent;
459
+ }
460
+
461
+ /** The agent section as stored: defaults dropped. Route defaults go only once the file is separated. */
462
+ function storedAgentForm(agent) {
463
+ const stripped = stripAgentSectionDefaults(resolveNumericDefault(agent));
464
+ return storedDefaultsVersion() >= DEFAULTS_VERSION ? stripStoredRouteDefaults(stripped) : stripped;
465
+ }
466
+
467
+ /** Separation pass: normalize the compared sections first, then drop every default. */
468
+ function separateStoredAgent(agent) {
469
+ const full = canonicalizeAgentStorage(agent);
470
+ const next = { ...agent };
471
+ for (const key of ['autoClear', 'compaction', 'modules']) {
472
+ if (!Object.hasOwn(agent, key)) continue;
473
+ if (full[key] === undefined) delete next[key];
474
+ else next[key] = full[key];
475
+ }
476
+ return stripStoredRouteDefaults(stripAgentSectionDefaults(resolveNumericDefault(next)));
477
+ }
478
+
479
+ /** Patch the stored section; unseparated data keeps its form, a numeric default becomes an id first. */
480
+ function applyStoredPatch(current, changes) {
481
+ const base = resolveNumericDefault(current);
482
+ if (storedDefaultsVersion() >= 1) return applyConfigPatch(base, changes);
483
+ const delta = withStoredDisabledAgents({ ...base, disabledAgents: disabledAgentIds(base) });
484
+ return withEffectiveDisabledAgents(applyConfigPatch(delta, changes));
485
+ }
395
486
  // Diff the persisted schema, not runtime defaults, aliases, or secret overlays.
487
+ // Patches always speak the delta domain; an unseparated file's full list is
488
+ // converted here and back in applyStoredPatch.
489
+ function patchForm(agent) {
490
+ if (storedDefaultsVersion() >= 1) return agent;
491
+ const { disabledAgents, ...rest } = agent;
492
+ return { ...rest, ...disabledAgentDelta(disabledAgents || []) };
493
+ }
494
+
396
495
  export function createConfigPatch(before, after) {
397
- return diffConfig(buildAgentSaveBuilder(before)({}), buildAgentSaveBuilder(after)({}));
496
+ return diffConfig(patchForm(buildAgentSaveBuilder(before)({})), patchForm(buildAgentSaveBuilder(after)({})));
398
497
  }
399
498
 
400
499
  export function saveConfigPatch(changes) {
401
500
  if (!changes.length) return;
402
- persistAgentConfig((current) => applyConfigPatch(current, changes));
501
+ persistAgentConfig((current) => applyStoredPatch(current, changes));
403
502
  }
404
503
 
405
504
  export async function saveConfigPatchAsync(changes) {
406
505
  if (!changes.length) return;
407
- await persistAgentConfigAsync((current) => applyConfigPatch(current, changes));
506
+ await persistAgentConfigAsync((current) => applyStoredPatch(current, changes));
408
507
  }
409
508
 
410
509
  // Whole-section replacement is reserved for explicit full saves. Read/edit/save
@@ -0,0 +1,130 @@
1
+ // The user's instruction files (AGENTS.md / CLAUDE.md), global then project,
2
+ // rendered as one `# Instruction Files` block for a new session's BP3 core.
3
+ import { closeSync, constants, fstatSync, openSync, readSync, realpathSync, statSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { isAbsolute, join, relative, resolve } from 'node:path';
6
+ import { readSection } from '../../../shared/config.mjs';
7
+ import { normalizeInstructionFilesMode } from '../../../shared/instruction-files-mode.mjs';
8
+ import { featureEnvOverride } from '../runtime-core/config-helpers.mjs';
9
+
10
+ // Per-file budget in UTF-8 bytes (same byte-based cap as core memory).
11
+ const INSTRUCTION_FILE_MAX_BYTES = 32 * 1024;
12
+ const TRUNCATED_MARK = '\n\n[... truncated]';
13
+ const KINDS_BY_MODE = { all: ['agents', 'claude'], agents: ['agents'], claude: ['claude'], off: [] };
14
+ const GLOBAL_DIR = { agents: '.codex', claude: '.claude' };
15
+ const FILE_NAME = { agents: 'AGENTS.md', claude: 'CLAUDE.md' };
16
+
17
+ /** The configured mode, forced off by MIXDOG_BOOT_INSTRUCTION_FILES=0. */
18
+ export function effectiveInstructionFilesMode(mode) {
19
+ if (featureEnvOverride('MIXDOG_BOOT_INSTRUCTION_FILES') === false) return 'off';
20
+ return normalizeInstructionFilesMode(mode);
21
+ }
22
+
23
+ /** The stored agent-config value; an unreadable config reads as the default. */
24
+ export function configuredInstructionFilesMode() {
25
+ try {
26
+ return normalizeInstructionFilesMode(readSection('agent')?.instructionFiles);
27
+ } catch {
28
+ return normalizeInstructionFilesMode(undefined);
29
+ }
30
+ }
31
+
32
+ const UTF8_BOM = Buffer.from([0xef, 0xbb, 0xbf]);
33
+
34
+ /** Candidate files in load order: global files first, then the project root's.
35
+ * Project files are confined to the real directory of their `root`; global
36
+ * files carry no root and may resolve anywhere. */
37
+ function instructionFileCandidates({ mode, cwd, homeDir = homedir() } = {}) {
38
+ const kinds = KINDS_BY_MODE[effectiveInstructionFilesMode(mode)];
39
+ const candidates = [];
40
+ if (homeDir) {
41
+ const root = resolve(homeDir);
42
+ // Global files may be symlinks into a dotfiles repo: no containment root.
43
+ for (const kind of kinds) candidates.push({ path: join(root, GLOBAL_DIR[kind], FILE_NAME[kind]) });
44
+ }
45
+ if (cwd) {
46
+ const root = resolve(cwd);
47
+ for (const kind of kinds) candidates.push({ path: join(root, FILE_NAME[kind]), root });
48
+ }
49
+ return candidates;
50
+ }
51
+
52
+ function isInside(root, path) {
53
+ const rel = relative(root, path);
54
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
55
+ }
56
+
57
+ function skipNote(path, reason) {
58
+ process.stderr.write(`[instruction-files] skipped ${path}: ${reason}\n`);
59
+ }
60
+
61
+ /** At most the file's first cap bytes (plus a BOM allowance and one probe byte)
62
+ * from an open fd, so a huge or endless file is never read whole. */
63
+ function readHead(fd) {
64
+ const buffer = Buffer.allocUnsafe(INSTRUCTION_FILE_MAX_BYTES + UTF8_BOM.length + 1);
65
+ let length = 0;
66
+ while (length < buffer.length) {
67
+ const count = readSync(fd, buffer, length, buffer.length - length, null);
68
+ if (count === 0) break;
69
+ length += count;
70
+ }
71
+ return buffer.subarray(0, length);
72
+ }
73
+
74
+ /** Decode a head that may end mid-character: the incomplete tail is dropped. */
75
+ function decodeHead(bytes) {
76
+ return new TextDecoder('utf-8').decode(bytes, { stream: true });
77
+ }
78
+
79
+ /** The bounded, trimmed text of one instruction file; over the budget it is cut
80
+ * at a word boundary and ends with the truncation notice. */
81
+ function boundedText(head) {
82
+ const bytes = head.subarray(0, UTF8_BOM.length).equals(UTF8_BOM) ? head.subarray(UTF8_BOM.length) : head;
83
+ if (bytes.length <= INSTRUCTION_FILE_MAX_BYTES) return decodeHead(bytes).trim();
84
+ const budget = INSTRUCTION_FILE_MAX_BYTES - Buffer.byteLength(TRUNCATED_MARK, 'utf8');
85
+ const text = decodeHead(bytes.subarray(0, budget)).trim();
86
+ return `${text.replace(/\s+\S*$/, '')}${TRUNCATED_MARK}`;
87
+ }
88
+
89
+ /** The file's bounded content, or '' when it is absent, unreadable, not a
90
+ * regular file, or resolves (symlink/junction) outside `root` when one is set. */
91
+ function readInstructionFile({ path, root }) {
92
+ let fd;
93
+ try {
94
+ const realPath = realpathSync.native(path);
95
+ if (root && !isInside(realpathSync.native(root), realPath)) {
96
+ skipNote(path, 'resolves outside its root');
97
+ return '';
98
+ }
99
+ if (!statSync(realPath).isFile()) return '';
100
+ fd = openSync(realPath, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
101
+ if (!fstatSync(fd).isFile()) return '';
102
+ return boundedText(readHead(fd));
103
+ } catch (error) {
104
+ if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR' && error?.code !== 'EISDIR') {
105
+ skipNote(path, error?.code || error?.message || 'unreadable');
106
+ }
107
+ return '';
108
+ } finally {
109
+ if (fd !== undefined) closeSync(fd);
110
+ }
111
+ }
112
+
113
+ /** The `# Instruction Files` block, or '' when no file has content. Files
114
+ * whose bounded content repeats an included one (symlinks, copies) are dropped. */
115
+ export function loadInstructionFilesContext({ mode, cwd, homeDir = homedir() } = {}) {
116
+ const seen = new Set();
117
+ const sections = [];
118
+ for (const candidate of instructionFileCandidates({ mode, cwd, homeDir })) {
119
+ const content = readInstructionFile(candidate);
120
+ if (!content || seen.has(content)) continue;
121
+ seen.add(content);
122
+ sections.push(`## ${candidate.path}\n\n${content}`);
123
+ }
124
+ if (!sections.length) return '';
125
+ return [
126
+ '# Instruction Files',
127
+ "The user's instruction files. Project files take precedence over global ones when they conflict.",
128
+ ...sections,
129
+ ].join('\n\n');
130
+ }
@@ -268,12 +268,16 @@ export function composeSystemPrompt(opts) {
268
268
  // Keep the active workflow first within BP3 so it leads the role and
269
269
  // environment material that varies with the session.
270
270
  const coreMemory = skip.memory ? '' : trimmedPromptSlice(opts.coreMemoryContext);
271
+ // User AGENTS.md / CLAUDE.md block (context/instruction-files.mjs), already
272
+ // headed `# Instruction Files`; rides right after core memory.
273
+ const instructionFiles = skip.claudemd ? '' : trimmedPromptSlice(opts.instructionFilesContext);
271
274
  const sessionMarkerCore = joinPromptSlices([
272
275
  trimmedPromptSlice(opts.workflowContext),
273
276
  opts.roleRules || '',
274
277
  opts.userPrompt || '',
275
278
  roleInstructionContext,
276
279
  coreMemory ? `# Core Memory\n${coreMemory}` : '',
280
+ instructionFiles,
277
281
  ]);
278
282
  // Response language is the LAST system text before the conversation. It
279
283
  // rides the environment block (not the tier3 core) because the `# Session`