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
@@ -11,10 +11,11 @@ export const ACCOUNT_PROVIDERS = Object.freeze([
11
11
  'command-code-oauth',
12
12
  'mimo-oauth',
13
13
  'cursor-oauth',
14
+ 'devin-oauth',
14
15
  'antigravity-oauth',
15
16
  'opencode-go-oauth',
16
17
  ]);
17
- const MAX_ACCOUNTS = 20;
18
+ export const MAX_ACCOUNTS = 20;
18
19
  // `dataDir` defaults to this process's data dir; an isolated runtime passes
19
20
  // the host's dir explicitly while its own MIXDOG_DATA_DIR points elsewhere.
20
21
  const file = (dataDir = resolvePluginData()) => join(dataDir, 'provider-accounts.json');
@@ -95,16 +96,32 @@ export function providerAccountPolicy(pool) {
95
96
  return pool?.policy === 'headroom' ? 'headroom' : 'limit';
96
97
  }
97
98
 
98
- function normalizePool(pool) {
99
+ // A reading stored before per-model gating is stale: it is dropped on read so
100
+ // it steers nothing until the account is measured again.
101
+ function dropStaleUsage(provider, row) {
102
+ if (!staleQuotaWindows(provider, row.usage?.windows)) return row;
103
+ const fresh = { ...row };
104
+ delete fresh.usage;
105
+ return fresh;
106
+ }
107
+
108
+ function normalizePool(provider, pool) {
99
109
  if (!pool) return pool;
100
110
  const policy = providerAccountPolicy(pool);
101
- return { ...normalizeLabels(pool), policy, auto: policy !== 'manual' };
111
+ const labelled = normalizeLabels(pool);
112
+ const accounts = labelled.accounts.map((row) => dropStaleUsage(provider, row));
113
+ return { ...labelled, accounts, policy, auto: policy !== 'manual' };
102
114
  }
103
115
 
104
116
  export function readProviderAccountPool(provider, dataDir = resolvePluginData()) {
105
117
  requireAccountProvider(provider);
106
118
  return (
107
- normalizePool(read(dataDir).providers[provider]) || { accounts: [], selectedId: null, auto: true, policy: 'limit' }
119
+ normalizePool(provider, read(dataDir).providers[provider]) || {
120
+ accounts: [],
121
+ selectedId: null,
122
+ auto: true,
123
+ policy: 'limit',
124
+ }
108
125
  );
109
126
  }
110
127
 
@@ -124,7 +141,7 @@ async function update(provider, mutate) {
124
141
  },
125
142
  { mode: 0o600, secret: true, fsyncDir: true, backup: true }
126
143
  );
127
- return normalizePool(result.providers[provider]);
144
+ return normalizePool(provider, result.providers[provider]);
128
145
  }
129
146
 
