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
@@ -25,10 +25,10 @@ export interface ConnectorCatalogEntry {
25
25
  id: string;
26
26
  name: string;
27
27
  backend: ConnectorBackend;
28
- /** Offered only after the user enters their own Google OAuth client. */
29
- ownKey?: boolean;
30
28
  /** Curated deferred-tool manifest line (connector-triggers.mjs). */
31
29
  trigger: string;
30
+ /** New connections go by app password only (Gmail). */
31
+ appPasswordOnly?: boolean;
32
32
  }
33
33
 
34
34
  export const CONNECTOR_CATALOG: readonly ConnectorCatalogEntry[];
@@ -43,9 +43,40 @@ export function connectorServerInput(
43
43
  key?: string,
44
44
  options?: { extraAccount?: boolean }
45
45
  ): Record<string, unknown>;
46
+ export function connectorAppPasswordOnly(entry: ConnectorCatalogEntry | null | undefined): boolean;
47
+ export function connectorUsesGoogleClient(entry: ConnectorCatalogEntry | null | undefined): boolean;
46
48
  export function connectorMultiAccount(entry: ConnectorCatalogEntry | null | undefined): boolean;
47
49
  export const GMAIL_APP_PASSWORD: Readonly<{ url: string; accountHeader: string; helpUrl: string }>;
48
50
  export function gmailAddress(value: unknown): string;
49
51
  export function gmailAppPasswordValue(value: unknown): string;
50
52
  export function gmailAppPasswordAccount(cfg: unknown): string;
51
53
  export function gmailAppPasswordServerInput(name: string, email: string, password: string): Record<string, unknown>;
54
+
55
+ /** One stored account of a multi-account connector server. */
56
+ export interface ConnectorAccountEntry {
57
+ name: string;
58
+ url: string;
59
+ headers?: Record<string, string>;
60
+ oauthClientId?: string;
61
+ /** `false`: the account is kept but neither connected nor called. */
62
+ enabled?: boolean;
63
+ }
64
+ export function connectorAccountsUrl(id: string): string;
65
+ export function connectorAccountsOf(cfg: unknown): ConnectorAccountEntry[];
66
+ export function connectorAccountServer(
67
+ cfg: Record<string, unknown>,
68
+ account: ConnectorAccountEntry
69
+ ): Record<string, unknown>;
70
+ export function connectorAccountServers(cfg: unknown): Record<string, unknown>[];
71
+ export function connectorAccountEnabled(account: ConnectorAccountEntry): boolean;
72
+ export function connectorLiveAccountServers(cfg: unknown): Record<string, unknown>[];
73
+ export function withConnectorAccountEnabled(
74
+ cfg: Record<string, unknown>,
75
+ name: string,
76
+ enabled: boolean
77
+ ): Record<string, unknown>;
78
+ export function connectorAccountEntry(name: string, cfg: Record<string, unknown>): ConnectorAccountEntry;
79
+ export function mergeConnectorAccountServers(servers: Record<string, unknown>): {
80
+ servers: Record<string, unknown>;
81
+ merged: { connector: string; name: string; from: string[] }[];
82
+ };
@@ -53,20 +53,18 @@ export const CONNECTOR_CATALOG = Object.freeze(
53
53
  id: 'gmail',
54
54
  name: 'Gmail',
55
55
  backend: google('gmail', GOOGLE_SCOPES.gmail, ['send_email']),
56
+ // New Gmail connections use an app password only (GMAIL_APP_PASSWORD);
57
+ // a Gmail server already on the Google sign-in keeps working.
58
+ appPasswordOnly: true,
56
59
  },
57
- // Calendar and Drive are offered only to a user who entered their own
58
- // Google OAuth client (Connectors → add custom → 내 Google 키): `ownKey`.
59
- // Existing connections keep working; Mixy and the chat cards never offer them.
60
60
  {
61
61
  id: 'google-calendar',
62
62
  name: 'Google Calendar',
63
- ownKey: true,
64
63
  backend: google('calendar', GOOGLE_SCOPES.calendar, ['delete_event']),
65
64
  },
66
65
  {
67
66
  id: 'google-drive',
68
67
  name: 'Google Drive',
69
- ownKey: true,
70
68
  backend: google('drive', GOOGLE_SCOPES.drive, []),
71
69
  },
72
70
  {
@@ -113,9 +111,8 @@ export const CONNECTOR_CATALOG = Object.freeze(
113
111
  ].map((entry) => Object.freeze({ ...entry, trigger: connectorTrigger(entry.id) }))
114
112
  );
115
113
 
