mixdog 1.2.6 → 1.3.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 (294) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +71 -9
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/isolated-test-env.mjs +16 -0
  7. package/src/agents/reviewer/AGENT.md +4 -2
  8. package/src/agents/reviewer/agent.json +1 -1
  9. package/src/defaults/shipped-history.json +10 -0
  10. package/src/defaults/skills/browser-use/SKILL.md +5 -0
  11. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  12. package/src/defaults/skills/setup/references/actions.md +14 -4
  13. package/src/defaults/skills/setup-guide/SKILL.md +178 -0
  14. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  15. package/src/lib/rules-builder.cjs +55 -6
  16. package/src/output-styles/common.md +13 -8
  17. package/src/rules/agent/AGENT.md +4 -0
  18. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  19. package/src/rules/agent-mode/MIXY.md +28 -0
  20. package/src/rules/agent-mode/ROUTINE.md +6 -0
  21. package/src/rules/lead/LEAD.md +7 -3
  22. package/src/rules/shared/70-delivery.md +3 -0
  23. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  24. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  25. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +241 -14
  26. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
  27. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +216 -0
  28. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
  29. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  30. package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
  31. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  33. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  34. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  35. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  36. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  37. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
  40. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  43. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +7 -8
  44. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +3 -1
  46. package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
  47. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +9 -2
  49. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  50. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  51. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
  52. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
  53. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
  55. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  56. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  57. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +14 -0
  58. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  59. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  60. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  61. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +21 -5
  62. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +40 -7
  63. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +52 -20
  64. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  66. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  67. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  68. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  69. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  70. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +166 -0
  71. package/src/runtime/agent/orchestrator/session/agent-room.mjs +650 -0
  72. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  73. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  75. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +192 -0
  76. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  77. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  78. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  79. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  80. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  81. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
  82. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  83. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +108 -6
  84. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -1
  85. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
  86. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
  87. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  88. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  89. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +49 -0
  90. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
  91. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
  93. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  94. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  95. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  96. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  97. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
  98. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  99. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  100. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +33 -24
  101. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +58 -8
  102. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  103. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
  105. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  106. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +27 -0
  108. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  109. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +86 -0
  110. package/src/runtime/agent/orchestrator/session/routine-result.mjs +329 -0
  111. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  112. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
  113. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  114. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  115. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  116. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  117. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +58 -10
  118. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +92 -15
  119. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  121. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
  123. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
  124. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
  125. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
  127. package/src/runtime/attachments/store.mjs +6 -0
  128. package/src/runtime/browser-bridge/client.mjs +24 -0
  129. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  130. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  131. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  132. package/src/runtime/channels/lib/webhook.mjs +0 -1
  133. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  134. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  135. package/src/runtime/computer-bridge/client.mjs +28 -1
  136. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  137. package/src/runtime/connectors/google-mcp.mjs +446 -0
  138. package/src/runtime/effort-judge/judge-client.mjs +1 -1
  139. package/src/runtime/effort-judge/model-manifest.json +7 -7
  140. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  141. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  142. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  143. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  144. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  145. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  146. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  147. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  148. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  149. package/src/runtime/memory/lib/memory.mjs +7 -1
  150. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  151. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  152. package/src/runtime/memory/lib/tool-call-handler.mjs +2 -0
  153. package/src/runtime/memory/tool-defs.mjs +5 -0
  154. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  155. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  156. package/src/runtime/shared/agent-avatar.mjs +320 -0
  157. package/src/runtime/shared/agent-kind.mjs +152 -0
  158. package/src/runtime/shared/agent-route-config.d.mts +4 -0
  159. package/src/runtime/shared/agent-route-config.mjs +30 -0
  160. package/src/runtime/shared/agent-store.mjs +132 -0
  161. package/src/runtime/shared/atomic-file.mjs +14 -1
  162. package/src/runtime/shared/bot-split-migration.mjs +457 -0
  163. package/src/runtime/shared/config-patch.mjs +20 -1
  164. package/src/runtime/shared/config.mjs +8 -1
  165. package/src/runtime/shared/connector-accounts.mjs +15 -0
  166. package/src/runtime/shared/connector-catalog.d.mts +51 -0
  167. package/src/runtime/shared/connector-catalog.mjs +228 -0
  168. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  169. package/src/runtime/shared/connector-triggers.mjs +45 -0
  170. package/src/runtime/shared/err-text.mjs +3 -1
  171. package/src/runtime/shared/error-presentation.mjs +91 -6
  172. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  173. package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
  174. package/src/runtime/shared/handoff-artifacts.mjs +61 -0
  175. package/src/runtime/shared/held-request-host.d.mts +12 -0
  176. package/src/runtime/shared/held-request-host.mjs +71 -0
  177. package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
  178. package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
  179. package/src/runtime/shared/model-route-fallback.mjs +79 -7
  180. package/src/runtime/shared/plain-preview.d.mts +1 -0
  181. package/src/runtime/shared/plain-preview.mjs +33 -0
  182. package/src/runtime/shared/routine-limits.d.mts +11 -0
  183. package/src/runtime/shared/routine-limits.mjs +21 -0
  184. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  185. package/src/runtime/shared/routine-trigger.mjs +44 -0
  186. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  187. package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
  188. package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
  189. package/src/runtime/shared/slow-span.mjs +4 -12
  190. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  191. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  192. package/src/runtime/shared/turn-changes.mjs +0 -8
  193. package/src/runtime/shared/usage-readout-totals.mjs +58 -0
  194. package/src/runtime/shared/user-data-guard.mjs +119 -38
  195. package/src/session-runtime/agent-home-delivery.mjs +826 -0
  196. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  197. package/src/session-runtime/agent-room-delivery.mjs +133 -0
  198. package/src/session-runtime/agent-task-sessions.mjs +460 -0
  199. package/src/session-runtime/boot/apis.mjs +63 -18
  200. package/src/session-runtime/boot/facade.mjs +5 -8
  201. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  202. package/src/session-runtime/boot/providers.mjs +14 -4
  203. package/src/session-runtime/boot/tools.mjs +3 -0
  204. package/src/session-runtime/channel-config-api.mjs +53 -5
  205. package/src/session-runtime/config-lifecycle.mjs +43 -2
  206. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  207. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  208. package/src/session-runtime/handoff-attachments.mjs +593 -0
  209. package/src/session-runtime/held-requests.mjs +228 -0
  210. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  211. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  212. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  213. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +56 -3
  214. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  215. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  216. package/src/session-runtime/lifecycle/session-switch/resume.mjs +4 -0
  217. package/src/session-runtime/lifecycle/shared.mjs +6 -0
  218. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  219. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  220. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  221. package/src/session-runtime/model-route/route-persist.mjs +2 -2
  222. package/src/session-runtime/model-route/route-tuning.mjs +7 -1
  223. package/src/session-runtime/model-route/set-route.mjs +35 -4
  224. package/src/session-runtime/native-web-search.mjs +2 -2
  225. package/src/session-runtime/new-session-config.mjs +6 -1
  226. package/src/session-runtime/owned-automation-run.mjs +229 -0
  227. package/src/session-runtime/resource-api.mjs +5 -0
  228. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  229. package/src/session-runtime/resource-skills-api.mjs +12 -0
  230. package/src/session-runtime/room-member-gate.mjs +48 -0
  231. package/src/session-runtime/route-resolution.mjs +5 -11
  232. package/src/session-runtime/routine-home-turn.mjs +156 -0
  233. package/src/session-runtime/routine-owner.mjs +48 -0
  234. package/src/session-runtime/schedule-session-run.mjs +30 -64
  235. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -1
  236. package/src/session-runtime/services/agent-tool/send-flow.mjs +11 -3
  237. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  238. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  239. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  240. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
  241. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  242. package/src/session-runtime/services/channel-admin.mjs +93 -24
  243. package/src/session-runtime/services/memory-access.mjs +37 -0
  244. package/src/session-runtime/services/provider-admin.mjs +74 -39
  245. package/src/session-runtime/services/schedule-access.mjs +91 -0
  246. package/src/session-runtime/session-create/session-options.mjs +34 -7
  247. package/src/session-runtime/session-hooks.mjs +16 -8
  248. package/src/session-runtime/setup-tool/executor.mjs +79 -6
  249. package/src/session-runtime/setup-tool/extended-actions.mjs +90 -20
  250. package/src/session-runtime/setup-tool/settings-contract.mjs +14 -6
  251. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  252. package/src/session-runtime/skills-api.mjs +16 -1
  253. package/src/session-runtime/tool-policy-refresh.mjs +249 -29
  254. package/src/session-runtime/turn/session-ops.mjs +8 -1
  255. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
  256. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +14 -0
  257. package/src/session-runtime/webhook-session-run.mjs +49 -67
  258. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  259. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +650 -25
  260. package/src/session-runtime/workflow-agents-api/agent-route.mjs +67 -7
  261. package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
  262. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  263. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  264. package/src/standalone/daemon-agent-control.mjs +1 -8
  265. package/src/standalone/daemon-session-control.mjs +21 -4
  266. package/src/standalone/daemon.mjs +31 -5
  267. package/src/standalone/session-protocol.mjs +27 -0
  268. package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
  269. package/src/standalone/session-service/agent-tree.mjs +4 -3
  270. package/src/standalone/session-service/session-calls/session-turns.mjs +28 -0
  271. package/src/standalone/session-service.mjs +19 -0
  272. package/src/tui/app/slash-commands.mjs +20 -0
  273. package/src/tui/dist/index.mjs +234 -95
  274. package/src/tui/session/context-state.mjs +6 -1
  275. package/src/tui/session/notice-surface.mjs +34 -4
  276. package/src/tui/session/queue-helpers.mjs +2 -1
  277. package/src/tui/session/session-api/extensions.mjs +4 -0
  278. package/src/tui/session/session-api/intake/submission.mjs +5 -0
  279. package/src/tui/session/session-api/integrations.mjs +8 -0
  280. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  281. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  282. package/src/tui/session/session-api/routes.mjs +61 -5
  283. package/src/tui/session/session-flow/drain.mjs +12 -3
  284. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  285. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  286. package/src/tui/session/session-flow/steering.mjs +58 -24
  287. package/src/tui/session/tool-approval.mjs +26 -5
  288. package/src/tui/session/transcript-intake.mjs +8 -2
  289. package/src/tui/session/transcript-spill.mjs +5 -1
  290. package/src/tui/session/tui-steering-persist.mjs +65 -42
  291. package/src/tui/session/turn-live-call.mjs +64 -0
  292. package/src/tui/session/turn.mjs +34 -8
  293. package/src/session-runtime/automation-agents.mjs +0 -94
  294. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -54,6 +54,7 @@ import { createResourceApi } from '../resource-api.mjs';