130
147
  /**
@@ -256,21 +273,55 @@ export async function recordProviderAccountUsage(provider, id, snapshot) {
256
273
  label: String(row.label || '').slice(0, 40),
257
274
  usedPct: typeof row.usedPct === 'number' && Number.isFinite(row.usedPct) ? row.usedPct : null,
258
275
  resetAt: typeof row.resetAt === 'number' && Number.isFinite(row.resetAt) ? row.resetAt : null,
276
+ ...windowScopeFields(row),
259
277
  }));
260
278
  if (!readProviderAccountPool(provider).accounts.some((row) => row.id === id)) return;
261
279
  await update(provider, (pool) => {
262
280
  const row = pool.accounts.find((entry) => entry.id === id);
263
281
  if (!row) return;
264
282
  row.usage = { windows, checkedAt: Date.now() };
265
- // A fresh observation of available quota releases a prior typed refusal.
266
- // Only a MEASURED window can testify: a provider that reports a window
267
- // without a percentage says nothing about that meter, and requiring a
268
- // number from it kept accounts refused that every real reading had freed.
269
- const measured = windows.filter((window) => window.usedPct !== null);
270
- if (measured.length && measured.every((window) => window.usedPct < 100)) clearAccountBlock(row);
283
+ // A fresh observation of available quota releases a prior refusal. Only
284
+ // the meters the refusal drew on testify (a full Claude/GPT bucket says
285
+ // nothing about a Gemini refusal), and only a MEASURED window: a provider
286
+ // that reports a window without a percentage says nothing about that
287
+ // meter, and requiring a number from it kept accounts refused that every
288
+ // real reading had freed. Each bucket's block is weighed on its own.
289
+ const lifted = (block) => {
290
+ const measured = blockEvidenceWindows(block).filter((window) => window.usedPct !== null);
291
+ return measured.length > 0 && measured.every((window) => window.usedPct < 100);
292
+ };
293
+ setAccountBlocks(
294
+ row,
295
+ accountBlocks(row).filter((block) => !lifted(block))
296
+ );
271
297
  });
272
298
  }
273
299
 
300
+ // The model-id tokens a window's gate names, as recorded: lowercased and bounded.
301
+ function modelTokens(value) {
302
+ if (!Array.isArray(value)) return null;
303
+ return value
304
+ .slice(0, 12)
305
+ .map((token) =>
306
+ String(token || '')
307
+ .trim()
308
+ .toLowerCase()
309
+ .slice(0, 80)
310
+ )
311
+ .filter(Boolean);
312
+ }
313
+
314
+ function windowScopeFields(row) {
315
+ const models = modelTokens(row.models);
316
+ const except = modelTokens(row.except);
317
+ const limit = typeof row.limit === 'string' ? row.limit.trim().slice(0, 80) : '';
318
+ const scope = {};
319
+ if (models) scope.models = models;
320
+ else if (except?.length) scope.except = except;
321
+ if (limit) scope.limit = limit;
322
+ return scope;
323
+ }
324
+
274
325
  // A refusal window is an AVOIDANCE hint for the roster, not the retry schedule —
275
326
  // the request-level retry honours Retry-After on its own. Generous enough to
276
327
  // cover a full five-hour burst window, tight enough that a header asking for
@@ -283,18 +334,29 @@ const MAX_BLOCK_MS = 6 * 60 * 60_000;
283
334
  // early can tell us anything.
284
335
  // `options.quota` records that the block came from a typed quota refusal or an
285
336
  // exhausted usage window, so later sends still count it as quota evidence.
337
+ // `options.limit` is the server's id of the limit the refusal named; the block
338
+ // then covers that limit's windows. `options.reset` marks `until` as the
339
+ // quota reset the server named: it is kept as given (a fresh reading showing
340
+ // room still lifts it early), while advice and inferred delays stay capped.
341
+ // A new refusal of the same model replaces that model's block; every other
342
+ // block still running keeps its own deadline, so refusing one bucket never
343
+ // frees another.
286
344
  export async function blockProviderAccount(provider, id, until, model = '', source = 'inferred', options = {}) {
287
345
  return update(provider, (pool) => {
288
346
  const row = pool.accounts.find((entry) => entry.id === id);
289
347
  if (!row) return;
290
- row.blockedUntil = Math.min(until, Date.now() + MAX_BLOCK_MS);
348
+ const now = Date.now();
349
+ const block = { blockedUntil: options.reset === true ? until : Math.min(until, now + MAX_BLOCK_MS) };
291
350
  const scope = String(model || '').trim();
292
- if (scope) row.blockedModel = scope;
293
- else delete row.blockedModel;
294
- if (source === 'retry-after') row.blockedSource = source;
295
- else delete row.blockedSource;
296
- if (options.quota === true) row.blockedQuota = true;
297
- else delete row.blockedQuota;
351
+ if (scope) block.blockedModel = scope;
352
+ const limit = String(options.limit || '').trim();
353
+ if (limit) block.blockedLimit = limit;
354
+ if (source === 'retry-after') block.blockedSource = source;
355
+ if (options.quota === true) block.blockedQuota = true;
356
+ const running = accountBlocks(row).filter(
357
+ (entry) => entry.blockedUntil > now && String(entry.blockedModel || '') !== scope
358
+ );
359
+ setAccountBlocks(row, [...running, block]);
298
360
  });
299
361
  }
300
362
 
@@ -312,41 +374,136 @@ export async function clearProviderAccountQuotaState(provider, id) {
312
374
  });
313
375
  }
314
376
 
377
+ const BLOCK_FIELDS = Object.freeze(['blockedUntil', 'blockedModel', 'blockedLimit', 'blockedSource', 'blockedQuota']);
378
+
315
379
  function clearAccountBlock(row) {
316
- delete row.blockedUntil;
317
- delete row.blockedModel;
318
- delete row.blockedSource;
319
- delete row.blockedQuota;
380
+ for (const key of BLOCK_FIELDS) delete row[key];
381
+ delete row.blockedBuckets;
382
+ }
383
+
384
+ const blockOf = (source) =>
385
+ Object.fromEntries(BLOCK_FIELDS.filter((key) => source[key] !== undefined).map((key) => [key, source[key]]));
386
+
387
+ // Every block the account holds, oldest first, each read beside the account's
388
+ // usage. The row's own fields carry the latest; earlier blocks of other
389
+ // buckets still running sit in `blockedBuckets`.
390
+ function accountBlocks(row) {
391
+ const blocks = [
392
+ ...(Array.isArray(row?.blockedBuckets) ? row.blockedBuckets : []),
393
+ ...(row?.blockedUntil === undefined ? [] : [blockOf(row)]),
394
+ ];
395
+ return blocks.map((block) => ({ ...block, usage: row.usage }));
320
396
  }
321
397
 
322
- // Single-token weekly labels ("7D Fable", "7D Opus") belong to that Claude
323
- // family only. A full Fable window must not idle an Opus request. Unscoped
324
- // 5H/7D meters and multi-word surfaces ("7D OAuth apps") still apply to every
325
- // model.
398
+ function setAccountBlocks(row, blocks) {
399
+ clearAccountBlock(row);
400
+ if (blocks.length) Object.assign(row, blockOf(blocks.at(-1)));
401
+ if (blocks.length > 1) row.blockedBuckets = blocks.slice(0, -1).map(blockOf);
402
+ return row;
403
+ }
404
+
405
+ // The row holding only the blocks `keep` accepts (none by default), so a check
406
+ // can weigh the usage windows, or only the quota refusals, on their own.
407
+ export function providerAccountWithBlocks(row, keep = () => false) {
408
+ return row && setAccountBlocks({ ...row }, accountBlocks(row).filter(keep));
409
+ }
410
+
411
+ // Labels every current reading scopes with `models`/`except`. A stored window
412
+ // carrying one of them without a scope predates per-model gating and would
413
+ // read as account-wide, so its whole reading is stale.
414
+ const SCOPED_WINDOW_LABELS = Object.freeze({
415
+ 'anthropic-oauth': ['EXTRA'],
416
+ 'antigravity-oauth': ['7D Argon', '5H Claude/GPT', '7D Claude/GPT'],
417
+ 'cursor-oauth': ['Basic', 'API'],
418
+ });
419
+
420
+ export function staleQuotaWindows(provider, windows) {
421
+ const labels = SCOPED_WINDOW_LABELS[provider] || [];
422
+ return (Array.isArray(windows) ? windows : []).some(
423
+ (window) =>
424
+ labels.includes(String(window?.label || '').trim()) &&
425
+ !Array.isArray(window.models) &&
426
+ !Array.isArray(window.except)
427
+ );
428
+ }
429
+
430
+ // Which models one quota window gates — the single rule every window and every
431
+ // block follows. The usage reading scopes a window to the server's quota
432
+ // bucket: `models` gates only model ids naming one of those tokens (an empty
433
+ // list gates nothing: a display-only meter such as paid extra usage), `except`
434
+ // gates every model but those. A window without either keeps the older label
435
+ // rule: a single-token weekly label ("7D Fable", "7D Opus") belongs to that
436
+ // family only, and every other window (5H/7D, multi-word surfaces such as
437
+ // "7D OAuth apps") gates every model.
326
438
  const FAMILY_QUOTA_WINDOW = /^7D\s+([A-Za-z][A-Za-z0-9]*)$/i;
327
439
 
440
+ function windowScope(window) {
441
+ if (Array.isArray(window?.models)) return { models: window.models };
442
+ if (Array.isArray(window?.except) && window.except.length) return { except: window.except };
443
+ const family = FAMILY_QUOTA_WINDOW.exec(String(window?.label || '').trim())?.[1];
444
+ return family ? { models: [family.toLowerCase()] } : null;
445
+ }
446
+
447
+ const displayOnly = (window) => Array.isArray(window?.models) && window.models.length === 0;
448
+
449
+ const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
450
+
451
+ function namesModel(model, tokens) {
452
+ const id = String(model).toLowerCase();
453
+ return tokens.some((token) => new RegExp(`(?:^|[-_/.@])${escapeRegExp(token)}(?:[-_/.@]|$)`).test(id));
454
+ }
455
+
328
456
  export function quotaWindowAppliesToModel(window, model) {
329
- const match = FAMILY_QUOTA_WINDOW.exec(String(window?.label || '').trim());
330
- if (!match) return true;
331
- const family = match[1].toLowerCase();
332
- const id = String(model || '').toLowerCase();
333
- if (!id) return false;
334
- return new RegExp(`(?:^|[-_/.@])${family}(?:[-_/.@]|$)`).test(id);
457
+ const scope = windowScope(window);
458
+ if (!scope) return true;
459
+ if (!model) return false;
460
+ return scope.models ? namesModel(model, scope.models) : !namesModel(model, scope.except);
461
+ }
462
+
463
+ // The meters a block stands for: the windows of the limit the refusal named,
464
+ // else the scoped windows that gate the model that earned it.
465
+ function blockWindows(row) {
466
+ const windows = row?.usage?.windows || [];
467
+ const limit = String(row?.blockedLimit || '');
468
+ const named = limit ? windows.filter((window) => window.limit === limit) : [];
469
+ if (named.length) return named;
470
+ const earned = String(row?.blockedModel || '').trim();
471
+ return earned ? windows.filter((window) => windowScope(window) && quotaWindowAppliesToModel(window, earned)) : [];
472
+ }
473
+
474
+ // The windows a fresh reading must show below 100% to lift a block: the
475
+ // block's own meters plus every meter, scoped or shared, the model that earned
476
+ // it (or, for an account-wide block, any model) draws on.
477
+ function blockEvidenceWindows(row) {
478
+ const earned = String(row?.blockedModel || '').trim();
479
+ const gating = (row?.usage?.windows || []).filter((window) =>
480
+ earned ? quotaWindowAppliesToModel(window, earned) : !displayOnly(window)
481
+ );
482
+ return [...new Set([...blockWindows(row), ...gating])];
335
483
  }
336
484
 
337
485
  // A refusal earned by one model must not idle a model that draws on a
338
- // different meter — the same rule the usage windows already follow. Two models
339
- // share a block when one scoped window covers both; when the account exposes no
340
- // scoped meter at all (a plain 5H/7D subscription) the block stays account-wide,
341
- // because there is no narrower category to attribute it to.
486
+ // different meter — the same rule the usage windows follow. Two models share a
487
+ // block when one of the block's windows gates both; when the account exposes
488
+ // no scoped meter for the model (a plain 5H/7D subscription) the block stays
489
+ // account-wide, because there is no narrower category to attribute it to.
342
490
  function blockAppliesToModel(row, model) {
343
- const scope = String(row?.blockedModel || '').trim();
344
- if (!scope || !model || scope === String(model)) return true;
345
- const scoped = (row?.usage?.windows || []).filter((window) =>
346
- FAMILY_QUOTA_WINDOW.test(String(window?.label || '').trim())
347
- );
348
- if (!scoped.length) return true;
349
- return scoped.some((window) => quotaWindowAppliesToModel(window, scope) && quotaWindowAppliesToModel(window, model));
491
+ const earned = String(row?.blockedModel || '').trim();
492
+ if (!earned || !model || earned === String(model)) return true;
493
+ const windows = blockWindows(row);
494
+ if (!windows.length) return true;
495
+ return windows.some((window) => quotaWindowAppliesToModel(window, model));
496
+ }
497
+
498
+ // When the blocks holding `model` lift (every block when no model is named):
499
+ // the latest of their deadlines, or null when none holds it. Past deadlines
500
+ // are returned as they are, so a caller compares the result with its clock.
501
+ export function providerAccountBlockedUntil(row, model = '') {
502
+ const ends = accountBlocks(row)
503
+ .filter((block) => blockAppliesToModel(block, model))
504
+ .map((block) => Number(block.blockedUntil))
505
+ .filter(Number.isFinite);
506
+ return ends.length ? Math.max(...ends) : null;
350
507
  }
351
508
 
352
509
  // Earliest moment any of the given exhausted accounts can serve `model` again,
@@ -360,8 +517,9 @@ export function providerAccountsResetAt(rows, now = Date.now(), model = '', opti
360
517
  if (!providerAccountExhausted(row, now, model, options)) continue;
361
518
  const times = [];
362
519
  let unknown = false;
363
- if (row.blockedUntil > now && blockAppliesToModel(row, model)) {
364
- if (row.blockedSource === 'retry-after') times.push(row.blockedUntil);
520
+ for (const block of accountBlocks(row)) {
521
+ if (!(block.blockedUntil > now) || !blockAppliesToModel(block, model)) continue;
522
+ if (block.blockedSource === 'retry-after') times.push(block.blockedUntil);
365
523
  else unknown = true;
366
524
  }
367
525
  if (options.useCredits !== true) {
@@ -381,39 +539,47 @@ function windowFull(row, window, now) {
381
539
  }
382
540
 
383
541
  export function providerAccountExhausted(row, now = Date.now(), model = '', options = {}) {
384
- if (row?.blockedUntil > now && blockAppliesToModel(row, model)) return true;
542
+ if (accountBlocks(row).some((block) => block.blockedUntil > now && blockAppliesToModel(block, model))) return true;
385
543
  if (options.useCredits === true) return false;
386
544
  return (row?.usage?.windows || []).some(
387
545
  (window) => quotaWindowAppliesToModel(window, model) && windowFull(row, window, now)
388
546
  );
389
547
  }
390
548
 
549
+ // The display name of a scoped limit: the family of a legacy "7D Fable" label,
550
+ // else the label without its window prefix ("5H Claude/GPT" → "Claude/GPT").
391
551
  function quotaWindowFamily(window) {
392
- return FAMILY_QUOTA_WINDOW.exec(String(window?.label || '').trim())?.[1] || '';
552
+ const label = String(window?.label || '').trim();
553
+ return FAMILY_QUOTA_WINDOW.exec(label)?.[1] || label.replace(/^(?:5H|7D)\s+/i, '') || label;
393
554
  }
394
555
 
395
556
  // Which limit currently holds the account, independent of any one model:
396
557
  // `{ scope: 'account' }` when every request is refused (an unscoped window is
397
558
  // full, or a block with no narrower meter to attribute it to), `{ scope:
398
- // 'model', families }` when only model-family meters ("7D Fable") or a block
399
- // attributed to one are spent, so other models still run — else null.
559
+ // 'model', families }` when only scoped meters ("7D Fable", "5H Claude/GPT")
560
+ // or a block attributed to one are spent, so other models still run — else null.
400
561
  export function providerAccountLimit(row, now = Date.now(), options = {}) {
401
562
  if (!row) return null;
402
563
  const windows = row.usage?.windows || [];
403
- const blocked = row.blockedUntil > now;
404
- const scope = String(row.blockedModel || '').trim();
405
- const scopedWindows = windows.filter((window) => quotaWindowFamily(window));
406
- // A block counts as family-scoped only when a family meter covers the model
407
- // that earned it; any other refusal is treated as the account's.
408
- const covering = scope ? scopedWindows.filter((window) => quotaWindowAppliesToModel(window, scope)) : [];
409
- if (blocked && !covering.length) return { scope: 'account' };
564
+ // A block counts as scoped only when scoped meters cover it; any other
565
+ // refusal is treated as the account's.
566
+ const covering = accountBlocks(row)
567
+ .filter((block) => block.blockedUntil > now)
568
+ .map(blockWindows);
569
+ if (covering.some((meters) => !meters.length || meters.some((window) => !windowScope(window)))) {
570
+ return { scope: 'account' };
571
+ }
410
572
  const credits = options.useCredits === true;
411
- if (!credits && windows.some((window) => !quotaWindowFamily(window) && windowFull(row, window, now))) {
573
+ if (!credits && windows.some((window) => !windowScope(window) && windowFull(row, window, now))) {
412
574
  return { scope: 'account' };
413
575
  }
414
- const families = new Set(blocked ? covering.map(quotaWindowFamily) : []);
576
+ const families = new Set(covering.flat().map(quotaWindowFamily));
415
577
  if (!credits) {
416
- for (const window of scopedWindows) if (windowFull(row, window, now)) families.add(quotaWindowFamily(window));
578
+ for (const window of windows) {
579
+ if (windowScope(window) && !displayOnly(window) && windowFull(row, window, now)) {
580
+ families.add(quotaWindowFamily(window));
581
+ }
582
+ }
417
583
  }
418
584
  return families.size ? { scope: 'model', families: [...families] } : null;
419
585
  }
@@ -431,6 +597,12 @@ export function providerAccountExhaustedForModelOnly(row, now = Date.now(), mode
431
597
  // enough that yesterday's number cannot send every fallback to one account.
432
598
  const USAGE_EVIDENCE_MAX_AGE_MS = 60 * 60_000;
433
599
 
600
+ /** Whether a usage reading taken at `checkedAt` may still steer a decision. */
601
+ export function quotaReadingFresh(checkedAt, now = Date.now()) {
602
+ const at = Number(checkedAt);
603
+ return at > 0 && now - at <= USAGE_EVIDENCE_MAX_AGE_MS;
604
+ }
605
+
434
606
  // Remaining headroom across every window that applies to this model, or null