116
- /** The ids offered to everyone (the setup tool's `connect_connector`): the
117
- * catalog minus the own-key-only Google services. */
118
- export const CONNECTOR_IDS = Object.freeze(CONNECTOR_CATALOG.filter((entry) => !entry.ownKey).map((entry) => entry.id));
114
+ /** The ids the setup tool's `connect_connector` offers: the whole catalog. */
115
+ export const CONNECTOR_IDS = Object.freeze(CONNECTOR_CATALOG.map((entry) => entry.id));
119
116
 
120
117
  /** The catalog entry for `id`, or null. */
121
118
  export function connectorById(id) {
@@ -193,6 +190,167 @@ export function connectorMultiAccount(entry) {
193
190
  return entry?.backend?.kind === 'oauth' && entry.backend.oauthClient === 'google';
194
191
  }
195
192
 
193
+ // ── One server per multi-account connector ──────────────────────────────────
194
+ // A multi-account connector is ONE MCP server (`gmail`, `google-calendar`,
195
+ // `google-drive`) holding its accounts:
196
+ // { type: 'http', url: connectorAccountsUrl(id), connector: id,
197
+ // accounts: [{ name, url, headers?, oauthClientId? }, …] }
198
+ // Each account keeps the transport it had as a server of its own (the Google
199
+ // sign-in URL its tokens are stored under, or the app-password address and
200
+ // account header), so its keychain entries are untouched. Account names share
201
+ // the server-name space (`gmail`, `gmail-2`…): a name-based call (remove, sign
202
+ // in, sign out) that names an account acts on that account. The server is
203
+ // answered in-process (connectors/connector-accounts-mcp.mjs): one tool set
204
+ // with an `account` argument.
205
+ const ACCOUNT_FIELDS = ['url', 'headers', 'oauthClientId'];
206
+
207
+ /** The reserved (never resolvable) address of connector `id`'s accounts server. */
208
+ export function connectorAccountsUrl(id) {
209
+ return `https://connectors.mixdog.invalid/${encodeURIComponent(id)}/mcp`;
210
+ }
211
+
212
+ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
213
+
214
+ /** The stored accounts of a multi-account connector server, [] for any other
215
+ * config (a legacy one-account server included). */
216
+ export function connectorAccountsOf(cfg) {
217
+ if (!isRecord(cfg) || !Array.isArray(cfg.accounts)) return [];
218
+ if (!connectorMultiAccount(connectorById(connectorOfServer(cfg)))) return [];
219
+ return cfg.accounts.filter(
220
+ (account) =>
221
+ isRecord(account) && typeof account.name === 'string' && account.name && typeof account.url === 'string'
222
+ );
223
+ }
224
+
225
+ /** The one-account server config of `account` of server `cfg` (the shape the
226
+ * account had as a server of its own), named by the account. */
227
+ export function connectorAccountServer(cfg, account) {
228
+ const out = { name: account.name, type: 'http' };
229
+ for (const field of ACCOUNT_FIELDS) if (account[field] !== undefined) out[field] = account[field];
230
+ out.connector = cfg.connector;
231
+ return out;
232
+ }
233
+
234
+ /** Every account of `cfg` as its one-account server config. */
235
+ export function connectorAccountServers(cfg) {
236
+ return connectorAccountsOf(cfg).map((account) => connectorAccountServer(cfg, account));
237
+ }
238
+
239
+ /** Whether stored account `account` is switched on (`enabled: false` is off). */
240
+ export const connectorAccountEnabled = (account) => account.enabled !== false;
241
+
242
+ /** The switched-on accounts of `cfg` as their one-account server configs: the
243
+ * ones the server connects and routes calls to. */
244
+ export function connectorLiveAccountServers(cfg) {
245
+ return connectorAccountsOf(cfg)
246
+ .filter(connectorAccountEnabled)
247
+ .map((account) => connectorAccountServer(cfg, account));
248
+ }
249
+
250
+ /**
251
+ * Server `cfg` with its account `name` switched on or off. Off flags the
252
+ * account (`enabled: false`) and switches the server off with its last
253
+ * switched-on account. On drops the flag (a switched-on account carries none)
254
+ * and switches the server on. Every other entry stays as stored.
255
+ */
256
+ export function withConnectorAccountEnabled(cfg, name, enabled) {
257
+ const accounts = cfg.accounts.map((account) => {
258
+ if (!isRecord(account) || account.name !== name) return account;
259
+ const { enabled: _enabled, ...rest } = account;
260
+ return enabled ? rest : { ...rest, enabled: false };
261
+ });
262
+ const next = { ...cfg, accounts };
263
+ if (enabled) return { ...next, enabled: true };
264
+ return connectorAccountsOf(next).some(connectorAccountEnabled) ? next : { ...next, enabled: false };
265
+ }
266
+
267
+ /** The stored account entry of one-account config `cfg` under `name`. */
268
+ export function connectorAccountEntry(name, cfg) {
269
+ const out = { name };
270
+ for (const field of ACCOUNT_FIELDS) if (cfg?.[field] !== undefined) out[field] = cfg[field];
271
+ return out;
272
+ }
273
+
274
+ const naturalOrder = (a, b) => a.localeCompare(b, 'en', { numeric: true });
275
+
276
+ /**
277
+ * Merges every multi-account connector's servers into one server holding the
278
+ * accounts: legacy one-account servers (`gmail`, `gmail-2`…, Calendar and
279
+ * Drive alike) and any second accounts server. The server keeps the name of
280
+ * an existing accounts server, else the connector id when a server has it,
281
+ * else the first name; its other fields come from that server. Accounts keep
282
+ * their names, transports and order (that server's first); a second entry
283
+ * for the same name or app-password address is dropped. The merged server is
284
+ * off only when every merged one was; otherwise the accounts of a merged
285
+ * server that was off stay off (`enabled: false` on the account), and an
286
+ * account's own `enabled: false` is kept. A server left with no switched-on
287
+ * account is off. Idempotent: `merged` is empty when
288
+ * nothing changed. Returns `{ servers, merged: [{ connector, name, from }] }`.
289
+ */
290
+ export function mergeConnectorAccountServers(input) {
291
+ const servers = isRecord(input) ? { ...input } : {};
292
+ const merged = [];
293
+ for (const entry of CONNECTOR_CATALOG.filter(connectorMultiAccount)) {
294
+ const names = Object.keys(servers)
295
+ .filter((name) => isRecord(servers[name]) && connectorOfServer(servers[name]) === entry.id)
296
+ .sort(naturalOrder);
297
+ if (!names.length) continue;
298
+ const grouped = names.filter((name) => Array.isArray(servers[name].accounts));
299
+ if (names.length === 1 && grouped.length === 1) continue;
300
+ const target = grouped[0] ?? (names.includes(entry.id) ? entry.id : names[0]);
301
+ const order = [target, ...names.filter((name) => name !== target)];
302
+ const off = order.every((name) => input[name].enabled === false);
303
+ const accounts = [];
304
+ const seenNames = new Set();
305
+ const seenAddresses = new Set();
306
+ for (const name of order) {
307
+ const cfg = servers[name];
308
+ const own = Array.isArray(cfg.accounts) ? connectorAccountsOf(cfg) : [connectorAccountEntry(name, cfg)];
309
+ // A merged server that was off keeps its accounts off, unless the whole
310
+ // connector is off (then re-enabling it brings every account back).
311
+ const serverOff = !off && cfg.enabled === false;
312
+ for (const account of own) {
313
+ const address = gmailAppPasswordAccount(connectorAccountServer(cfg, account));
314
+ if (seenNames.has(account.name) || (address && seenAddresses.has(address))) continue;
315
+ seenNames.add(account.name);
316
+ if (address) seenAddresses.add(address);
317
+ accounts.push(serverOff ? { ...account, enabled: false } : account);
318
+ }
319
+ }
320
+ const {
321
+ type: _type,
322
+ url: _url,
323
+ headers: _headers,
324
+ oauthClientId: _client,
325
+ accounts: _accounts,
326
+ enabled: _enabled,
327
+ ...rest
328
+ } = servers[target];
329
+ for (const name of order) delete servers[name];
330
+ servers[target] = {
331
+ ...rest,
332
+ type: 'http',
333
+ url: connectorAccountsUrl(entry.id),
334
+ connector: entry.id,
335
+ accounts,
336
+ ...(off || (accounts.length > 0 && !accounts.some(connectorAccountEnabled)) ? { enabled: false } : {}),
337
+ };
338
+ merged.push({ connector: entry.id, name: target, from: order });
339
+ }
340
+ return { servers, merged };
341
+ }
342
+
343
+ /** Whether a new connection of `entry` goes by app password only (Gmail). */
344
+ export function connectorAppPasswordOnly(entry) {
345
+ return entry?.appPasswordOnly === true;
346
+ }
347
+
348
+ /** Whether `entry` signs in with Mixdog's built-in Google client (or the
349
+ * user's own Google client in its place). */
350
+ export function connectorUsesGoogleClient(entry) {
351
+ return entry?.backend?.kind === 'oauth' && entry.backend.oauthClient === 'google';
352
+ }
353
+
196
354
  /** The MCP server input that connects catalog entry `entry` under `name`
197
355
  * (oauth / apiKey); `key` fills an apiKey backend; `extraAccount`
198
356
  * makes it one more account of a multi-account connector. */
@@ -1,30 +1,33 @@
1
1
  // Curated selection triggers for catalog connectors (MCP servers). A catalog
2
2
  // entry carries its line as `trigger`; the deferred-tool manifest lists each
3
3
  // connected server once as `- mcp__<server id>: <trigger>`, so one line must
4
- // say what the connector is for (plus a close negative where useful), in
5
- // English, at most CONNECTOR_TRIGGER_MAX_CHARS characters.
4
+ // say WHEN to call the server (the user requests it covers, plus a close
5
+ // negative where useful), not list its capabilities; plain ASCII English, at
6
+ // most CONNECTOR_TRIGGER_MAX_CHARS characters.
6
7
  export const CONNECTOR_TRIGGER_MAX_CHARS = 100;
7
8
 
8
9
  export const CONNECTOR_TRIGGERS = Object.freeze({
9
10
  // Not a catalog connector (Mixdog uses the signed-in GitHub CLI), but a
10
11
  // user-added `github` MCP server still gets this line.
11
- github: 'GitHub repos, issues, pull requests, Actions and code search; not local Git history or diffs.',
12
- notion: 'Notion pages, databases and workspace search; create or update team docs and notes.',
13
- linear: 'Linear issues, projects, cycles and comments; create, triage or update tickets.',
14
- sentry: 'Sentry issues, events, stack traces and releases; triage production errors.',
15
- gmail: 'Gmail: send email; with an app password also search, read, archive, label, star, trash.',
16
- 'google-calendar': 'Google Calendar events on the primary calendar; list, search, create, update or delete.',
17
- 'google-drive': 'Google Drive files Mixdog created or was given; search, read or create; not local files.',
18
- jira: 'Jira issues, projects, sprints and comments; JQL search, create or transition tickets.',
19
- confluence: 'Confluence spaces and pages; search, read or edit the team wiki; not Jira tickets.',
20
- dropbox: 'Dropbox files and folders; search, read, upload or share cloud files; not local files.',
21
- trello: 'Trello boards, lists and cards; create, move or update cards.',
22
- airtable: 'Airtable bases, tables and records; query, create or update rows.',
23
- stripe: 'Stripe customers, payments, invoices, subscriptions and refunds; billing data and actions.',
24
- vercel: 'Vercel projects, deployments, domains and logs; deploy status and build errors.',
25
- supabase: 'Supabase projects: Postgres tables and SQL, auth, storage and edge functions.',
26
- postgres: 'PostgreSQL schemas, tables and SQL queries on a connected database; not local files.',
27
- 'hugging-face': 'Hugging Face Hub models, datasets, Spaces and papers; not local GGUF model setup.',
12
+ github: 'GitHub issues, pull requests, Actions runs and repo code search; not local Git history.',
13
+ notion: 'Finding, reading or updating Notion pages, databases and team notes.',
14
+ linear: 'Looking up, creating or triaging Linear issues, projects and cycles.',
15
+ sentry: 'Investigating production errors, stack traces and releases in Sentry.',
16
+ // The line never names an account: that arrives with the server's
17
+ // instructions when its tools are loaded.
18
+ gmail: 'Gmail email: sending, reading, searching or organizing mail.',
19
+ 'google-calendar': 'Google Calendar: checking, creating or editing events.',
20
+ 'google-drive': 'Google Drive: finding, reading or creating files Mixdog made or was given; not local files.',
21
+ jira: 'Looking up, creating or transitioning Jira tickets, sprints and JQL searches.',
22
+ confluence: 'Searching, reading or editing Confluence wiki pages; not Jira tickets.',
23
+ dropbox: 'Finding, reading, uploading or sharing Dropbox files; not local files.',
24
+ trello: 'Viewing, creating or moving Trello boards, lists and cards.',
25
+ airtable: 'Querying or updating Airtable bases, tables and records.',
26
+ stripe: 'Checking or acting on Stripe payments, invoices, subscriptions and refunds.',
27
+ vercel: 'Checking Vercel deployments, domains, logs and build errors.',
28
+ supabase: 'Working with Supabase Postgres tables, SQL, auth, storage or edge functions.',
29
+ postgres: 'Running SQL or inspecting schemas on a connected PostgreSQL database; not local files.',
30
+ 'hugging-face': 'Finding Hugging Face Hub models, datasets, Spaces or papers; not local GGUF setup.',
28
31
  });
29
32
 
30
33
  // Server ids differ only in separators and case (google_calendar, huggingface).
@@ -40,13 +40,12 @@ const AGENT_SECTION_DEFAULTS = Object.freeze({
40
40
  const MODULE_DEFAULTS = Object.freeze({ enabled: true });
41
41
 
42
42
  export const CHANNELS_DEFAULTS = Object.freeze({
43
- access: Object.freeze({ dmPolicy: 'allowlist', allowFrom: [], channels: {} }),
44
43
  webhook: Object.freeze({ enabled: true, port: 3333 }),
45
44
  });
46
45
 
47
46
  // Disabled-agent roster shipped before `advisor` joined the defaults
48
47
  // (worker … writer plus the Maintainer). Stored full lists are read against it.
49
- export const PREVIOUS_DISABLED_AGENT_ROSTER = Object.freeze([
48
+ const PREVIOUS_DISABLED_AGENT_ROSTER = Object.freeze([
50
49
  'front-worker',
51
50
  'heavy-worker',
52
51
  'maintainer',
@@ -168,17 +167,6 @@ function removeLegacyKeys(root, dataDir) {
168
167
  if (isPlainObject(agent.agents)) agent.agents = withoutRetiredAgentRoutes(agent.agents, dataDir);
169
168
  next.agent = agent;
170
169
  }
171
- const channelEntries = next.channels?.access?.channels;
172
- if (isPlainObject(channelEntries)) {
173
- const channels = Object.fromEntries(
174
- Object.entries(channelEntries).map(([id, entry]) => {
175
- if (!isPlainObject(entry)) return [id, entry];
176
- const { requireMention: _retired, ...rest } = entry;
177
- return [id, rest];
178
- })
179
- );
180
- next.channels = { ...next.channels, access: { ...next.channels.access, channels } };
181
- }
182
170
  return next;
183
171
  }
184
172
 
@@ -188,7 +176,7 @@ function removeLegacyKeys(root, dataDir) {
188
176
  * not set the marker. `stripRouteDefaults` drops shipped presets and
189
177
  * maintenance routes (their defaults live with the agent config).
190
178
  */
191
- export function separateDefaultsInConfig(
179
+ function separateDefaultsInConfig(
192
180
  root,
193
181
  { stripRouteDefaults = (agent) => agent, dataDir = resolvePluginData() } = {}
194
182
  ) {
@@ -6,14 +6,86 @@
6
6
  // owner, and the copy is withdrawn as soon as the owner settles (answered in
7
7
  // either place, timed out, its turn ended). Like held-request-host.mjs this
8
8
  // registry is process-wide and dependency-free.
9
+ // Hand-offs take the same path: a session chats handed work to (`session`
10
+ // send / create from a bot conversation, agent-task-sessions.mjs) shows its
11
+ // own approvals in EVERY chat whose hand-off to it still owes its report, one
12
+ // copy per chat, all settling the one request. The report watch store
13
+ // (session-runtime/agent-home-delivery.mjs) notes and ends hand-offs as its
14
+ // watches come and go, whatever removes them; a chat
15
+ // whose last hand-off to the session ends loses its copy, the others keep
16
+ // theirs.
9
17
 
10
18
  const KEY = Symbol.for('mixdog.delegatedApprovalHost.v1');
11
19
 
12
20
  function registry() {
13
- globalThis[KEY] ??= { queues: new Set(), pending: new Map(), opener: null };
21
+ globalThis[KEY] ??= { queues: new Set(), pending: new Map(), opener: null, handoffs: new Map(), ended: new Set() };
14
22
  return globalThis[KEY];
15
23
  }
16
24
 
25
+ /** The chats with a live hand-off to `sessionId`, each once (in noting order)
26
+ * with the name its latest hand-off gave the session: Map chat → `{ title }`. */
27
+ export function handoffChatsOf(sessionId) {
28
+ const chats = new Map();
29
+ for (const handoff of registry().handoffs.values()) {
30
+ if (handoff.sessionId !== sessionId) continue;
31
+ chats.delete(handoff.surfaceSessionId);
32
+ chats.set(handoff.surfaceSessionId, { title: handoff.title });
33
+ }
34
+ return chats;
35
+ }
36
+
37
+ /** Bring each pending request of `sessionId` that hand-offs reach to exactly
38
+ * the chats now handing it work: a new chat gets its copy, a chat whose
39
+ * hand-offs all ended loses only its own. */
40
+ function syncHandoffCopies(sessionId) {
41
+ const { pending } = registry();
42
+ const chats = handoffChatsOf(sessionId);
43
+ for (const asked of pending.values()) {
44
+ if (!asked.copies || asked.sessionId !== sessionId) continue;
45
+ for (const [chat, copy] of asked.copies) {
46
+ if (chats.has(chat)) continue;
47
+ asked.copies.delete(chat);
48
+ for (const queue of liveQueuesOf(chat)) queue.withdraw(copy.id, null);
49
+ }
50
+ for (const [chat, handoff] of chats) {
51
+ if (asked.copies.has(chat)) continue;
52
+ const copy = { id: asked.id, sessionId: chat, request: asked.copyFor(chat, handoff), answer: asked.answer };
53
+ asked.copies.set(chat, copy);
54
+ deliver(copy, () => pending.get(asked.id) === asked && asked.copies.get(chat) === copy);
55
+ }
56
+ }
57
+ }
58
+
59
+ // Run ids of hand-offs that ended: never noted again (a watch read before it
60
+ // was removed must not bring it back, however many hand-offs ended since).
61
+ // Run ids are never reused, so an ended id is final for the life of the
62
+ // process and there is nothing to age out: no count decides what is
63
+ // forgotten. One id string is kept per ended hand-off.
64
+
65
+ /** Chat `homeSessionId` handed session `taskSessionId` work (`title`: the
66
+ * target's name in that chat) under report watch `runId`. */
67
+ export function noteHandoff({ runId, taskSessionId, homeSessionId, title }) {
68
+ if (!runId || !taskSessionId || !homeSessionId || taskSessionId === homeSessionId) return;
69
+ if (registry().ended.has(String(runId))) return;
70
+ registry().handoffs.set(String(runId), {
71
+ sessionId: String(taskSessionId),
72
+ surfaceSessionId: String(homeSessionId),
73
+ title: String(title || ''),
74
+ });
75
+ syncHandoffCopies(String(taskSessionId));
76
+ }
77
+
78
+ /** The report watch `runId` is gone (its work reported back, was never
79
+ * taken, or the watch was retired): its hand-off ends for good. */
80
+ export function endHandoff(runId) {
81
+ const { handoffs, ended } = registry();
82
+ ended.add(String(runId));
83
+ const handoff = handoffs.get(String(runId));
84
+ if (!handoff) return;
85
+ handoffs.delete(String(runId));
86
+ syncHandoffCopies(handoff.sessionId);
87
+ }
88
+
17
89
  /** `queue`: `{ sessionId() → string, disposed() → boolean, offer(delegated),
18
90
  * withdraw(id, result) }` — one session runtime's approval queue. */
19
91
  export function registerApprovalQueue(queue) {
@@ -46,20 +118,36 @@ function offerTo(sessionId, delegated) {
46
118
  return live.length > 0;
47
119
  }
48
120
 
49
- /** Show `delegated` (`{ id, sessionId, request, answer(...finishArgs) → boolean }`,
50
- * `sessionId` being the top-level conversation) there, now or once it loads. */
51
- export function delegateApproval(delegated) {
52
- const { pending, opener } = registry();
53
- pending.set(delegated.id, delegated);
54
- if (offerTo(delegated.sessionId, delegated) || typeof opener !== 'function') return;
121
+ /** Offer `copy` in its chat now, or once that chat loads while `current()`
122
+ * still holds. */
123
+ function deliver(copy, current) {
124
+ const { opener } = registry();
125
+ if (offerTo(copy.sessionId, copy) || typeof opener !== 'function') return;
55
126
  void Promise.resolve()
56
- .then(() => opener(delegated.sessionId))
127
+ .then(() => opener(copy.sessionId))
57
128
  .then(() => {
58
- if (pending.get(delegated.id) === delegated) offerTo(delegated.sessionId, delegated);
129
+ if (current()) offerTo(copy.sessionId, copy);
59
130
  })
60
131
  .catch(() => {});
61
132
  }
62
133
 
134
+ /** Show `delegated` (`{ id, sessionId, request, answer(...finishArgs) → boolean }`,
135
+ * `sessionId` being the top-level conversation) there, now or once it loads. */
136
+ export function delegateApproval(delegated) {
137
+ const { pending } = registry();
138
+ pending.set(delegated.id, delegated);
139
+ deliver(delegated, () => pending.get(delegated.id) === delegated);
140
+ }
141
+
142
+ /** A top-level session's request (`{ id, sessionId, request, answer }`,
143
+ * `sessionId` being the asking session) that every chat handing it work
144
+ * shows too, now and while it waits: `copyFor(chat, { title })` is the
145
+ * request as that chat shows it. */
146
+ export function delegateToHandoffs(asked) {
147
+ registry().pending.set(asked.id, { ...asked, copies: new Map() });
148
+ syncHandoffCopies(asked.sessionId);
149
+ }
150
+
63
151
  /** The owner settled `id`: every copy goes away. `result` (`{ approved, device }`)
64
152
  * lets the copy say who answered. */
65
153
  export function withdrawDelegatedApproval(id, result = null) {
@@ -68,7 +156,11 @@ export function withdrawDelegatedApproval(id, result = null) {
68
156
  for (const queue of queues) queue.withdraw(id, result);
69
157
  }
70
158
 
71
- /** The pending delegated approvals a top-level session should show. */
159
+ /** The pending delegated approvals (and hand-off copies) a top-level session
160
+ * should show. */
72
161
  export function delegatedApprovalsFor(sessionId) {
73
- return [...registry().pending.values()].filter((delegated) => delegated.sessionId === sessionId);
162
+ return [...registry().pending.values()].flatMap((delegated) => {
163
+ if (delegated.copies) return delegated.copies.has(sessionId) ? [delegated.copies.get(sessionId)] : [];
164
+ return delegated.sessionId === sessionId ? [delegated] : [];
165
+ });
74
166
  }
@@ -5,12 +5,25 @@
5
5
  // `developer.<optionId>` is true. Default off. An option with a `warning`
6
6
  // turns on only after the user confirms that warning; an option with a
7
7
  // `provider` gates that dev-only OAuth provider.
8
- import { readSection } from './config.mjs';
8
+ import { readSection, readSectionStrict } from './config.mjs';
9
9
 
10
10
  const OAUTH_RISK_WARNING =
11
11
  'Using this provider through OAuth carries a high risk of penalties such as account restrictions.';
12
12
 
13
13
  export const DEVELOPER_SECTIONS = Object.freeze([
14
+ Object.freeze({
15
+ id: 'approvals',
16
+ label: 'Approvals',
17
+ options: Object.freeze([
18
+ Object.freeze({
19
+ id: 'allowAllActions',
20
+ label: 'Allow all actions',
21
+ description: 'Skips the approval cards for every action except payments and permanent deletes.',
22
+ warning:
23
+ 'Turning this on lets agents act without asking you first, except for payments and permanent deletes. Use it only if you accept that risk.',
24
+ }),
25
+ ]),
26
+ }),
14
27
  Object.freeze({
15
28
  id: 'providers',
16
29
  label: 'Providers',
@@ -30,6 +43,13 @@ export const DEVELOPER_SECTIONS = Object.freeze([
30
43
  warning: OAUTH_RISK_WARNING,
31
44
  provider: 'cursor-oauth',
32
45
  }),
46
+ Object.freeze({
47
+ id: 'devinOAuth',
48
+ label: 'Devin',
49
+ description: 'Show Devin OAuth in Providers and the model picker.',
50
+ warning: OAUTH_RISK_WARNING,
51
+ provider: 'devin-oauth',
52
+ }),
33
53
  Object.freeze({
34
54
  id: 'mimoOAuth',
35
55
  label: 'MiMo',
@@ -74,6 +94,17 @@ export function developerOptionEnabled(id, stored = storedDeveloperConfig()) {
74
94
  return developerOption(id) !== null && normalizeDeveloperConfig(stored)[id] === true;
75
95
  }
76
96
 
97
+ /** `developerOptionEnabled` for a safety bypass: fail-closed. Read straight
98
+ * from the config file, so a missing or malformed config is never restored
99
+ * from a backup (which may hold a stale `true`); any error means off. */
100
+ export function developerOptionEnabledStrict(id) {
101
+ try {
102
+ return developerOption(id) !== null && normalizeDeveloperConfig(readSectionStrict('agent').developer)[id] === true;
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+
77
108
  /** Settings view of every section; `stored` defaults to the on-disk values. */
78
109
  export function developerSettingsView(stored = storedDeveloperConfig()) {
79
110
  const values = normalizeDeveloperConfig(stored);
@@ -206,24 +206,25 @@ function hasTypedEvidence(error) {
206
206
  );
207
207
  }
208
208
 
209
- // The window a provider timeout names (providerTimeoutError's message), or 0.
210
- const timedOutMs = (text) => Number(/timed out after (\d+)ms/i.exec(text)?.[1]) || 0;
209
+ // The window a provider timeout names (providerTimeoutError's message, or a
210
+ // first-byte watchdog's "within Nms"), or 0.
211
+ const timedOutMs = (text) => Number(/(?:timed out after|within) (\d+)ms/i.exec(text)?.[1]) || 0;
211
212
 
212
213
  // A provider timeout before the model produced anything (isFirstByteTimeout):
213
- // the model is slow, not the connection.
214
- function stillThinkingText(error, text) {
214
+ // the model is slow, not the connection. The provider name comes from the
215
+ // caller's own provider config, never from the error.
216
+ function stillThinkingText(error, text, providerLabel) {
215
217
  const ms = Number(error.waitedMs) || timedOutMs(text);
216
- const provider =
217
- typeof error.providerLabel === 'string' && error.providerLabel ? error.providerLabel : 'the provider';
218
+ const provider = typeof providerLabel === 'string' && providerLabel ? providerLabel : 'the provider';
218
219
  const attempts = Number(error.attempts) > 0 ? `; attempts: ${Math.floor(Number(error.attempts))}` : '';
219
220
  return `Model still thinking: no output in ${formatDurationMs(ms)}; ${provider} sent nothing yet${attempts}.`;
220
221
  }
221
222
 
222
223
  // Stamped provider timeout phases and stream cuts. Null when the error names
223
224
  // neither, so the text rules below decide.
224
- function typedStreamFailureText(error, text) {
225
+ function typedStreamFailureText(error, text, providerLabel) {
225
226
  if (!error || typeof error !== 'object') return null;
226
- if (isFirstByteTimeout(error)) return stillThinkingText(error, text);
227
+ if (isFirstByteTimeout(error)) return stillThinkingText(error, text, providerLabel);
227
228
  if (error.timeoutPhase === TIMEOUT_PHASE.IDLE) {
228
229
  const ms = timedOutMs(text);
229
230
  return ms > 0 ? `No progress ${formatDurationMs(ms)}.` : 'No progress.';
@@ -267,7 +268,7 @@ export function presentErrorText(error, options = {}) {
267
268
  if (embeddedError) text = embeddedError;
268
269
  text = oneLine(stripErrorPrefix(text));
269
270
  if (!text) return 'Unknown error';
270
- const streamFailure = typedStreamFailureText(error, text);
271
+ const streamFailure = typedStreamFailureText(error, text, options.providerLabel);
271
272
  if (streamFailure) return streamFailure;
272
273
 
273
274
  if (code === 'PROVIDER_PREFIX_MUTATION' || name === 'ProviderPrefixMutationError') {
@@ -369,12 +370,20 @@ export function presentErrorText(error, options = {}) {
369
370
  return capText(safeErrorDetails(text) || 'Unknown error', max);
370
371
  }
371
372
 
372
- /** Why a provider request is being retried, as one short status phrase. */
373
- export function retryReasonText(error, options = {}) {
374
- return presentErrorText(error, {
375
- surface: options.surface || 'request',
376
- max: options.max ?? 120,
377
- }).replace(/\.$/, '');
373
+ /**
374
+ * Our own wording for a typed timeout retry (`first_response_timeout` /
375
+ * `stalled`), built only from the error's stamps and the window in its
376
+ * message; null for any other class. No error text is ever echoed.
377
+ */
378
+ export function retryTimeoutText(error, reasonClass, providerLabel = null) {
379
+ const text = String(error?.message ?? '');
380
+ const ms = timedOutMs(text);
381
+ if (reasonClass === 'first_response_timeout') {
382
+ if (isFirstByteTimeout(error)) return stillThinkingText(error, text, providerLabel).replace(/\.$/, '');
383
+ return ms > 0 ? `No first response ${formatDurationMs(ms)}` : 'No first response';
384
+ }
385
+ if (reasonClass === 'stalled') return ms > 0 ? `No progress ${formatDurationMs(ms)}` : 'No progress';
386
+ return null;
378
387
  }
379
388
 
380
389
  /** The one retry status line every surface shows: `reason · retry n/max in 15s`. */
@@ -3,14 +3,6 @@ export function enforceOwnerOnlyAclWin32(targetPath: string, options?: { fresh?:
3
3
  /** Test hook: the live per-process ACL helper, or null. */
4
4
  export function ownerOnlyAclHelperForTests(): import('node:child_process').ChildProcess | null;
5
5
 
6
- /** Test/diagnostic hook: helper attachment, pending requests, starts in the restart window. */
7
- export function ownerOnlyAclHelperStats(): {
8
- alive: boolean;
9
- pid: number | null;
10
- pending: number;
11
- recentStarts: number;
12
- };
13
-
14
6
  /** Non-fresh calls verify (and tighten) the real DACL on every call through a shared helper. */
15
7
  export function enforceOwnerOnlyAclWin32Async(targetPath: string, options?: { fresh?: boolean }): Promise<void>;
16
8