54
54
  import { createModelRouteApi } from '../model-route-api.mjs';
55
55
  import { createWorkflowAgentsApi } from '../workflow-agents-api.mjs';
56
56
  import { createToolPolicyRefresh } from '../tool-policy-refresh.mjs';
57
+ import { subscribeAgentProfileChanges } from '../agent-profile-changes.mjs';
57
58
  import { loadInstructionFilesContext } from '../../runtime/agent/orchestrator/context/instruction-files.mjs';
58
59
  import { createSessionTurnApi } from '../session-turn-api.mjs';
59
60
  import { createGoalFacadeApi } from '../goal-facade-api.mjs';
@@ -66,6 +67,7 @@ import { createConversationImportApi } from '../conversation-import/service.mjs'
66
67
  import { importError } from '../conversation-import/discovery.mjs';
67
68
  import { bootProfile } from '../boot-profile.mjs';
68
69
  import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
70
+ import { registerBotRouteStore } from '../../runtime/shared/agent-store.mjs';
69
71
  import {
70
72
  dataDirOf,
71
73
  resolveRoute,
@@ -110,10 +112,29 @@ export function wireApis(boot) {
110
112
  }
111
113
  boot.disposeGlobalExtensionSubscription = () => {
112
114
  boot.disposeConfigSync?.();
115
+ boot.disposeAgentProfileChanges();
113
116
  boot.resourceApi.disposeGlobalExtensionSubscription?.();
114
117
  };
115
118
  boot.modelRouteApi = modelRouteApiFor(boot);
116
119
  boot.workflowAgentsApi = workflowAgentsApiFor(boot);
120
+ // Connector allow lists (agent lists, MCP project scopes) become the
121
+ // equivalent "switched off" lists once; a later connector is on everywhere.
122
+ if (!boot.params.initialConfig || typeof boot.params.initialConfig !== 'object') {
123
+ try {
124
+ boot.workflowAgentsApi.migrateConnectorPolicies();
125
+ } catch (error) {
126
+ bootProfile('connector-policy:migrate-failed', { error: error?.message || String(error) });
127
+ }
128
+ // Bots get their own store (bots/) once, before any catalog read; a
129
+ // failed split leaves agents/ read as before (bot-split-migration.mjs).
130
+ try {
131
+ boot.workflowAgentsApi.migrateBotStore();
132
+ } catch (error) {
133
+ bootProfile('bot-split:migrate-failed', { error: error?.message || String(error) });
134
+ }
135
+ }
136
+ // Route reads ask a store bot's own folder first (agent-store.mjs).
137
+ registerBotRouteStore(() => dataDirOf(boot.cfgMod));
117
138
  boot.workspaceApi = workspaceApiFor(boot);
118
139
  boot.sessionTurnApi = sessionTurnApiFor(boot);
119
140
  const getFacadeSessionId = () => boot.rt.session?.id || boot.rt.reservedSessionId || null;
@@ -132,24 +153,36 @@ export function wireApis(boot) {
132
153
 
133
154
  function wireToolPolicyRefresh(boot) {
134
155
  const { rt, cfgMod } = boot;
135
- const { refreshEmptySessionToolPolicy } = createToolPolicyRefresh({
136
- getSession: () => rt.session,
137
- getRoute: () => rt.route,
138
- getMode: () => rt.mode,
139
- getConfig: () => rt.config,
140
- getDataDir: () => dataDirOf(cfgMod),
141
- getActiveTurnCount: () => rt.activeTurnCount || 0,
142
- modelStandaloneTools: boot.modelStandaloneTools,
143
- featureDisallowedTools: boot.featureDisallowedTools,
144
- loadCoreMemoryContext: boot.loadCoreMemoryContext,
145
- loadInstructionFilesContext: (session) =>
146
- session.skipInstructionFiles
147
- ? ''
148
- : loadInstructionFilesContext({ mode: rt.config?.instructionFiles, cwd: session.cwd }),
149
- activeWorkflowContext: workflowHelpers.activeWorkflowContext,
150
- invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
151
- });
156
+ const { refreshEmptySessionToolPolicy, switchSessionWorkflow, refreshAgentProfile, applyPendingAgentProfile } =
157
+ createToolPolicyRefresh({
158
+ getSession: () => rt.session,
159
+ getRoute: () => rt.route,
160
+ getMode: () => rt.mode,
161
+ getConfig: () => rt.config,
162
+ getDataDir: () => dataDirOf(cfgMod),
163
+ getActiveTurnCount: () => rt.activeTurnCount || 0,
164
+ modelStandaloneTools: boot.modelStandaloneTools,
165
+ featureDisallowedTools: boot.featureDisallowedTools,
166
+ loadCoreMemoryContext: boot.loadCoreMemoryContext,
167
+ loadInstructionFilesContext: (session) =>
168
+ session.skipInstructionFiles
169
+ ? ''
170
+ : loadInstructionFilesContext({ mode: rt.config?.instructionFiles, cwd: session.cwd }),
171
+ activeWorkflowContext: workflowHelpers.activeWorkflowContext,
172
+ invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
173
+ });
152
174
  boot.refreshEmptySessionToolPolicy = refreshEmptySessionToolPolicy;
175
+ boot.switchSessionWorkflow = switchSessionWorkflow;
176
+ boot.refreshAgentProfile = refreshAgentProfile;
177
+ boot.applyPendingAgentProfile = applyPendingAgentProfile;
178
+ // A saved bot reaches this runtime's thread when its prompt embeds it (it
179
+ // runs as that bot, or lists it); the rebuilt prompt is saved with the session.
180
+ const profileChanges = subscribeAgentProfileChanges(async () => {
181
+ if (!(await refreshAgentProfile())) return;
182
+ saveSession(rt.session);
183
+ boot.invalidateContextStatusCache();
184
+ });
185
+ boot.disposeAgentProfileChanges = profileChanges.unsubscribe;
153
186
  }
154
187
 
155
188
  // Built-in install adapters. Memory downloads and loads the embedding model at
@@ -425,6 +458,7 @@ function lifecycleApiFor(boot) {
425
458
  invalidateContextStatusCache: boot.invalidateContextStatusCache,
426
459
  invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
427
460
  applyResolvedCwd: boot.applyResolvedCwd,
461
+ refreshAgentProfile: boot.refreshAgentProfile,
428
462
  // Created after the lifecycle API, so resolved at call time.
429
463
  cleanupDeletedSessionWorktree: (sessionId) => boot.workspaceApi?.cleanupDeletedSession?.(sessionId),
430
464
  resolveRoute,
@@ -458,7 +492,9 @@ function resourceApiFor(boot) {
458
492
  connectConfiguredMcp: boot.connectConfiguredMcp,
459
493
  cancelMcpLogin: boot.cancelMcpLogin,
460
494
  completeMcpLogin: boot.completeMcpLogin,
461
- clearMcpServerOAuth: (cfg) => boot.mcpClient.clearMcpServerOAuth(cfg),
495
+ clearMcpServerOAuth: (cfg, options) => boot.mcpClient.clearMcpServerOAuth(cfg, options),
496
+ startMcpServerOAuthSwitch: (cfg, launch, name) => boot.mcpClient.startMcpServerOAuthSwitch(cfg, launch, name),
497
+ commitMcpServerOAuthSwitch: (cfg, signIn) => boot.mcpClient.commitMcpServerOAuthSwitch(cfg, signIn),
462
498
  getMcpServerOAuthState: (cfg) => boot.mcpClient.getMcpServerOAuthState(cfg),
463
499
  probeMcpServerAuth: (cfg) => boot.mcpClient.probeMcpServerAuth(cfg),
464
500
  invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
@@ -473,6 +509,7 @@ function resourceApiFor(boot) {
473
509
  skillContent: boot.skillContent,
474
510
  addGlobalSkill: boot.addGlobalSkill,
475
511
  saveSkillDocument: boot.saveSkillDocument,
512
+ removeGlobalSkill: boot.removeGlobalSkill,
476
513
  invalidateSkills: boot.invalidateSkills,
477
514
  getDisabledSkills: () => settingsApi.getDisabledSkills(),
478
515
  setDisabledSkills: (names) => settingsApi.setDisabledSkills(names),
@@ -511,6 +548,8 @@ function modelRouteApiFor(boot) {
511
548
  adoptConfig: boot.adoptConfig,
512
549
  saveConfigAndAdopt: boot.saveConfigAndAdopt,
513
550
  ensureFullConfig: boot.ensureFullConfig,
551
+ reloadFullConfig: boot.reloadFullConfig,
552
+ confirmLeadFill: boot.confirmLeadFill,
514
553
  awaitKeychainPrewarm: boot.awaitKeychainPrewarm,
515
554
  ensureProvidersReady: boot.ensureProvidersReady,
516
555
  persistLeadRoute: boot.persistLeadRoute,
@@ -540,8 +579,12 @@ function workflowAgentsApiFor(boot) {
540
579
  lookupModelMeta: boot.lookupModelMeta,
541
580
  adoptConfig: boot.adoptConfig,
542
581
  saveConfigAndAdopt: boot.saveConfigAndAdopt,
582
+ reloadFullConfig: boot.reloadFullConfig,
543
583
  ensureProvidersReady: boot.ensureProvidersReady,
544
584
  displayConfig: boot.displayConfig,
585
+ mcpStatus: () => boot.mcpStatus(),
586
+ resolveConfiguredMcpCatalog: () => boot.resolveConfiguredMcpCatalog(),
587
+ getMcpTools: () => boot.mcpClient.getMcpTools(rt.mcpScopeId),
545
588
  agentRouteFromConfig,
546
589
  loadAgentDefinition: workflowHelpers.loadAgentDefinition,
547
590
  listCustomAgentIds: workflowHelpers.listCustomAgentIds,
@@ -555,6 +598,7 @@ function workflowAgentsApiFor(boot) {
555
598
  invalidateContextStatusCache: boot.invalidateContextStatusCache,
556
599
  invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
557
600
  refreshEmptySessionToolPolicy: boot.refreshEmptySessionToolPolicy,
601
+ switchSessionWorkflow: boot.switchSessionWorkflow,
558
602
  });
559
603
  }
560
604
 
@@ -596,6 +640,7 @@ function sessionTurnApiFor(boot) {
596
640
  const { rt, tunables, remoteTranscript, routePreparation } = boot;
597
641
  return createSessionTurnApi({
598
642
  applyPendingRoute: () => boot.modelRouteApi.applyPendingRoute(),
643
+ applyPendingAgentProfile: boot.applyPendingAgentProfile,
599
644
  prepareTurnWorkspace: boot.workspaceApi.prepareTurnWorkspace,
600
645
  admitTurnWorkspace: boot.workspaceApi.admitTurnWorkspace,
601
646
  getSession: () => rt.session,
@@ -85,17 +85,14 @@ function reserveSessionId(boot, id) {
85
85
  }
86
86
 
87
87
  // Every session's 2 s status pulse lands here; activeWorkflowSummary reads
88
- // through the process-wide shared caches, so no per-session file I/O.
88
+ // through the process-wide shared caches, so no per-session file I/O. A
89
+ // session presents its own workflow (set at creation, or switched in it);
90
+ // the configured one is the default for new sessions.
89
91
  function currentWorkflow(boot) {
90
92
  const { rt, cfgMod } = boot;
91
93
  const active = workflowHelpers.activeWorkflowSummary(rt.config, dataDirOf(cfgMod));
92
- if (rt.session?.workflow && typeof rt.session.workflow === 'object') {
93
- const current = rt.session.workflow;
94
- return current?.id && active?.id && current.id !== active.id
95
- ? { ...active, currentSession: current, appliedToCurrentSession: false }
96
- : active;
97
- }
98
- return active;
94
+ const current = rt.session?.workflow;
95
+ return current?.id && active?.id && current.id !== active.id ? current : active;
99
96
  }
100
97
 
101
98
  function facadeGetters(boot) {
@@ -14,6 +14,7 @@ import { createSelfUpdateController } from '../self-update.mjs';
14
14
  import { createCompletionWakeScheduler, createNotificationBus } from '../notification-bus.mjs';
15
15
  import { createSkillsApi } from '../skills-api.mjs';
16
16
  import { createCwdPlugins } from '../cwd-plugins.mjs';
17
+ import { injectedMemoryAgentId } from '../services/memory-access.mjs';
17
18
  import { listProjectsStrict } from '../../runtime/shared/projects.mjs';
18
19
  import { isWithin } from '../../runtime/shared/worktrees/paths.mjs';
19
20
  import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
@@ -212,29 +213,37 @@ export function sessionAdopter(boot) {
212
213
  // the mutable cwd, the context module and the live tool surface.
213
214
  function wireSkills(boot) {
214
215
  const { rt, contextMod } = boot;
215
- const { skillsStatus, skillContent, skillToolContent, addGlobalSkill, saveSkillDocument, invalidateSkills } =
216
- createSkillsApi({
217
- contextMod,
218
- getCwd: () => rt.currentCwd,
219
- getTools: () => {
220
- const surface = boot.activeToolSurface();
221
- return [
222
- ...new Map(
223
- [
224
- ...(surface?.tools || []),
225
- ...(surface?.deferredToolCatalog || []),
226
- ...(surface?.deferredLateToolCatalog || []),
227
- ].map((tool) => [tool.name, tool])
228
- ).values(),
229
- ];
230
- },
231
- });
216
+ const {
217
+ skillsStatus,
218
+ skillContent,
219
+ skillToolContent,
220
+ addGlobalSkill,
221
+ saveSkillDocument,
222
+ removeGlobalSkill,
223
+ invalidateSkills,
224
+ } = createSkillsApi({
225
+ contextMod,
226
+ getCwd: () => rt.currentCwd,
227
+ getTools: () => {
228
+ const surface = boot.activeToolSurface();
229
+ return [
230
+ ...new Map(
231
+ [
232
+ ...(surface?.tools || []),
233
+ ...(surface?.deferredToolCatalog || []),
234
+ ...(surface?.deferredLateToolCatalog || []),
235
+ ].map((tool) => [tool.name, tool])
236
+ ).values(),
237
+ ];
238
+ },
239
+ });
232
240
  Object.assign(boot, {
233
241
  skillsStatus,
234
242
  skillContent,
235
243
  skillToolContent,
236
244
  addGlobalSkill,
237
245
  saveSkillDocument,
246
+ removeGlobalSkill,
238
247
  invalidateSkills,
239
248
  });
240
249
  }
@@ -247,6 +256,8 @@ function wireCwdPlugins(boot) {
247
256
  const { rt, cfgMod, hooks, hookCommonPayload, tunables } = boot;
248
257
  const { resolveCwdPath, applyResolvedCwd, pluginsStatus, loadCoreMemoryContext } = createCwdPlugins({
249
258
  getCurrentCwd: () => rt.currentCwd,
259
+ getMemoryAgentId: () =>
260
+ injectedMemoryAgentId({ session: rt.session, sessionId: rt.reservedSessionId, profile: rt.sessionProfile }),
250
261
  setCurrentCwd: (next) => {
251
262
  rt.currentCwd = next;
252
263
  },
@@ -11,7 +11,10 @@ import { LOCAL_PROVIDER_ID, configureLocalProviderIdleTtl } from '../../runtime/
11
11
  import { resolve } from 'node:path';
12
12
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
13
13
  import { LAZY_SECRET_PROVIDERS } from '../../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
14
- import { ensureProviderEnabled, normalizeSystemShellConfig } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
14
+ import {
15
+ ensureProviderEnabled,
16
+ normalizeSystemShellConfig,
17
+ } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
15
18
  import {
16
19
  workflowPresetId,
17
20
  normalizeWebSearchProviderId,
@@ -33,7 +36,7 @@ import { createProviderModels } from '../provider-models.mjs';
33
36
  import { createProviderUsage } from '../provider-usage.mjs';
34
37
  import { bootProfile } from '../boot-profile.mjs';
35
38
  import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
36
- import { outputStyleStatus, webSearchCapableFor } from './shared.mjs';
39
+ import { outputStyleStatus, resolveRoute, webSearchCapableFor } from './shared.mjs';
37
40
 
38
41
  export function wireProviders(boot) {
39
42
  wireConfigLifecycle(boot);
@@ -63,6 +66,10 @@ function wireConfigLifecycle(boot) {
63
66
  rt.configHasSecrets = next;
64
67
  },
65
68
  getRoute: () => rt.route,
69
+ setRouteState: (next) => {
70
+ rt.route = next;
71
+ },
72
+ resolveRoute,
66
73
  cfgMod,
67
74
  sharedCfgMod,
68
75
  setConfiguredShell,
@@ -82,6 +89,7 @@ function wireConfigLifecycle(boot) {
82
89
  seedOutputStyleStatusCache,
83
90
  adoptConfig,
84
91
  saveConfigAndAdopt,
92
+ confirmLeadFill,
85
93
  scheduleSkillsSave,
86
94
  flushSkillsSave,
87
95
  scheduleOutputStyleSave,
@@ -98,6 +106,7 @@ function wireConfigLifecycle(boot) {
98
106
  seedOutputStyleStatusCache,
99
107
  adoptConfig,
100
108
  saveConfigAndAdopt,
109
+ confirmLeadFill,
101
110
  scheduleSkillsSave,
102
111
  flushSkillsSave,
103
112
  scheduleOutputStyleSave,
@@ -106,13 +115,14 @@ function wireConfigLifecycle(boot) {
106
115
  ensureFullConfig,
107
116
  displayConfig,
108
117
  ensureConfigForRouteProvider,
109
- persistLeadRoute: (routeLike) => {
118
+ persistLeadRoute: (routeLike, { onlyIfAbsent = false } = {}) => {
110
119
  const leadRoute = normalizeWorkflowRoute(routeLike);
111
120
  if (!leadRoute) return null;
112
121
  const nextConfig = { ...(rt.config || {}) };
113
122
  nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, 'lead', leadRoute);
114
123
  nextConfig.default = workflowPresetId('lead');
115
- saveConfigAndAdopt(nextConfig);
124
+ // A conditional fill reaches the file only while no Lead route is stored.
125
+ saveConfigAndAdopt(nextConfig, { leadFill: onlyIfAbsent });
116
126
  return leadRoute;
117
127
  },
118
128
  });
@@ -225,6 +225,8 @@ function wireInternalTools(boot) {
225
225
  getConfig: () => rt.config,
226
226
  notifySessionUi,
227
227
  getSessionId: () => rt.session?.id || rt.reservedSessionId || null,
228
+ getCallerSession: (sessionId) =>
229
+ sessionId === rt.session?.id ? rt.session : boot.mgr?.getSession?.(sessionId) || null,
228
230
  flushSettings: () => boot.flushAllConfigSavesAsync({ requireSaved: true }),
229
231
  });
230
232
  const disposeInternalTools = internalTools.setInternalToolsProvider({
@@ -250,6 +252,7 @@ function wireInternalTools(boot) {
250
252
  runNativeWebSearch: (...a) => boot.runNativeWebSearch(...a),
251
253
  activeToolSurface: boot.activeToolSurface,
252
254
  mcpStatus: boot.mcpStatus,
255
+ awaitMcpConnect: () => boot.awaitInitialMcpConnect?.(),
253
256
  skillToolContent: boot.skillToolContent,
254
257
  // Late-bound: the workspace authority is built with the API slices.
255
258
  getWorkspaceApi: () => boot.workspaceApi || null,
@@ -10,8 +10,18 @@ import {
10
10
  setWebhookConfigAsync,
11
11
  testWebhook,
12
12
  } from './services/channel-admin.mjs';
13
- import { getSchedule } from '../runtime/channels/lib/schedules-db.mjs';
13
+ import { getSchedule, markScheduleFailure, markScheduleSuccess } from '../runtime/channels/lib/schedules-db.mjs';
14
+ import { loadEndpointConfig, readEndpointLastPayload } from '../runtime/channels/lib/webhooks-db.mjs';
15
+ import { webhookEventSummary, webhookRunPrompt } from '../runtime/channels/lib/webhook/session-dispatch.mjs';
14
16
  import { runScheduleSession } from './schedule-session-run.mjs';
17
+ import { runWebhookSession } from './webhook-session-run.mjs';
18
+ import { retryRoutineDelivery } from './owned-automation-run.mjs';
19
+
20
+ /** Why a "Run now" did not run: its owner bot is missing, or a run is going. */
21
+ function skippedRunMessage(name, outcome) {
22
+ if (outcome.reason === 'owner') return `"${name}" is paused: ${outcome.message}`;
23
+ return `"${name}" is still running; this run was skipped`;
24
+ }
15
25
 
16
26
  // Webhook/schedule config surface. The mutating admin helpers are imported
17
27
  // directly here and the runtime injects only the closure-owned callbacks
@@ -78,10 +88,48 @@ export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAut
78
88
  const id = String(name || '').trim();
79
89
  const schedule = await getSchedule(id);
80
90
  if (!schedule) throw new Error(`schedule "${id}" does not exist`);
81
- // Runs in the engine process as a VISIBLE schedule session, so it works
82
- // even while the channels worker is off and the run lands in Recent.
83
- const { sessionId, result } = await runScheduleSession(schedule);
84
- return { name: schedule.name, ok: true, sessionId, result };
91
+ // Runs in the engine process as a turn in the owner bot's chat, so it
92
+ // works even while the channels worker is off.
93
+ let outcome;
94
+ try {
95
+ // Asked for by a person: no failure backoff.
96
+ outcome = await runScheduleSession(schedule, { attended: true });
97
+ } catch (error) {
98
+ await markScheduleFailure(schedule.name, new Date()).catch(() => {});
99
+ throw error;
100
+ }
101
+ if (outcome.skipped) throw new Error(skippedRunMessage(schedule.name, outcome));
102
+ // The run's outcome shows as the schedule's last run, like a timed fire.
103
+ await markScheduleSuccess(schedule.name, new Date()).catch(() => {});
104
+ return { name: schedule.name, ok: true, sessionId: outcome.sessionId, result: outcome.result };
105
+ },
106
+ // Re-runs the endpoint on its latest received payload, with its current
107
+ // instructions, behind the same untrusted-data fence as a live delivery.
108
+ async runWebhookNow(name) {
109
+ const id = String(name || '').trim();
110
+ const endpoint = await loadEndpointConfig(id);
111
+ if (!endpoint) throw new Error(`webhook "${id}" does not exist`);
112
+ const payload = await readEndpointLastPayload(id);
113
+ if (!payload) throw new Error(`webhook "${id}" has not received a payload yet`);
114
+ const run = webhookRunPrompt(endpoint.instructions, payload.body ?? {}, payload.headers ?? {});
115
+ const outcome = await runWebhookSession(
116
+ {
117
+ name: endpoint.name,
118
+ ownerAgentId: endpoint.ownerAgentId,
119
+ cwd: endpoint.cwd || null,
120
+ attachments: endpoint.attachments || null,
121
+ event: webhookEventSummary(payload.headers ?? {}, payload.body ?? {}),
122
+ prompt: run.prompt,
123
+ skillSelection: run.skillSelection,
124
+ },
125
+ { attended: true }
126
+ );
127
+ if (outcome.skipped) throw new Error(skippedRunMessage(endpoint.name, outcome));
128
+ return { name: endpoint.name, ok: true, sessionId: outcome.sessionId, result: outcome.result };
129
+ },
130
+ // Queues again the result a finished run could not deliver; never reruns it.
131
+ async retryRoutineDelivery(kind, name) {
132
+ return retryRoutineDelivery(kind, String(name || '').trim());
85
133
  },
86
134
  };
87
135
  }
@@ -79,6 +79,9 @@ export function createConfigLifecycle({
79
79
  getConfigHasSecrets,
80
80
  setConfigHasSecrets,
81
81
  getRoute,
82
+ // Main route sync on reload (both optional: a lifecycle without them never syncs)
83
+ setRouteState,
84
+ resolveRoute,
82
85
  // shared modules / helpers
83
86
  cfgMod,
84
87
  sharedCfgMod,
@@ -121,16 +124,42 @@ export function createConfigLifecycle({
121
124
  // separate from adoptConfig: model tuning is adopted before its route save.
122
125
  let configSaveBaseline = structuredClone(getConfig());
123
126
 
124
- function saveConfigAndAdopt(nextConfig, { hasSecrets = getConfigHasSecrets() } = {}) {
127
+ // The Lead route lives in these two stored keys; a conditional fill writes
128
+ // them only while `default` is still unset on disk, judged under the config
129
+ // file lock when the patch is applied (config-patch.mjs `unlessSet`).
130
+ const LEAD_FILL_KEYS = new Set(['default', 'presets']);
131
+
132
+ function saveConfigAndAdopt(nextConfig, { hasSecrets = getConfigHasSecrets(), leadFill = false } = {}) {
125
133
  // In-memory adopt is synchronous and first so callers that read back the
126
134
  // value immediately (e.g. setProfile -> getProfile) see the new state.
127
135
  const adopted = adoptConfig(nextConfig, { hasSecrets });
128
- const changes = cfgMod.createConfigPatch(configSaveBaseline, adopted);
136
+ const changes = cfgMod
137
+ .createConfigPatch(configSaveBaseline, adopted)
138
+ .map((change) =>
139
+ leadFill && LEAD_FILL_KEYS.has(change.path[0]) ? { ...change, unlessSet: ['default'] } : change
140
+ );
129
141
  configSaveBaseline = structuredClone(adopted);
130
142
  if (changes.length) writers.queueConfigChanges([...changes, ...seedChanges(adopted, changes)]);
131
143
  return adopted;
132
144
  }
133
145
 
146
+ const sameModel = (a, b) => clean(a?.provider) === clean(b?.provider) && clean(a?.model) === clean(b?.model);
147
+
148
+ /** After a conditional Lead fill adopted `filled`: persist it now and, when
149
+ * the file already held another Lead route (written by another process after
150
+ * the fill's re-read, so the patch skipped it), adopt that one instead.
151
+ * Answers whether the runtime route was replaced. A route the user has moved
152
+ * off `filled` meanwhile stays. */
153
+ async function confirmLeadFill(filled) {
154
+ await writers.flushConfigSaveAsync();
155
+ reloadFullConfig();
156
+ if (!setRouteState || !resolveRoute || !sameModel(getRoute(), filled)) return false;
157
+ const winner = resolveRoute(getConfig(), {});
158
+ if (!clean(winner?.model) || sameModel(winner, filled)) return false;
159
+ setRouteState(winner);
160
+ return true;
161
+ }
162
+
134
163
  // A fresh profile's install markers (an empty `builtins`) equal the
135
164
  // defaults, so no diff ever carries them — yet on disk their absence reads
136
165
  // as a grandfathered profile. The first write seeds them where the stored
@@ -177,9 +206,20 @@ export function createConfigLifecycle({
177
206
  }
178
207
  const adopted = adoptConfig(next, { hasSecrets: true });
179
208
  configSaveBaseline = structuredClone(adopted);
209
+ syncEmptyMainRoute(adopted);
180
210
  return adopted;
181
211
  }
182
212
 
213
+ // A Main route the runtime still has no model for takes the Lead route a
214
+ // reload brings in (edited on disk or by another process), so a conditional
215
+ // backfill sees it and never overwrites it. A live route that has a model is
216
+ // the user's selection and stays.
217
+ function syncEmptyMainRoute(config) {
218
+ if (!setRouteState || !resolveRoute || clean(getRoute()?.model)) return;
219
+ const resolved = resolveRoute(config, {});
220
+ if (clean(resolved?.model)) setRouteState(resolved);
221
+ }
222
+
183
223
  function ensureFullConfig() {
184
224
  if (getConfigHasSecrets()) return getConfig();
185
225
  return reloadFullConfig();
@@ -211,6 +251,7 @@ export function createConfigLifecycle({
211
251
  // adopt / save
212
252
  adoptConfig,
213
253
  saveConfigAndAdopt,
254
+ confirmLeadFill,
214
255
  // Skills publication also drains older config edits first.
215
256
  flushSkillsSave: writers.flushConfigSaveAsync,
216
257
  scheduleSkillsSave,
@@ -1,6 +1,10 @@
1
1
  // cwd-plugins/core-memory-context.mjs — the user-curated core-memory block
2
- // injected into new sessions, read from the memory runtime's atomic snapshot.
2
+ // injected into new sessions, read from the memory runtime's atomic snapshot:
3
+ // common, then the project's, then the session's own agent memory
4
+ // (services/memory-access.mjs injectedMemoryAgentId). The same snapshot gives
5
+ // the same bytes, so an unchanged pool never invalidates the prompt cache.
3
6
  import { featureEnvOverride } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
7
+ import { clean as cleanText } from '../../runtime/shared/clean.mjs';
4
8
  import { readSessionCoreMemoryPayload } from '../../runtime/memory/lib/core-memory-file.mjs';
5
9
 
6
10
  // Budget for the whole injected block in UTF-8 bytes: bytes track token cost
@@ -8,25 +12,37 @@ import { readSessionCoreMemoryPayload } from '../../runtime/memory/lib/core-memo
8
12
  const CORE_MEMORY_MAX_BYTES = 32 * 1024;
9
13
  const TRUNCATED_MARK = '\n- ...';
10
14
 
11
- export function createCoreMemoryContext({ getCurrentCwd, bootProfile, clean, cfgMod, STANDALONE_DATA_DIR }) {
12
- function formatCoreMemoryLines(payload = {}) {
13
- const seen = new Set();
14
- const lines = [];
15
- for (const value of Array.isArray(payload.userLines) ? payload.userLines : []) {
16
- const text = clean(value).replace(/\s+/g, ' ');
17
- if (!text) continue;
18
- const key = text.toLowerCase();
19
- if (seen.has(key)) continue;
20
- seen.add(key);
21
- lines.push(`- ${text}`);
22
- }
23
- const out = lines.join('\n');
24
- if (Buffer.byteLength(out, 'utf8') <= CORE_MEMORY_MAX_BYTES) return out;
25
- const budget = CORE_MEMORY_MAX_BYTES - Buffer.byteLength(TRUNCATED_MARK, 'utf8');
26
- // A byte cut can split a multi-byte character; drop its replacement chars.
27
- const head = Buffer.from(out, 'utf8').subarray(0, budget).toString('utf8').replace(/\uFFFD+$/, '');
28
- return `${head.replace(/\s+\S*$/, '')}${TRUNCATED_MARK}`;
15
+ function formatCoreMemoryBlock(payload = {}, clean = cleanText) {
16
+ const seen = new Set();
17
+ const lines = [];
18
+ for (const value of Array.isArray(payload.userLines) ? payload.userLines : []) {
19
+ const text = clean(value).replace(/\s+/g, ' ');
20
+ if (!text) continue;
21
+ const key = text.toLowerCase();
22
+ if (seen.has(key)) continue;
23
+ seen.add(key);
24
+ lines.push(`- ${text}`);
29
25
  }
26
+ const out = lines.join('\n');
27
+ if (Buffer.byteLength(out, 'utf8') <= CORE_MEMORY_MAX_BYTES) return out;
28
+ const budget = CORE_MEMORY_MAX_BYTES - Buffer.byteLength(TRUNCATED_MARK, 'utf8');
29
+ // A byte cut can split a multi-byte character; drop its replacement chars.
30
+ const head = Buffer.from(out, 'utf8')
31
+ .subarray(0, budget)
32
+ .toString('utf8')
33
+ .replace(/\uFFFD+$/, '');
34
+ return `${head.replace(/\s+\S*$/, '')}${TRUNCATED_MARK}`;
35
+ }
36
+
37
+ export function createCoreMemoryContext({
38
+ getCurrentCwd,
39
+ getMemoryAgentId = () => '',
40
+ bootProfile,
41
+ clean,
42
+ cfgMod,
43
+ STANDALONE_DATA_DIR,
44
+ }) {
45
+ const formatCoreMemoryLines = (payload) => formatCoreMemoryBlock(payload, clean);
30
46
 
31
47
  async function loadCoreMemoryContext() {
32
48
  // User-curated core memory always injects into new sessions: the Memory
@@ -42,7 +58,7 @@ export function createCoreMemoryContext({ getCurrentCwd, bootProfile, clean, cfg
42
58
  // The prompt path never starts or waits for PG/embedding/IPC. Memory
43
59
  // runtime maintains this atomic snapshot independently.
44
60
  const dataDir = process.env.MIXDOG_DATA_DIR || cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
45
- const payload = readSessionCoreMemoryPayload(dataDir, getCurrentCwd());
61
+ const payload = readSessionCoreMemoryPayload(dataDir, getCurrentCwd(), getMemoryAgentId());
46
62
  if (!payload) {
47
63
  bootProfile('core-memory:file-missing');
48
64
  return '';
@@ -68,6 +68,10 @@ export function createPluginStatus({
68
68
  const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
69
69
  const { mcpScript, mcpInline } = discoverPluginMcp(root);
70
70
  const skillCount = countSkillFiles(root);
71
+ // The hook files the hook bus loads for a plugin (hook-bus/config.mjs).
72
+ const hooks = [resolve(root, 'hooks', 'hooks.json'), resolve(root, '.mixdog', 'hooks.json')].some((file) =>
73
+ existsSync(file)
74
+ );
71
75
  const plugin = {
72
76
  id: clean(entry.id) || name,
73
77
  name,
@@ -85,11 +89,12 @@ export function createPluginStatus({
85
89
  installedAt: entry.installedAt || null,
86
90
  updatedAt: entry.updatedAt || null,
87
91
  skillCount,
92
+ hooks,
88
93
  mcpScript,
89
94
  mcpInline,
90
95
  };
91
96
  if (invalid) plugin.invalid = true;
92
- if (plugin.enabled && skillCount === 0 && !mcpScript && !mcpInline) {
97
+ if (plugin.enabled && skillCount === 0 && !mcpScript && !mcpInline && !hooks) {
93
98
  plugin.searchedSkillRoots = pluginSkillsRoots(root);
94
99
  plugin.warning = `No skills or MCP server found. Searched skill roots: ${plugin.searchedSkillRoots.join(', ')}`;
95
100
  }