mixdog 1.3.4 → 1.4.0

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 (472) hide show
  1. package/LICENSES/oh-my-pi-MIT.txt +23 -0
  2. package/NOTICE.md +17 -2
  3. package/README.md +11 -0
  4. package/package.json +3 -11
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/embed-google-oauth-client.mjs +49 -0
  7. package/scripts/gate-local.mjs +4 -2
  8. package/scripts/lib/test-spawn-ledger.mjs +46 -1
  9. package/scripts/native-release-status.mjs +2 -2
  10. package/scripts/recall-history-cases.json +1 -1
  11. package/scripts/suite-health.mjs +10 -1
  12. package/scripts/test.mjs +12 -11
  13. package/src/agents/reviewer/AGENT.md +7 -11
  14. package/src/agents/reviewer/agent.json +1 -1
  15. package/src/defaults/shipped-history.json +25 -1
  16. package/src/defaults/skills/browser-use/SKILL.md +77 -17
  17. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  18. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  19. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  20. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  21. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  22. package/src/defaults/skills/setup/SKILL.md +29 -2
  23. package/src/defaults/skills/setup/references/actions.md +117 -28
  24. package/src/defaults/skills/setup/references/surfaces.md +77 -35
  25. package/src/defaults/skills/setup-guide/SKILL.md +3 -3
  26. package/src/defaults/skills/visualize/SKILL.md +2 -1
  27. package/src/lib/keychain-cjs.cjs +359 -29
  28. package/src/lib/rules-builder.cjs +10 -8
  29. package/src/rules/agent/AGENT.md +6 -6
  30. package/src/rules/agent-mode/AGENT_MODE.md +10 -2
  31. package/src/rules/agent-mode/MIXY.md +12 -0
  32. package/src/rules/lead/LEAD.md +18 -20
  33. package/src/rules/shared/05-tool-calls.md +5 -6
  34. package/src/rules/shared/10-tool-workflow.md +2 -0
  35. package/src/rules/shared/60-verification.md +7 -5
  36. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  37. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
  38. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  39. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/config.mjs +34 -0
  41. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  42. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  43. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  44. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  45. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
  46. package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
  49. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
  50. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
  51. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
  53. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
  54. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
  55. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
  56. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  60. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
  61. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  63. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
  65. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
  66. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
  67. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
  68. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
  69. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
  70. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  71. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
  73. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
  74. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  75. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
  76. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
  77. package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
  78. package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
  79. package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
  81. package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
  82. package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
  83. package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
  84. package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
  85. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
  86. package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
  87. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
  88. package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
  89. package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
  90. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
  91. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
  92. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
  93. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
  94. package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
  95. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
  97. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  98. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
  99. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  100. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  101. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  109. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  113. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
  114. package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
  115. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  116. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  117. package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
  118. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  119. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  120. package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
  121. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  122. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
  127. package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
  128. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  131. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  132. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
  133. package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
  134. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
  135. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
  136. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
  137. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  138. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
  139. package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
  140. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  141. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
  142. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  143. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  144. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
  145. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
  146. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
  147. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  148. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
  149. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  151. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  152. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
  153. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
  154. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
  155. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  156. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
  157. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  158. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
  159. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  163. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
  164. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
  165. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  166. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  167. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
  168. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  172. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  173. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  174. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  175. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  176. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  177. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  178. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  179. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  180. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
  182. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  183. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  184. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  185. package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
  186. package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
  187. package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
  188. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  189. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  191. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
  192. package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
  193. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  203. package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  207. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  208. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  209. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
  214. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
  216. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  217. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  218. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  219. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  225. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  226. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  227. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  228. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  229. package/src/runtime/browser-bridge/client.mjs +73 -8
  230. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  231. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  232. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  233. package/src/runtime/channels/lib/config.mjs +6 -61
  234. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  235. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  236. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  237. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  238. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  239. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  240. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  241. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  242. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  243. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  244. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  245. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  246. package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
  247. package/src/runtime/channels/lib/scheduler.mjs +135 -154
  248. package/src/runtime/channels/lib/schedules-db.mjs +83 -11
  249. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  250. package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
  251. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  252. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  253. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  254. package/src/runtime/channels/lib/worker-main.mjs +19 -34
  255. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  256. package/src/runtime/computer-bridge/actions.mjs +9 -1
  257. package/src/runtime/computer-bridge/client.mjs +180 -8
  258. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  259. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  260. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  261. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  262. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  263. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  264. package/src/runtime/connectors/google-mcp.mjs +10 -12
  265. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  266. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  268. package/src/runtime/office/com/com-adapter.mjs +17 -1
  269. package/src/runtime/office/com/office-com-host.ps1 +51 -1
  270. package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
  271. package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
  272. package/src/runtime/office/pdf/document-preview.mjs +326 -8
  273. package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
  274. package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
  275. package/src/runtime/office/portable/portable-soffice.mjs +45 -5
  276. package/src/runtime/session-search/session-search-db.mjs +73 -9
  277. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  278. package/src/runtime/shared/abort-race.mjs +89 -0
  279. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  280. package/src/runtime/shared/agent-kind.mjs +4 -3
  281. package/src/runtime/shared/bot-hops.mjs +62 -2
  282. package/src/runtime/shared/bounded-download.mjs +8 -3
  283. package/src/runtime/shared/config.mjs +51 -35
  284. package/src/runtime/shared/connector-accounts.mjs +20 -10
  285. package/src/runtime/shared/connector-catalog.d.mts +33 -2
  286. package/src/runtime/shared/connector-catalog.mjs +166 -8
  287. package/src/runtime/shared/connector-triggers.mjs +22 -19
  288. package/src/runtime/shared/defaults-separation.mjs +2 -14
  289. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  290. package/src/runtime/shared/developer-options.mjs +32 -1
  291. package/src/runtime/shared/err-text.mjs +24 -15
  292. package/src/runtime/shared/file-permissions.d.mts +0 -8
  293. package/src/runtime/shared/file-permissions.mjs +0 -11
  294. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  295. package/src/runtime/shared/google-oauth-client.generated.json +4 -0
  296. package/src/runtime/shared/google-oauth-client.mjs +34 -9
  297. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  298. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  299. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  300. package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
  301. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
  302. package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
  303. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  304. package/src/runtime/shared/llm/well-formed.mjs +66 -0
  305. package/src/runtime/shared/mixy-presence.mjs +22 -0
  306. package/src/runtime/shared/orchestration.d.mts +2 -0
  307. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  308. package/src/runtime/shared/profile-config.cjs +1 -1
  309. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  310. package/src/runtime/shared/prompt-origin.mjs +21 -0
  311. package/src/runtime/shared/provider-accounts.mjs +231 -59
  312. package/src/runtime/shared/provider-api-key.mjs +19 -8
  313. package/src/runtime/shared/provider-display-name.mjs +29 -0
  314. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  315. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  316. package/src/runtime/shared/routine-trigger.mjs +52 -0
  317. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  318. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  319. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  320. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  321. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  322. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  323. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  324. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  325. package/src/runtime/shared/transcript-metadata.mjs +38 -0
  326. package/src/runtime/shared/user-data-guard.mjs +30 -2
  327. package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
  328. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  329. package/src/runtime/terminal-bridge/client.mjs +10 -1
  330. package/src/runtime/visualize-bridge/client.mjs +27 -8
  331. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  332. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  333. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  334. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  335. package/src/session-runtime/agent-home-delivery.mjs +486 -179
  336. package/src/session-runtime/agent-room-delivery.mjs +28 -1
  337. package/src/session-runtime/agent-task-sessions.mjs +561 -199
  338. package/src/session-runtime/boot/apis.mjs +9 -0
  339. package/src/session-runtime/boot/begin.mjs +3 -2
  340. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  341. package/src/session-runtime/boot/providers.mjs +3 -0
  342. package/src/session-runtime/boot/tools.mjs +8 -1
  343. package/src/session-runtime/bot-chain.mjs +32 -0
  344. package/src/session-runtime/bot-status.mjs +7 -4
  345. package/src/session-runtime/channel-config-api.mjs +63 -17
  346. package/src/session-runtime/config-lifecycle.mjs +6 -0
  347. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  348. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  349. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  350. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
  351. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
  352. package/src/session-runtime/internal-tool-executor.mjs +4 -0
  353. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  354. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  355. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  356. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  357. package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
  358. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  359. package/src/session-runtime/mixy-heartbeat.mjs +927 -0
  360. package/src/session-runtime/model-route/route-tuning.mjs +10 -6
  361. package/src/session-runtime/notification-bus.mjs +31 -117
  362. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  363. package/src/session-runtime/notification-listeners.mjs +76 -0
  364. package/src/session-runtime/owned-automation-run.mjs +35 -13
  365. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  366. package/src/session-runtime/provider-auth/login.mjs +7 -0
  367. package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
  368. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  369. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  370. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  371. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  372. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  373. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  374. package/src/session-runtime/routine-home-turn.mjs +33 -6
  375. package/src/session-runtime/routine-owner.mjs +40 -9
  376. package/src/session-runtime/schedule-session-run.mjs +7 -3
  377. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  378. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
  379. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  380. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  381. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  382. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  383. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  384. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  385. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  386. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  387. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  388. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  389. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  390. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  391. package/src/session-runtime/services/channel-admin.mjs +73 -31
  392. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  393. package/src/session-runtime/services/channel-worker.mjs +1 -1
  394. package/src/session-runtime/services/provider-admin.mjs +128 -25
  395. package/src/session-runtime/services/schedule-access.mjs +14 -0
  396. package/src/session-runtime/services/seeds.mjs +0 -28
  397. package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
  398. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  399. package/src/session-runtime/session-create/session-options.mjs +8 -4
  400. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  401. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  402. package/src/session-runtime/settings-system-api.mjs +28 -1
  403. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  404. package/src/session-runtime/setup-tool/executor.mjs +187 -29
  405. package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
  406. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  407. package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
  408. package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
  409. package/src/session-runtime/task-session-route.mjs +99 -0
  410. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  411. package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
  412. package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
  413. package/src/session-runtime/tool-surface.mjs +4 -0
  414. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  415. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
  416. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  417. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
  418. package/src/session-runtime/workspace/transitions.mjs +10 -2
  419. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  420. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  421. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  422. package/src/standalone/channel-transport.mjs +1 -1
  423. package/src/standalone/daemon-channels-loader.mjs +10 -8
  424. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  425. package/src/standalone/daemon-session-control/request.mjs +51 -0
  426. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  427. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  428. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  429. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  430. package/src/standalone/daemon-session-control.mjs +161 -157
  431. package/src/standalone/daemon.mjs +33 -2
  432. package/src/standalone/session-client.mjs +7 -3
  433. package/src/standalone/session-protocol.mjs +10 -0
  434. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
  435. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  436. package/src/standalone/session-service/quota-resume.mjs +3 -0
  437. package/src/standalone/session-service/retention/busy.mjs +65 -1
  438. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  439. package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
  440. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  441. package/src/standalone/session-service.mjs +13 -0
  442. package/src/standalone/session-tool-output.mjs +21 -10
  443. package/src/tui/app/model-options.mjs +4 -27
  444. package/src/tui/dist/index.mjs +81 -65
  445. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  446. package/src/tui/session/initial-state.mjs +3 -0
  447. package/src/tui/session/notice-surface.mjs +23 -1
  448. package/src/tui/session/queue-helpers.mjs +3 -42
  449. package/src/tui/session/session-api/integrations.mjs +1 -0
  450. package/src/tui/session/session-api/lifecycle.mjs +8 -0
  451. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  452. package/src/tui/session/session-api/routes-write.mjs +5 -1
  453. package/src/tui/session/session-api/routes.mjs +1 -0
  454. package/src/tui/session/session-api/settings.mjs +30 -2
  455. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  456. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  457. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  458. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  459. package/src/tui/session/session-flow/drain.mjs +118 -149
  460. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  461. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  462. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  463. package/src/tui/session/tool-approval-request.mjs +39 -0
  464. package/src/tui/session/tool-approval-settle.mjs +62 -0
  465. package/src/tui/session/tool-approval.mjs +69 -115
  466. package/src/tui/session/tui-steering-persist.mjs +8 -1
  467. package/src/tui/session/turn-stream.mjs +36 -3
  468. package/src/tui/session/turn.mjs +23 -2
  469. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  470. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  471. package/scripts/code-graph-description-contract.mjs +0 -188
  472. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -107,8 +107,17 @@ export function wireApis(boot) {
107
107
  try {
108
108
  boot.resourceApi.migratePluginMcpCopies();
109
109
  } catch (error) {
110
+ console.warn(`[mcp] adopting plugin MCP copies failed: ${error?.message || error}`);
110
111
  bootProfile('plugin-mcp:adopt-failed', { error: error?.message || String(error) });
111
112
  }
113
+ // One server per multi-account connector: `gmail`, `gmail-2`… merge into
114
+ // `gmail` holding the accounts (their sign-ins and app passwords stay).
115
+ try {
116
+ boot.resourceApi.migrateConnectorAccounts();
117
+ } catch (error) {
118
+ console.warn(`[connectors] merging connector accounts failed: ${error?.message || error}`);
119
+ bootProfile('connector-accounts:merge-failed', { error: error?.message || String(error) });
120
+ }
112
121
  }