435
607
  // when nothing recent measured the account. The fullest window decides: an
436
608
  // account with 4% of its burst window left cannot serve now, however untouched
@@ -26,16 +26,27 @@ function providerSecretEnv(provider) {
26
26
  .toUpperCase()}_APIKEY`;
27
27
  }
28
28
 
29
- function environmentApiKey(provider) {
30
- const id = String(provider || '').trim();
29
+ // The environment variable that supplies a provider's key, in the resolver's
30
+ // precedence (standard name, MIXDOG_AGENT_<ID>_APIKEY, then aliases); null when none is set.
31
+ function environmentKeyName(id) {
31
32
  const standardEnv = AGENT_PROVIDER_ENV[id];
32
- if (standardEnv && process.env[standardEnv]) return process.env[standardEnv];
33
+ if (standardEnv && process.env[standardEnv]) return standardEnv;
33
34
  const mixdogEnv = providerSecretEnv(id);
34
- if (process.env[mixdogEnv]) return process.env[mixdogEnv];
35
- for (const alias of AGENT_PROVIDER_ENV_ALIASES[id] || []) {
36
- if (process.env[alias]) return process.env[alias];
37
- }
38
- return null;
35
+ if (process.env[mixdogEnv]) return mixdogEnv;
36
+ return (AGENT_PROVIDER_ENV_ALIASES[id] || []).find((alias) => process.env[alias]) ?? null;
37
+ }
38
+
39
+ function environmentApiKey(provider) {
40
+ const name = environmentKeyName(String(provider || '').trim());
41
+ return name ? process.env[name] : null;
42
+ }
43
+
44
+ /** Name of the environment variable getAgentApiKey would return for `provider`
45
+ * (it wins over any stored key), or null when the effective key is not an
46
+ * environment one. Provenance for the setup views; never the value. */
47
+ export function agentApiKeyEnvName(provider) {
48
+ if (modelProxyClientMode()) return null;
49
+ return environmentKeyName(String(provider || '').trim());
39
50
  }
40
51
 
41
52
  /** Presence-only credential probe. On Windows this is a cheap DPAPI-file
@@ -0,0 +1,29 @@
1
+ // Display names for provider ids; an unknown id displays as itself.
2
+ const PROVIDER_DISPLAY_NAMES = new Map([
3
+ ['openai-oauth', 'OpenAI OAuth'],
4
+ ['anthropic-oauth', 'Anthropic OAuth'],
5
+ ['grok-oauth', 'Grok OAuth'],
6
+ ['muse-code-oauth', 'Muse Code'],
7
+ ['command-code-oauth', 'Command Code'],
8
+ ['mimo-oauth', 'MiMo'],
9
+ ['cursor-oauth', 'Cursor OAuth'],
10
+ ['devin-oauth', 'Devin'],
11
+ ['antigravity-oauth', 'Antigravity OAuth'],
12
+ ['openai', 'OpenAI API'],
13
+ ['openai-api', 'OpenAI API'],
14
+ ['anthropic', 'Anthropic API'],
15
+ ['anthropic-api', 'Anthropic API'],
16
+ ['gemini', 'Gemini API'],
17
+ ['gemini-api', 'Gemini API'],
18
+ ['xai', 'xAI API'],
19
+ ['xai-api', 'xAI API'],
20
+ ['deepseek', 'DeepSeek API'],
21
+ ['deepseek-api', 'DeepSeek API'],
22
+ ['opencode-go-oauth', 'OpenCode Go OAuth'],
23
+ ['openrouter', 'OpenRouter'],
24
+ ['mixdog-local', 'Local Provider'],
25
+ ['default', 'Default'],
26
+ ]);
27
+
28
+ export const providerDisplayName = (provider) =>
29
+ PROVIDER_DISPLAY_NAMES.get(String(provider || '').toLowerCase()) ?? (provider || 'Provider');
@@ -8,6 +8,8 @@ export const TIMEOUT_PHASE = Object.freeze({
8
8
  FIRST_CHUNK: 'first-chunk',
9
9
  // A stream that already produced output went silent.
10
10
  IDLE: 'idle',
11
+ // A sign-in token refresh did not answer: an auth problem, not a slow model.
12
+ AUTH: 'auth',
11
13
  });
12
14
 
13
15
  /** A timeout before the model produced anything: headers or first chunk. */
@@ -4,6 +4,8 @@ export interface RoutineTrigger {
4
4
  event?: string;
5
5
  }
6
6
 
7
+ export declare const ROUTINE_QUIET: 'ROUTINE_QUIET';
8
+ export declare function isRoutineQuietReply(text: unknown): boolean;
7
9
  export declare function routineTriggerHeader(input: {
8
10
  kind: string;
9
11
  name: string;
@@ -9,6 +9,15 @@ import { localTimestampParts } from './time-format.mjs';
9
9
  const HEADER_PREFIX = '[Routine trigger]';
10
10
  const KINDS = new Set(['schedule', 'webhook']);
11
11
 
12
+ /** The whole reply of a routine turn with nothing new to tell: kept in the
13
+ * record, never painted, previewed, counted unread or notified. */
14
+ export const ROUTINE_QUIET = 'ROUTINE_QUIET';
15
+
16
+ /** Whether a reply is exactly the routine quiet sentinel. */
17
+ export function isRoutineQuietReply(text) {
18
+ return String(text ?? '').trim() === ROUTINE_QUIET;
19
+ }
20
+
12
21
  /** The first line of a routine turn's prompt: which routine fired, when (local
13
22
  * time and zone: the turn gets no other date), and how to treat the turn.
14
23
  * It is the routine's only conduct text. Only the turn's content carries it,
@@ -24,10 +33,53 @@ export function routineTriggerHeader({ kind, name, project = '', now = new Date(
24
33
  'Payments and permanent deletes are refused here: report that and suggest doing it from the chat.',
25
34
  'If a sign-in, code or CAPTCHA blocks you, stop and say which site needs the user.',
26
35
  'Reply in this chat in plain words, without tool names; if you handed work off, say it started — its result arrives here later.',
36
+ `Tell what is new in a fitting tone; if nothing is worth telling, reply with exactly ${ROUTINE_QUIET}.`,
27
37
  ...(project ? [`Project folder: ${project}`] : []),
28
38
  ].join(' ');
29
39
  }
30
40
 
41
+ /** The whole reply of a heartbeat turn that has nothing to tell the user. */
42
+ export const HEARTBEAT_QUIET = 'HEARTBEAT_QUIET';
43
+ /** The routine name a heartbeat turn runs under and its hidden session's source. */
44
+ export const HEARTBEAT_SOURCE = 'heartbeat';
45
+ /** A heartbeat turn's bounds: its tool calls; when it is told to reply with no
46
+ * more tools (session/loop/turn-limits.mjs); when it is cut off without a
47
+ * reply (session-runtime/routine-home-turn.mjs). */
48
+ export const HEARTBEAT_TURN_LIMITS = Object.freeze({ maxToolCalls: 8, wrapUpMs: 60_000, wallClockMs: 90_000 });
49
+
50
+ // Mixy's hidden heartbeat sessions (mixy-heartbeat.mjs wakeMixy), marked in
51
+ // this process from creation until their close lands. Only the mark counts:
52
+ // stored fields (source, owner) can be supplied by any session create.
53
+ const heartbeatSessions = new Set();
54
+
55
+ /** Mark `sessionId` as a heartbeat session; `unmark` ends it. */
56
+ export function markHeartbeatSession(sessionId, unmark = false) {
57
+ const id = String(sessionId || '');
58
+ if (unmark) heartbeatSessions.delete(id);
59
+ else if (id) heartbeatSessions.add(id);
60
+ }
61
+
62
+ /** Whether `sessionId` is a marked heartbeat session. */
63
+ export function isHeartbeatSession(sessionId) {
64
+ return heartbeatSessions.has(String(sessionId || ''));
65
+ }
66
+
67
+ /** The first line of a heartbeat turn (session-runtime/mixy-heartbeat.mjs):
68
+ * a system check-in, never the user, and it grants no approval. */
69
+ export function heartbeatTriggerHeader({ now = new Date() } = {}) {
70
+ const at = localTimestampParts(now);
71
+ return [
72
+ `${HEADER_PREFIX} Heartbeat: a system check-in from Mixdog, not a message from the user; it grants no approval.`,
73
+ ...(at ? [`Local time: ${at.date} ${at.time.slice(0, 5)} ${at.timeZone} (${at.offset}).`] : []),
74
+ 'Nobody is watching this turn and nothing asks for approval: act within your permissions.',
75
+ 'Payments and permanent deletes are refused here.',
76
+ 'Do not start agents, task sessions, session sends, Goals or background commands here: this session closes when the turn ends, so their results would reach nobody; propose them instead.',
77
+ 'Handle the items below as your heartbeat instructions say, finishing in this turn:',
78
+ 'only its final reply is posted in your chat with the user, as your message.',
79
+ `If nothing is worth telling the user, reply with exactly ${HEARTBEAT_QUIET} and nothing else.`,
80
+ ].join(' ');
81
+ }
82
+
31
83
  /** `text` without the header line a routine turn opens with. */
32
84
  export function stripRoutineTriggerHeader(text) {
33
85
  const value = String(text ?? '');
@@ -1,15 +1,7 @@
1
1
  export interface SensitiveScope {
2
+ /** A Mixdog home or data folder, as path segments; only its secret subset is denied. */
2
3
  root: string[];
3
- /** Subfolders of the root that stay reachable. */
4
- except: readonly string[];
5
- /** Whole tree (a registered private root) or Mixdog's secret subset. */
6
- whole: boolean;
7
4
  }
8
- export function sensitivePathSegments(path: string): string[];
9
- export function matchesSensitivePath(
10
- path: string,
11
- within?: readonly SensitiveScope[],
12
- options?: { envTemplates?: boolean }
13
- ): boolean;
5
+ export function matchesSensitivePath(path: string, within?: readonly SensitiveScope[]): boolean;
14
6
  export function mixdogSensitiveScopes(extraData?: readonly string[]): SensitiveScope[];
15
7
  export function isSensitiveLocalPath(path: string, options?: { extraData?: readonly string[] }): boolean;