113
122
  boot.disposeGlobalExtensionSubscription = () => {
114
123
  boot.disposeConfigSync?.();
@@ -8,7 +8,7 @@ import { createProviderReadiness } from '../provider-readiness.mjs';
8
8
  import { createRoutePreparationGate } from '../route-preparation.mjs';
9
9
  import { createRuntimeFeatureGates } from '../runtime-feature-gates.mjs';
10
10
  import { createLazyRuntimeModules } from '../runtime-modules.mjs';
11
- import { normalizeToolProfile } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
11
+ import { sessionToolProfile } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
12
12
 
13
13
  // The boot record: `rt` (shared mutable runtime state, promoted from closure
14
14
  // `let`s so extracted modules read/write live values through one reference),
@@ -18,13 +18,14 @@ import { normalizeToolProfile } from '../../runtime/agent/orchestrator/runtime-c
18
18
  export function beginBoot(params) {
19
19
  const { provider, model, toolMode, cwd, toolProfile, approvalMode, disallowDelegation } = params;
20
20
  const rt = {};
21
- rt.toolProfile = normalizeToolProfile(toolProfile);
22
21
  rt.approvalMode = approvalMode === 'implicit' ? 'implicit' : null;
23
22
  rt.disallowDelegation = disallowDelegation === true;
24
23
  rt.mcpScopeId = randomUUID();
25
24
  rt.desktopSession = params.desktopSession;
26
25
  rt.sessionProfile =
27
26
  params.sessionProfile && typeof params.sessionProfile === 'object' ? { ...params.sessionProfile } : null;
27
+ // A heartbeat run's session is fenced to its few tools (tool-profile.mjs).
28
+ rt.toolProfile = sessionToolProfile({ toolProfile, sessionProfile: rt.sessionProfile });
28
29
  bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
29
30
  // Last assistant text handed to the transcript writer (via onAssistantText),
30
31
  // so the post-turn final-content append can skip an exact duplicate.
@@ -20,6 +20,7 @@ import { listProjectsStrict } from '../../runtime/shared/projects.mjs';
20
20
  import { isWithin } from '../../runtime/shared/worktrees/paths.mjs';
21
21
  import { standaloneDataDir } from '../runtime-paths.mjs';
22
22
  import { SessionAddressError, isAdmissionOpen, ownerLifecycle } from '../session-address.mjs';
23
+ import { syncSessionCompaction, usesInjectedConfig } from '../session-compaction-sync.mjs';
23
24
  import { dataDirOf } from './shared.mjs';
24
25
 
25
26
  export function wireInfrastructure(boot) {
@@ -187,6 +188,9 @@ export function sessionAdopter(boot) {
187
188
  }
188
189
  }
189
190
  rt.session = v;
191
+ // A resumed or swapped-in session keeps the compaction it was saved with
192
+ // unless it follows the current global settings (not on an injected config).
193
+ if (v?.id && !usesInjectedConfig(boot.params)) syncSessionCompaction(v, rt.config);
190
194
  // The PreToolUse/PostToolUse bridge is a non-enumerable property that
191
195
  // neither the store nor a resumed record carries: every session installed
192
196
  // here (create, resume, route swap, clear, the manager's copy) gets it, so
@@ -31,6 +31,7 @@ import {
31
31
  } from '../model-recency.mjs';
32
32
  import { createNativeWebSearch } from '../native-web-search.mjs';
33
33
  import { createConfigLifecycle } from '../config-lifecycle.mjs';
34
+ import { usesInjectedConfig } from '../session-compaction-sync.mjs';
34
35
  import { createQuickModelRows } from '../quick-model-rows.mjs';
35
36
  import { createProviderModels } from '../provider-models.mjs';
36
37
  import { createProviderUsage } from '../provider-usage.mjs';
@@ -66,6 +67,8 @@ function wireConfigLifecycle(boot) {
66
67
  rt.configHasSecrets = next;
67
68
  },
68
69
  getRoute: () => rt.route,
70
+ getSession: () => rt.session,
71
+ fixedSessionCompaction: usesInjectedConfig(boot.params),
69
72
  setRouteState: (next) => {
70
73
  rt.route = next;
71
74
  },
@@ -9,6 +9,7 @@ import {
9
9
  channelNotificationSessionId,
10
10
  } from '../../runtime/shared/channel-notification-routing.mjs';
11
11
  import { CHANNEL_NOTIFICATION_METHOD } from '../services/channel-session-router.mjs';
12
+ import { PROMPT_ORIGIN_CHANNEL } from '../../runtime/agent/orchestrator/session/manager/pending-message-entry.mjs';
12
13
  import { envFlag } from '../../runtime/shared/env.mjs';
13
14
  // Desktop-app bridges: tiny fs/fetch clients, so a static import adds no
14
15
  // meaningful boot cost. The tools themselves are gated per session by the
@@ -22,6 +23,7 @@ import { TOOL_DEFS as MEDIA_TOOL_DEFS } from '../../runtime/media/tool-defs.mjs'
22
23
  import { TOOL_DEFS as TIDY_TOOL_DEFS } from '../../runtime/tidy/tool-defs.mjs';
23
24
  import { TOOL_DEFS as VISUALIZE_BRIDGE_TOOL_DEFS } from '../../runtime/visualize-bridge/tool-defs.mjs';
24
25
  import { ASK_USER_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
26
+ import { REACTION_TOOL_DEFS } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
25
27
  import { SETUP_TOOL_DEFS } from '../setup-tool/tool-defs.mjs';
26
28
  import { createSetupToolExecutor } from '../setup-tool/executor.mjs';
27
29
  import { bootProfile } from '../boot-profile.mjs';
@@ -143,7 +145,9 @@ function wireChannels(boot) {
143
145
  const meta = p.meta && typeof p.meta === 'object' ? p.meta : {};
144
146
  const content = channelNotificationModelContent(p);
145
147
  if (!content) return;
146
- notifySession(channelNotificationSessionId(rt.session, rt.reservedSessionId), content, meta);
148
+ notifySession(channelNotificationSessionId(rt.session, rt.reservedSessionId), content, meta, {
149
+ origin: PROMPT_ORIGIN_CHANNEL,
150
+ });
147
151
  },
148
152
  });
149
153
  bootProfile('channels:worker-ready', { ms: (performance.now() - channelsStartedAt).toFixed(1) });
@@ -167,6 +171,7 @@ function wireToolSurface(boot) {
167
171
  visualizeToolDefs: VISUALIZE_BRIDGE_TOOL_DEFS,
168
172
  setupToolDefs: SETUP_TOOL_DEFS,
169
173
  askUserToolDefs: ASK_USER_TOOL_DEFS,
174
+ reactionToolDefs: REACTION_TOOL_DEFS,
170
175
  goalTools: goalRuntime.tools,
171
176
  agentTools: agentTool.tools,
172
177
  skillToolEnabled: !envFlag('MIXDOG_DISABLE_SKILLS'),
@@ -232,6 +237,8 @@ function wireInternalTools(boot) {
232
237
  getCallerSession: (sessionId) =>
233
238
  sessionId === rt.session?.id ? rt.session : boot.mgr?.getSession?.(sessionId) || null,
234
239
  flushSettings: () => boot.flushAllConfigSavesAsync({ requireSaved: true }),
240
+ // Another session's route and quota actions (daemon-session-control.mjs); null without a daemon.
241
+ executeSessionControl: boot.params.executeSessionControl || null,
235
242
  });
236
243
  const disposeInternalTools = internalTools.setInternalToolsProvider({
237
244
  scopeId: rt.mcpScopeId,
@@ -0,0 +1,32 @@
1
+ // The bot-to-bot chain a session acts in (bot-hops.mjs botChainOf), from
2
+ // trusted runtime state only: the session's own turn chain (stamped from the
3
+ // submission that started it) and its delegation ancestry (parentSessionId /
4
+ // ownerSessionId), never from tool input.
5
+ import { resolveSessionMode } from '../runtime/agent/orchestrator/session/agent-mode.mjs';
6
+ import { botChainOf, turnBotChainOf } from '../runtime/shared/bot-hops.mjs';
7
+
8
+ const clean = (value) => String(value ?? '').trim();
9
+
10
+ /** The agent `session` acts as, by the session-mode resolver: a Lead its
11
+ * resolved agent (a coding Lead Mixy), a delegated worker its role. */
12
+ export function actingAgentOf(session, sessionId = '') {
13
+ const { agentId } = resolveSessionMode({ session, sessionId });
14
+ return clean(agentId || session?.agent);
15
+ }
16
+
17
+ /** The chain session `sessionId` acts in, oldest first, ending with the agent
18
+ * it acts as (when it has one): walking parentSessionId/ownerSessionId up
19
+ * from it, each session contributes its current turn's chain, then the
20
+ * agent it acts as. The visited set ends a malformed cycle. */
21
+ export function delegationChainOf(sessionId, getSession = () => null) {
22
+ const chain = [];
23
+ const seen = new Set();
24
+ for (let id = clean(sessionId); id && !seen.has(id); ) {
25
+ seen.add(id);
26
+ const session = getSession(id) || null;
27
+ const acting = actingAgentOf(session, id);
28
+ chain.unshift(...turnBotChainOf(session), ...(acting ? [acting] : []));
29
+ id = clean(session?.parentSessionId || session?.ownerSessionId);
30
+ }
31
+ return botChainOf(chain);
32
+ }
@@ -20,7 +20,8 @@
20
20
  * — the human's own sessions (Mixy's home, an ordinary Lead session) see every
21
21
  * bot; a bot's chat, a room seat or an unattended run (a routine, even one in
22
22
  * Mixy's own chat) only its own bot; a background run none. `session list`
23
- * follows the same scope.
23
+ * names every session for every scope, but gives previews and bot details
24
+ * only for the sessions the scope sees here (a background run none).
24
25
  *
25
26
  * Titles and previews are other bots' words: tag-shaped text is escaped and
26
27
  * the answer says they are data, not instructions (UNTRUSTED_NOTE).
@@ -63,8 +64,10 @@ const cut = (text, max) => (text.length > max ? `${text.slice(0, Math.max(0, max
63
64
  export const quotedText = (value, max) => cut(escapeHarnessTags(oneLine(value)), max);
64
65
  const plural = (count, word) => `${count} ${word}${count === 1 ? '' : 's'}`;
65
66
  const outputBytes = (value) => Buffer.byteLength(JSON.stringify(value), 'utf8');
66
- const isWorking = (row) => row?.working === true || row?.agentWorking === true;
67
- const activityOf = (row) => Number(row?.lastUsedAt) || Number(row?.updatedAt) || 0;
67
+ /** A catalog row whose session runs a turn now. */
68
+ export const isWorking = (row) => row?.working === true || row?.agentWorking === true;
69
+ /** A catalog row's last activity (epoch ms), 0 when unknown. */
70
+ export const activityOf = (row) => Number(row?.lastUsedAt) || Number(row?.updatedAt) || 0;
68
71
 
69
72
  /** The bot a catalog row belongs to: its home chat, room seat or task origin; '' for none. */
70
73
  export function sessionBotOf(row) {
@@ -309,7 +312,7 @@ export function createBotStatus({
309
312
  const parts = [`${quotedText(bot.name, 40)} (${bot.id})`, `chat ${state.homeId}`];
310
313
  if (bot.self) parts.push('you');
311
314
  parts.push(state.working ? 'working' : 'idle');
312
- if (state.flags.length) parts.push(`⚠ ${state.flags.join('; ')}`);
315
+ if (state.flags.length) parts.push(`attention: ${state.flags.join('; ')}`);
313
316
  parts.push(state.lastAt ? `last ${ago(state.lastAt, now)}` : 'no chat yet');
314
317
  if (state.running.length) {
315
318
  const [first] = state.running;
@@ -3,19 +3,22 @@ import {
3
3
  deleteSchedule,
4
4
  deleteWebhook,
5
5
  getWebhookSecret,
6
+ renameScheduleHere,
6
7
  saveSchedule,
7
8
  saveWebhook,
8
9
  setScheduleEnabled,
9
10
  setWebhookEnabled,
10
11
  setWebhookConfigAsync,
11
12
  testWebhook,
13
+ withScheduleRenameReserved,
12
14
  } from './services/channel-admin.mjs';
13
- import { getSchedule, markScheduleFailure, markScheduleSuccess } from '../runtime/channels/lib/schedules-db.mjs';
15
+ import { getSchedule, markScheduleFailure, markScheduleSkipped, markScheduleSuccess } from '../runtime/channels/lib/schedules-db.mjs';
16
+ import { scheduleRenameUncertainError } from '../runtime/channels/lib/schedule-errors.mjs';
14
17
  import { loadEndpointConfig, readEndpointLastPayload } from '../runtime/channels/lib/webhooks-db.mjs';
15
18
  import { webhookEventSummary, webhookRunPrompt } from '../runtime/channels/lib/webhook/session-dispatch.mjs';
16
19
  import { runScheduleSession } from './schedule-session-run.mjs';
17
20
  import { runWebhookSession } from './webhook-session-run.mjs';
18
- import { retryRoutineDelivery } from './owned-automation-run.mjs';
21
+ import { beginAutomationRename, retryRoutineDelivery } from './owned-automation-run.mjs';
19
22
 
20
23
  /** Why a "Run now" did not run: its owner bot is missing, or a run is going. */
21
24
  function skippedRunMessage(name, outcome) {
@@ -27,6 +30,36 @@ function skippedRunMessage(name, outcome) {
27
30
  // directly here and the runtime injects only the closure-owned callbacks
28
31
  // (channel worker handle, soft reload).
29
32
  export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAutomationRuntime = () => {} }) {
33
+ // The channels worker owns the scheduler and the runs it fires, so while it
34
+ // is up it performs the run check and the row move in one step
35
+ // (rename_schedule); this process's own runs ("Run now") are reserved around
36
+ // that call. Without a worker, the check and the move happen here.
37
+ async function renameSchedule(oldName, def, opts) {
38
+ if (!channels.status().running) return renameScheduleHere(oldName, def, opts);
39
+ return withScheduleRenameReserved(oldName, async () => {
40
+ try {
41
+ const reply = await channels.execute('rename_schedule', { name: oldName, def, ...opts });
42
+ const text = reply?.content?.[0]?.text ?? '';
43
+ if (reply?.isError === true) {
44
+ // The worker's refusal keeps its structured code and details.
45
+ throw Object.assign(
46
+ new Error(text.replace(/^rename_schedule failed: /, '')),
47
+ typeof reply.code === 'string' ? { code: reply.code } : {},
48
+ reply.details && typeof reply.details === 'object' ? { details: reply.details } : {}
49
+ );
50
+ }
51
+ return text ? JSON.parse(text) : null;
52
+ } catch (error) {
53
+ // Whatever the worker did, have it re-read the schedules.
54
+ reloadChannelsSoon();
55
+ // execute() already retried a transport failure (call timeout 120 s,
56
+ // same callId so the daemon runs the request once); what is left is
57
+ // an unanswered request.
58
+ if (error?.daemonTransportError) throw scheduleRenameUncertainError(error.message);
59
+ throw error;
60
+ }
61
+ });
62
+ }
30
63
  return {
31
64
  async getChannelSetup() {
32
65
  return channelSetup();
@@ -40,7 +73,7 @@ export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAut
40
73
  return result;
41
74
  },
42
75
  async saveSchedule(entry) {
43
- const result = await saveSchedule(entry);
76
+ const result = await saveSchedule(entry, { rename: renameSchedule });
44
77
  reloadChannelsSoon();
45
78
  // First automation created while the app is already running: the boot-
46
79
  // time autostart check has passed, so kick the worker start path now
@@ -86,22 +119,35 @@ export function createChannelConfigApi({ channels, reloadChannelsSoon, ensureAut
86
119
  },
87
120
  async runScheduleNow(name) {
88
121
  const id = String(name || '').trim();
89
- const schedule = await getSchedule(id);
90
- if (!schedule) throw new Error(`schedule "${id}" does not exist`);
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;
122
+ // The name is held from the read of the row through the outcome stamps,
123
+ // so a rename can neither move the row this run read nor slip in before
124
+ // its stamps are written (they would land on no row).
125
+ const release = beginAutomationRename('schedule', id);
126
+ // A run or a rename holding it: not run, recorded as an overlap skip.
127
+ if (!release) {
128
+ await markScheduleSkipped(id, new Date()).catch(() => {});
129
+ throw new Error(skippedRunMessage(id, { reason: 'overlap' }));
130
+ }
94
131
  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;
132
+ const schedule = await getSchedule(id);
133
+ if (!schedule) throw new Error(`schedule "${id}" does not exist`);
134
+ // Runs in the engine process as a turn in the owner bot's chat, so it
135
+ // works even while the channels worker is off.
136
+ let outcome;
137
+ try {
138
+ // Asked for by a person: no failure backoff.
139
+ outcome = await runScheduleSession(schedule, { attended: true, reserved: true });
140
+ } catch (error) {
141
+ await markScheduleFailure(schedule.name, new Date()).catch(() => {});
142
+ throw error;
143
+ }
144
+ if (outcome.skipped) throw new Error(skippedRunMessage(schedule.name, outcome));
145
+ // The run's outcome shows as the schedule's last run, like a timed fire.
146
+ await markScheduleSuccess(schedule.name, new Date()).catch(() => {});
147
+ return { name: schedule.name, ok: true, sessionId: outcome.sessionId, result: outcome.result };
148
+ } finally {
149
+ release();
100
150
  }
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
151
  },
106
152
  // Re-runs the endpoint on its latest received payload, with its current
107
153
  // instructions, behind the same untrusted-data fence as a live delivery.
@@ -17,6 +17,7 @@ import {
17
17
  stripStoredRouteDefaults,
18
18
  withDefaultPresets,
19
19
  } from '../runtime/agent/orchestrator/config-presets.mjs';
20
+ import { syncSessionCompaction } from './session-compaction-sync.mjs';
20
21
  import { createConfigWriters } from './config-lifecycle/config-writers.mjs';
21
22
  import { createLeadRouteSync } from './config-lifecycle/lead-route-sync.mjs';
22
23
  import { createOutputStyleStatusCache } from './config-lifecycle/output-style-cache.mjs';
@@ -134,6 +135,10 @@ export function createConfigLifecycle({
134
135
  getConfigHasSecrets,
135
136
  setConfigHasSecrets,
136
137
  getRoute,
138
+ // The live session follows the reloaded global compaction settings, unless
139
+ // the runtime runs on an injected (headless) config.
140
+ getSession,
141
+ fixedSessionCompaction = false,
137
142
  // Main route sync on reload (both optional: a lifecycle without them never syncs)
138
143
  setRouteState,
139
144
  resolveRoute,
@@ -222,6 +227,7 @@ export function createConfigLifecycle({
222
227
  const adopted = adoptConfig(next, { hasSecrets: true });
223
228
  configSaveBaseline = structuredClone(adopted);
224
229
  leadRoute.syncEmptyMainRoute(adopted);
230
+ if (!fixedSessionCompaction) syncSessionCompaction(getSession?.(), adopted);
225
231
  return adopted;
226
232
  }
227
233
 
@@ -0,0 +1,305 @@
1
+ // After the multi-account connector merge (connector-catalog.mjs
2
+ // mergeConnectorAccountServers) `gmail-2`, `google-calendar-2`… are accounts of
3
+ // one server, no longer servers. Every store of ours that keys a setting by
4
+ // MCP server name must name the surviving server instead:
5
+ // agents/<id>/agent.json, bots/<id>/agent.json connectors.off / connectors.tools
6
+ // (a legacy allow list too)
7
+ // <data>/project-connectors.json projects.<root>.off
8
+ // <data>/hooks.json, <data>/hooks/hooks.json mcp_tool `server`, tool matchers
9
+ // (extension scopes are the config's and are dropped by migrateConnectorAccounts;
10
+ // an approval's "always allow" is the agent's connectors.tools; agent routes
11
+ // name models, not servers.) Project and plugin hook files are the user's or
12
+ // the plugin's own and are never rewritten here.
13
+ //
14
+ // Rule: the merged connector is OFF for an agent/project only when EVERY
15
+ // merged server was off (the same rule as the merged server's `enabled`: one
16
+ // working account keeps the connector usable). Per-tool permissions move to
17
+ // the surviving name; where two merged servers disagree on a tool the MORE
18
+ // PERMISSIVE permission wins, as "on beats off" does at server level. Entries
19
+ // of a server that was off are dormant and are dropped when the connector
20
+ // stays on, so they never become live. A setting that names no merged-away
21
+ // server is never touched, which makes every run after the first a no-op.
22
+ //
23
+ // The merge is saved to the config once, so the rename mapping outlives it:
24
+ // `<data>/connector-merge-pending.json` ({ merged: [{ name, from }] }) is
25
+ // written before any store is touched and deleted only when every store
26
+ // migrated. A store that fails (unreadable, malformed, not writable) is
27
+ // reported and the others still go on; the next start retries them all.
28
+ //
29
+ // Hooks: only tool identifiers are rewritten — an `mcp_tool` handler's
30
+ // `server`, and a matcher / tool-name field (or the tool part of an `if`
31
+ // before its `(`) that is an `mcp__<server>__…` identifier or an
32
+ // alternative of a pattern. Argument conditions such as `Bash(mcp__x__*)`
33
+ // and any other data are never touched.
34
+ import { readdirSync, readFileSync, rmSync } from 'node:fs';
35
+ import { join } from 'node:path';
36
+ import { normalizeRules, standardConfigReport } from './services/hook-bus/config.mjs';
37
+ import {
38
+ PERMISSION_RANK,
39
+ PROJECT_CONNECTORS_FILE,
40
+ connectorEnabled,
41
+ parseConnectorPolicy,
42
+ storedConnectorPolicy,
43
+ } from '../runtime/agent/orchestrator/mcp/agent-connectors.mjs';
44
+ import { parseJsonText, withFileLockSync, writeJsonAtomicSync } from '../runtime/shared/atomic-file.mjs';
45
+
46
+ const unique = (list) => [...new Set(list)].sort();
47
+
48
+ /** The policy `{ off, tools }` (or a legacy `{ allow, off, tools }`) with every
49
+ * merged-away server of `merged` mapped onto its surviving one; null when it
50
+ * names none. `merged` is mergeConnectorAccountServers' `merged`. */
51
+ function mapMergedConnectorPolicy(policy, merged) {
52
+ let current = policy;
53
+ for (const { name, from } of merged) {
54
+ const gone = from.filter((server) => server !== name);
55
+ const named = (server) =>
56
+ current.off.includes(server) || current.allow?.includes(server) || Object.hasOwn(current.tools, server);
57
+ if (!gone.some(named)) continue;
58
+ const live = from.filter((server) => connectorEnabled(current, server));
59
+ const on = live.length > 0;
60
+ const off = current.off.filter((server) => !from.includes(server));
61
+ const allow = current.allow?.filter((server) => !from.includes(server));
62
+ if (allow && on) allow.push(name);
63
+ if (!allow && !on) off.push(name);
64
+ const tools = { ...current.tools };
65
+ for (const server of from) delete tools[server];
66
+ const moved = {};
67
+ for (const server of on ? live : from) {
68
+ for (const [tool, permission] of Object.entries(current.tools[server] || {})) {
69
+ if (!(tool in moved) || PERMISSION_RANK[permission] > PERMISSION_RANK[moved[tool]]) moved[tool] = permission;
70
+ }
71
+ }
72
+ if (Object.keys(moved).length) tools[name] = moved;
73
+ current = { ...(allow ? { allow: unique(allow) } : {}), off: unique(off), tools };
74
+ }
75
+ return current === policy ? null : current;
76
+ }
77
+
78
+ /** A mapped policy as the value the agent.json `connectors` field stores
79
+ * (undefined: the field goes). */
80
+ function storedValue(policy) {
81
+ if (!policy.allow) return storedConnectorPolicy(policy) ?? undefined;
82
+ const ids = unique([...policy.allow, ...Object.keys(policy.tools)]);
83
+ return ids.map((id) => ({
84
+ id,
85
+ enabled: policy.allow.includes(id),
86
+ ...(policy.tools[id] ? { tools: policy.tools[id] } : {}),
87
+ }));
88
+ }
89
+
90
+ export const PENDING_MERGE_FILE = 'connector-merge-pending.json';
91
+
92
+ const warn = (file, error) => process.stderr.write(`[connectors] ${file} not migrated (${error?.message || error})\n`);
93
+
94
+ /** The JSON at `file`; null when it does not exist. Any other failure throws. */
95
+ function readJsonFile(file) {
96
+ try {
97
+ return parseJsonText(readFileSync(file, 'utf8'));
98
+ } catch (error) {
99
+ if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return null;
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
105
+
106
+ /** Runs `step` for one store; a failure is reported and returned, never thrown. */
107
+ function attempt(file, step, outcome) {
108
+ try {
109
+ step();
110
+ } catch (error) {
111
+ warn(file, error);
112
+ outcome.failed.push(file);
113
+ }
114
+ }
115
+
116
+ function migrateAgentFiles(dataDir, merged, outcome, readdir) {
117
+ for (const store of ['agents', 'bots']) {
118
+ const dir = join(dataDir, store);
119
+ let entries;
120
+ try {
121
+ entries = readdir(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory());
122
+ } catch (error) {
123
+ // A store that does not exist has nothing to migrate; any other failure
124
+ // leaves its agents unprocessed, so the mapping must stay pending.
125
+ if (error?.code !== 'ENOENT') {
126
+ warn(dir, error);
127
+ outcome.failed.push(dir);
128
+ }
129
+ continue;
130
+ }
131
+ for (const entry of entries) {
132
+ const file = join(dataDir, store, entry.name, 'agent.json');
133
+ attempt(
134
+ file,
135
+ () => {
136
+ const manifest = readJsonFile(file);
137
+ if (!isRecord(manifest) || manifest.connectors == null) return;
138
+ const next = mapMergedConnectorPolicy(parseConnectorPolicy(manifest.connectors), merged);
139
+ if (!next) return;
140
+ const value = storedValue(next);
141
+ if (value) manifest.connectors = value;
142
+ else delete manifest.connectors;
143
+ writeJsonAtomicSync(file, manifest, { backup: true });
144
+ outcome.agents.push(`${store}/${entry.name}`);
145
+ },
146
+ outcome
147
+ );
148
+ }
149
+ }
150
+ }
151
+
152
+ function migrateProjectFile(dataDir, merged, outcome) {
153
+ const file = join(dataDir, PROJECT_CONNECTORS_FILE);
154
+ attempt(
155
+ file,
156
+ () => {
157
+ withFileLockSync(`${file}.lock`, () => {
158
+ const document = readJsonFile(file);
159
+ const projects = document?.projects;
160
+ if (!isRecord(projects)) return;
161
+ const changed = [];
162
+ for (const [root, entry] of Object.entries(projects)) {
163
+ if (!isRecord(entry) || !Array.isArray(entry.off)) continue;
164
+ const next = mapMergedConnectorPolicy({ off: entry.off.map(String), tools: {} }, merged);
165
+ if (!next) continue;
166
+ if (next.off.length) projects[root] = { ...entry, off: next.off };
167
+ else delete projects[root];
168
+ changed.push(root);
169
+ }
170
+ if (!changed.length) return;
171
+ writeJsonAtomicSync(file, document, { lock: false, backup: true });
172
+ outcome.projects.push(...changed);
173
+ });
174
+ },
175
+ outcome
176
+ );
177
+ }
178
+
179
+ const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
180
+
181
+ /** A tool identifier or pattern (`mcp__a__x`, `^mcp__a__.*$`, `Bash|mcp__a__x`)
182
+ * with each `mcp__<merged-away>` alternative renamed. */
183
+ function renameToolPattern(value, renames) {
184
+ let next = value;
185
+ for (const [gone, name] of renames) {
186
+ const identifier = new RegExp(`(^|\\|)(\\^?)mcp__${escapeRegExp(gone)}(?=__|(?![A-Za-z0-9_-]))`, 'g');
187
+ next = next.replace(identifier, (_all, lead, anchor) => `${lead}${anchor}mcp__${name}`);
188
+ }
189
+ return next;
190
+ }
191
+
192
+ /** A condition `Tool(arguments)`: only the tool part is an identifier. */
193
+ function renameCondition(value, renames) {
194
+ const open = value.indexOf('(');
195
+ return open < 0
196
+ ? renameToolPattern(value, renames)
197
+ : renameToolPattern(value.slice(0, open), renames) + value.slice(open);
198
+ }
199
+
200
+ /** Renames in the hook structures of `document` (in place); true when any changed. */
201
+ function renameInHooks(document, renames) {
202
+ let changed = false;
203
+ const set = (owner, key, rename) => {
204
+ if (typeof owner[key] !== 'string') return;
205
+ const next = rename(owner[key], renames);
206
+ if (next === owner[key]) return;
207
+ owner[key] = next;
208
+ changed = true;
209
+ };
210
+ const handler = (hook) => {
211
+ if (!isRecord(hook)) return;
212
+ if (hook.type === 'mcp_tool') {
213
+ if (typeof hook.server === 'string' && renames.has(hook.server.trim())) {
214
+ hook.server = renames.get(hook.server.trim());
215
+ changed = true;
216
+ }
217
+ set(hook, 'tool', renameToolPattern);
218
+ }
219
+ set(hook, 'if', renameCondition);
220
+ };
221
+ if (standardConfigReport(document).standard) {
222
+ for (const groups of Object.values(document.hooks)) {
223
+ for (const group of Array.isArray(groups) ? groups : []) {
224
+ if (!isRecord(group)) continue;
225
+ set(group, 'matcher', renameToolPattern);
226
+ set(group, 'if', renameCondition);
227
+ for (const hook of Array.isArray(group.hooks) ? group.hooks : []) handler(hook);
228
+ }
229
+ }
230
+ } else {
231
+ for (const rule of normalizeRules(document)) {
232
+ if (!isRecord(rule)) continue;
233
+ set(rule, 'tool', renameToolPattern);
234
+ set(rule, 'name', renameToolPattern);
235
+ set(rule, 'if', renameCondition);
236
+ }
237
+ }
238
+ return changed;
239
+ }
240
+
241
+ function migrateHookFiles(dataDir, merged, outcome) {
242
+ const renames = new Map(
243
+ merged.flatMap(({ name, from }) => from.filter((server) => server !== name).map((gone) => [gone, name]))
244
+ );
245
+ for (const file of [join(dataDir, 'hooks.json'), join(dataDir, 'hooks', 'hooks.json')]) {
246
+ attempt(
247
+ file,
248
+ () => {
249
+ const document = readJsonFile(file);
250
+ if (!isRecord(document) && !Array.isArray(document)) return;
251
+ if (!renameInHooks(document, renames)) return;
252
+ writeJsonAtomicSync(file, document, { backup: true });
253
+ outcome.hooks.push(file);
254
+ },
255
+ outcome
256
+ );
257
+ }
258
+ }
259
+
260
+ const validGroup = (group) =>
261
+ isRecord(group) &&
262
+ typeof group.name === 'string' &&
263
+ Array.isArray(group.from) &&
264
+ group.from.every((n) => typeof n === 'string');
265
+
266
+ /** Maps every per-server setting of `dataDir` onto the surviving servers of
267
+ * `merged` (this start's merge, [] to only retry) plus any still pending from
268
+ * an earlier start. Returns what it rewrote and the files that failed:
269
+ * `{ agents, projects, hooks, failed, unrecorded? }`. The pending record is
270
+ * removed only when `failed` is empty; `unrecorded` says it could not be
271
+ * written, so the caller must not save the merge. `readdir` lists the agent
272
+ * and bot directories. */
273
+ export function migrateMergedConnectorServers(dataDir, merged, { readdir = readdirSync } = {}) {
274
+ const outcome = { agents: [], projects: [], hooks: [], failed: [] };
275
+ if (!dataDir) return outcome;
276
+ const pendingFile = join(dataDir, PENDING_MERGE_FILE);
277
+ let pending = [];
278
+ try {
279
+ const stored = readJsonFile(pendingFile)?.merged;
280
+ pending = Array.isArray(stored) ? stored.filter(validGroup).map(({ name, from }) => ({ name, from })) : [];
281
+ } catch (error) {
282
+ warn(pendingFile, error);
283
+ }
284
+ const groups = [...pending];
285
+ for (const { name, from } of merged) {
286
+ if (!groups.some((group) => group.name === name && JSON.stringify(group.from) === JSON.stringify(from))) {
287
+ groups.push({ name, from });
288
+ }
289
+ }
290
+ if (!groups.length) return outcome;
291
+ if (groups.length !== pending.length) {
292
+ try {
293
+ writeJsonAtomicSync(pendingFile, { merged: groups }, { backup: false });
294
+ } catch (error) {
295
+ warn(pendingFile, error);
296
+ // Nothing records the mapping: a failed store must keep the merge unsaved.
297
+ outcome.unrecorded = true;
298
+ }
299
+ }
300
+ migrateAgentFiles(dataDir, groups, outcome, readdir);
301
+ migrateProjectFile(dataDir, groups, outcome);
302
+ migrateHookFiles(dataDir, groups, outcome);
303
+ if (!outcome.failed.length) rmSync(pendingFile, { force: true });
304
+ return outcome;
305
+ }