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
@@ -274,7 +274,7 @@ function* walkContentParts(content) {
274
274
  * beforehand by preparePdfTextForProvider, so one reaching here is a bug.
275
275
  * Null when the part is not a file at all.
276
276
  */
277
- export function textFormOfFilePart(part) {
277
+ function textFormOfFilePart(part) {
278
278
  if (part?.type !== 'file' && part?.type !== 'document') return null;
279
279
  try {
280
280
  const file = fileInfo(part);
@@ -840,6 +840,56 @@ function partCarriesLiveMedia(part) {
840
840
  return Array.isArray(part.content) && contentCarriesLiveMedia(part.content);
841
841
  }
842
842
 
843
+ /** An image part in any spelling the providers use: inline, URL, file id or
844
+ * URI, Gemini inline data. */
845
+ function isImagePart(part) {
846
+ return Boolean(
847
+ part.type === 'image' ||
848
+ part.type === 'image_url' ||
849
+ part.type === 'input_image' ||
850
+ imageUrlFromPart(part) ||
851
+ imageFileIdFromPart(part) ||
852
+ imageFileUriFromPart(part) ||
853
+ geminiInlineInfo(part)
854
+ );
855
+ }
856
+
857
+ function mapPartText(part, mapText) {
858
+ if (typeof part === 'string') return mapText(part);
859
+ if (!part || typeof part !== 'object') return part;
860
+ // Media and attachment references are encodings, never text.
861
+ if (isAttachmentReference(part) || isImagePart(part) || fileInfo(part)) return part;
862
+ for (const key of ['text', 'content']) {
863
+ if (typeof part[key] === 'string') {
864
+ const next = mapText(part[key]);
865
+ return next === part[key] ? part : { ...part, [key]: next };
866
+ }
867
+ }
868
+ if (Array.isArray(part.content)) {
869
+ const nextContent = mapContentText(part.content, mapText);
870
+ if (nextContent !== part.content) return { ...part, content: nextContent };
871
+ }
872
+ return part;
873
+ }
874
+
875
+ /** `content` with `mapText` applied to its text and nothing else: a string,
876
+ * the text a text part or a part's string content carries (textFromPart),
877
+ * and nested part content; media parts and attachment references stay as
878
+ * they are. The same value when nothing changed. */
879
+ export function mapContentText(content, mapText) {
880
+ if (typeof content === 'string') return mapText(content);
881
+ const parts = contentParts(content);
882
+ if (!parts) return content;
883
+ let changed = false;
884
+ const out = parts.map((part) => {
885
+ const next = mapPartText(part, mapText);
886
+ if (next !== part) changed = true;
887
+ return next;
888
+ });
889
+ if (!changed) return content;
890
+ return Array.isArray(content) ? out : { ...content, content: out };
891
+ }
892
+
843
893
  function sanitizePartForStoredHistory(part) {
844
894
  if (typeof part === 'string') return part;
845
895
  if (!part || typeof part !== 'object') return part;
@@ -849,15 +899,7 @@ function sanitizePartForStoredHistory(part) {
849
899
  // reloading the session keeps the provider prompt cache intact.
850
900
  const storedImage = part.type === 'image' ? storedImagePart(part) : null;
851
901
  if (storedImage) return storedImage;
852
- if (
853
- part.type === 'image' ||
854
- part.type === 'image_url' ||
855
- part.type === 'input_image' ||
856
- imageUrlFromPart(part) ||
857
- imageFileIdFromPart(part) ||
858
- imageFileUriFromPart(part) ||
859
- geminiInlineInfo(part)
860
- ) {
902
+ if (isImagePart(part)) {
861
903
  return { type: 'text', text: storedHistoryImagePlaceholder(part) };
862
904
  }
863
905
  const file = fileInfo(part);
@@ -5,7 +5,14 @@ export const MODEL_PROXY_OPERATIONS: Readonly<{
5
5
  resolve: 'modelProxyResolve';
6
6
  heartbeat: 'modelProxyHeartbeat';
7
7
  }>;
8
- export type ModelProxyMethod = 'send' | 'listModels' | 'refreshModels' | 'isAvailable' | 'resolveWireRoute' | 'providerConfig';
8
+ export type ModelProxyMethod =
9
+ | 'send'
10
+ | 'listModels'
11
+ | 'refreshModels'
12
+ | 'isAvailable'
13
+ | 'quotaUsable'
14
+ | 'resolveWireRoute'
15
+ | 'providerConfig';
9
16
  export const MODEL_PROXY_METHODS: ReadonlyArray<ModelProxyMethod>;
10
17
  export const MODEL_PROXY_HEARTBEAT_MS: number;
11
18
  export const MODEL_PROXY_LEASE_MS: number;
@@ -34,6 +34,7 @@ export const MODEL_PROXY_METHODS = Object.freeze([
34
34
  'listModels',
35
35
  'refreshModels',
36
36
  'isAvailable',
37
+ 'quotaUsable',
37
38
  'resolveWireRoute',
38
39
  'providerConfig',
39
40
  ]);
@@ -8,6 +8,7 @@
8
8
  // so a new process (or an offline one) starts from the newest version this
9
9
  // machine has seen rather than from a floor that only moves with a release.
10
10
 
11
+ import { runAbortable } from '../../../shared/abort-race.mjs';
11
12
  import { readLastKnownVersion, rememberLastKnownVersion } from './client-version-store.mjs';
12
13
 
13
14
  const SEMVER = /^\d+\.\d+\.\d+$/;
@@ -41,11 +42,18 @@ export async function parseNpmLatest(res) {
41
42
  return SEMVER.test(v) ? v : null;
42
43
  }
43
44
 
44
- /** One GET of `url` parsed to a version; null when offline, slow, or unrecognised. */
45
+ /** One GET of `url` parsed to a version; null when offline, slow, or unrecognised.
46
+ * The deadline settles the lookup itself instead of relying on the request
47
+ * and its body read to honour the abort: a send awaits this lookup, and its
48
+ * timer is unref'd, so once the abort tears the socket down nothing else is
49
+ * left to wake a read that missed it. */
45
50
  export async function fetchRemoteVersion(url, parse, timeoutMs = TIMEOUT_MS) {
51
+ const signal = AbortSignal.timeout(timeoutMs);
46
52
  try {
47
- const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
48
- return res.ok ? (await parse(res)) || null : null;
53
+ return await runAbortable(signal, async () => {
54
+ const res = await fetch(url, { signal });
55
+ return res.ok ? (await parse(res)) || null : null;
56
+ });
49
57
  } catch {
50
58
  return null;
51
59
  }
@@ -4,6 +4,8 @@ import { join } from 'node:path';
4
4
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
5
5
  import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
6
6
  import { cursorTokenExpiry } from './cursor-auth.mjs';
7
+ import { describeDevinOAuthCredentials } from './devin-auth.mjs';
8
+ import { isSignedInTokenDocument } from './lib/oauth-token-utils.mjs';
7
9
  import { isValidMuseCredentialDocument } from './muse-code-oauth-tokens.mjs';
8
10
  import { isValidOpenCodeGoCredentialDocument } from './opencode-go-oauth-tokens.mjs';
9
11
  import { developerOptionEnabled, developerOptionForProvider } from '../../../shared/developer-options.mjs';
@@ -151,7 +153,7 @@ function openAIOAuthState() {
151
153
  join(resolvePluginData(), 'openai-oauth.json'),
152
154
  ];
153
155
  return memoProbe('openai-oauth', paths, () =>
154
- resolveProbeState(paths, (docs) => docs.some((raw) => !!(raw?.access_token && raw?.refresh_token)))
156
+ resolveProbeState(paths, (docs) => docs.some((raw) => isSignedInTokenDocument(raw)))
155
157
  );
156
158
  }
157
159
 
@@ -273,6 +275,21 @@ function cursorOAuthState() {
273
275
  );
274
276
  }
275
277
 
278
+ // Account-scoped like the token store: the selected account's file wins. Expiry
279
+ // comes from describeDevinOAuthCredentials (no refresh is attempted here); an
280
+ // expired access token still counts when a refresh token can renew it.
281
+ function devinOAuthState() {
282
+ const paths = [
283
+ boundProviderAuthPath('devin-oauth') ||
284
+ process.env.DEVIN_OAUTH_CREDENTIALS_PATH ||
285
+ join(resolvePluginData(), 'devin-oauth.json'),
286
+ ];
287
+ return memoProbe('devin-oauth', paths, () => {
288
+ const info = describeDevinOAuthCredentials();
289
+ return info?.authenticated && (info.usable || info.refreshable) ? 'present' : 'absent';
290
+ });
291
+ }
292
+
276
293
  const OAUTH_PROBE_STATES = new Map([
277
294
  ['anthropic-oauth', anthropicOAuthState],
278
295
  ['openai-oauth', openAIOAuthState],
@@ -281,6 +298,7 @@ const OAUTH_PROBE_STATES = new Map([
281
298
  ['command-code-oauth', commandCodeOAuthState],
282
299
  ['mimo-oauth', mimoOAuthState],
283
300
  ['cursor-oauth', cursorOAuthState],
301
+ ['devin-oauth', devinOAuthState],
284
302
  ['antigravity-oauth', antigravityOAuthState],
285
303
  ['opencode-go-oauth', openCodeGoOAuthState],
286
304
  ]);
@@ -609,14 +609,47 @@ function balanceFromAnthropicSpend(spend) {
609
609
  return null;
610
610
  }
611
611
 
612
+ // The server's limit id, in the one spelling headers and the usage payload share.
613
+ const codexLimitId = (value) => (cleanString(value) || '').toLowerCase().replace(/-/g, '_');
614
+
615
+ // Model-specific limits beside the plan's own: one per server limit id
616
+ // (`metered_feature`), whose `limit_name` names the model it meters. Each
617
+ // window gates that model alone.
618
+ function codexAdditionalLimits(list) {
619
+ return (Array.isArray(list) ? list : []).flatMap((entry) => {
620
+ const limit = codexLimitId(entry?.metered_feature || entry?.limit_name);
621
+ if (!limit) return [];
622
+ const name = cleanString(entry.limit_name) || limit;
623
+ const rate = entry.rate_limit && typeof entry.rate_limit === 'object' ? entry.rate_limit : {};
624
+ const windows = [
625
+ windowFromPercent('5H', rate.primary_window || rate.primary, 'openai-codex-oauth'),
626
+ windowFromPercent('7D', rate.secondary_window || rate.secondary, 'openai-codex-oauth'),
627
+ ]
628
+ .filter(Boolean)
629
+ .map((window) => ({ ...window, label: `${window.label} ${name}`, limit, models: [name.toLowerCase()] }));
630
+ // A limit without a usable window meters nothing we can read: its model
631
+ // stays under the plan's windows.
632
+ return windows.length ? [{ model: name.toLowerCase(), windows }] : [];
633
+ });
634
+ }
635
+
612
636
  export function normalizeOpenAIWhamUsage(data) {
613
637
  const rate = data?.rate_limit && typeof data.rate_limit === 'object' ? data.rate_limit : null;
614
638
  if (!rate) return null;
639
+ const additional = codexAdditionalLimits(data.additional_rate_limits);
640
+ // The plan's windows gate every model that no model-specific limit meters.
641
+ const plan = {
642
+ limit: 'codex',
643
+ ...(additional.length ? { except: additional.map((entry) => entry.model) } : {}),
644
+ };
615
645
  const windows = [
616
646
  windowFromPercent('5H', rate.primary_window || rate.primary, 'openai-codex-oauth'),
617
647
  windowFromPercent('7D', rate.secondary_window || rate.secondary, 'openai-codex-oauth'),
618
- ].filter(Boolean);
648
+ ]
649
+ .filter(Boolean)
650
+ .map((window) => ({ ...window, ...plan }));
619
651
  if (!windows.length) return null;
652
+ windows.push(...additional.flatMap((entry) => entry.windows));
620
653
  return {
621
654
  provider: 'openai-oauth',
622
655
  source: 'openai-codex-wham',
@@ -687,12 +720,14 @@ function anthropicLimitWindows(limits, source) {
687
720
  .filter(Boolean);
688
721
  }
689
722
 
690
- // The extra-usage (pay-as-you-go) window when it is enabled, else null.
723
+ // The extra-usage (pay-as-you-go) window when it is enabled, else null. It is
724
+ // shown, never a limit: paid overage spent to its cap leaves the plan's own
725
+ // windows serving, so it gates no model (`models: []`).
691
726
  function anthropicExtraUsageWindow(extraUsage) {
692
727
  if (extraUsage?.is_enabled !== true) return null;
693
728
  const limitUsd = num(extraUsage.monthly_limit, 0) / 100;
694
729
  const usedUsd = num(extraUsage.used_credits, 0) / 100;
695
- return windowFromPercent(
730
+ const window = windowFromPercent(
696
731
  'EXTRA',
697
732
  {
698
733
  utilization: extraUsage.utilization,
@@ -702,6 +737,7 @@ function anthropicExtraUsageWindow(extraUsage) {
702
737
  },
703
738
  'anthropic-oauth-extra'
704
739
  );
740
+ return window ? { ...window, models: [] } : null;
705
741
  }
706
742
 
707
743
  export function normalizeAnthropicUsage(data, source = 'anthropic-oauth') {
@@ -23,6 +23,8 @@ export function createCompatStreamState({
23
23
  onToolCall,
24
24
  onTextDelta,
25
25
  onStreamItemStart,
26
+ // false: text stays text; only native tool_calls deltas become calls.
27
+ leakedToolCallRecovery = true,
26
28
  }) {
27
29
  const toolDedupe = createToolCallDedupe();
28
30
  return {
@@ -49,7 +51,7 @@ export function createCompatStreamState({
49
51
  rawUsage: null,
50
52
  toolAcc: createToolCallAccumulator(),
51
53
  streamEmitState: { emittedToolCallKeys: new Set(), emittedToolCall: false, _toolDedupe: toolDedupe },
52
- leakGuard: createLeakGuard({ knownToolNames, harmony: true }),
54
+ leakGuard: createLeakGuard({ knownToolNames: leakedToolCallRecovery ? knownToolNames : [], harmony: true }),
53
55
  leakedCalls: [],
54
56
  pendingLeakedCalls: [],
55
57
  };
@@ -44,6 +44,7 @@ export async function consumeCompatChatCompletionStream(
44
44
  knownToolNames,
45
45
  semanticIdleTimeoutMs,
46
46
  firstByteTimeoutMs,
47
+ leakedToolCallRecovery = true,
47
48
  } = {}
48
49
  ) {
49
50
  const idleOverrideEnabled = Number.isFinite(Number(semanticIdleTimeoutMs)) && Number(semanticIdleTimeoutMs) > 0;
@@ -57,6 +58,7 @@ export async function consumeCompatChatCompletionStream(
57
58
  onToolCall,
58
59
  onTextDelta,
59
60
  onStreamItemStart,
61
+ leakedToolCallRecovery,
60
62
  });
61
63
  reportTransport(state);
62
64
  const iterator = stream[Symbol.asyncIterator]();
@@ -7,6 +7,7 @@
7
7
  * headers response headers (HTTP, WS handshake, nested error.headers)
8
8
  * retryAfterMs server advice, uncapped (Retry-After / Retry-After-Ms)
9
9
  * resetAt epoch ms from quota reset fields (resets_at, resets_in_seconds)
10
+ * quotaLimit limit id from x-codex-active-limit, `-` normalized to `_`
10
11
  *
11
12
  * Pure: no I/O. Message text is never parsed for status or code.
12
13
  */
@@ -18,7 +19,7 @@ const isObject = (value) => value !== null && typeof value === 'object' && !Arra
18
19
  * Parse an HTTP error body ONCE into its contract fields. `message` is the
19
20
  * body's own message (untruncated); callers truncate for diagnostics.
20
21
  */
21
- export function parseErrorBody(text) {
22
+ function parseErrorBody(text) {
22
23
  let json = null;
23
24
  try {
24
25
  json = JSON.parse(String(text || ''));
@@ -206,6 +207,10 @@ export function applyErrorContract(err, { status, headers, code, error, event }
206
207
  const wireHeaders =
207
208
  headers ?? error?.headers ?? event?.error?.headers ?? event?.response?.error?.headers ?? event?.headers;
208
209
  if (wireHeaders && typeof wireHeaders === 'object') err.headers = wireHeaders;
210
+ // The limit a refusal hit, by the server's limit id (`codex` is the plan's
211
+ // own; others are model-specific), so the account pool blocks only it.
212
+ const activeLimit = headerValue(err.headers, 'x-codex-active-limit').toLowerCase().replace(/-/g, '_');
213
+ if (activeLimit) err.quotaLimit = activeLimit;
209
214
  const rawCode = code ?? error?.code ?? error?.type ?? null;
210
215
  if (typeof rawCode === 'string' && rawCode.trim()) err.code = rawCode.trim().toLowerCase();
211
216
  const resetAt = resetAtFrom(error, event?.error, event?.response?.error, event);
@@ -18,12 +18,15 @@ import { getPluginData } from '../config.mjs';
18
18
  import { writeJsonAtomicSync, withFileLock } from '../../../shared/atomic-file.mjs';
19
19
  import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
20
20
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
21
+ import { TIMEOUT_PHASE } from '../../../shared/provider-timeout-phase.mjs';
21
22
  import { modelProxyClientMode, serverModelCredentialsDisabledError } from '../../../shared/model-proxy-mode.mjs';
22
23
  import {
23
24
  accountIdentityFields,
24
25
  decodeJwtPayload,
25
26
  expiryFromAccessToken,
27
+ isSignedInTokenDocument,
26
28
  normalizeAccountIdentity,
29
+ normalizeExpiresAtMs,
27
30
  oauthCredentialStatus,
28
31
  } from './lib/oauth-token-utils.mjs';
29
32
  import { createOpenAIOAuthLogin } from './openai-oauth-login.mjs';
@@ -60,11 +63,8 @@ function getRefreshLockPath() {
60
63
  return `${getOwnTokenPath()}.refresh.lock`;
61
64
  }
62
65
 
63
- function _normalizeExpiresAt(value) {
64
- const n = Number(value || 0);
65
- if (!Number.isFinite(n) || n <= 0) return 0;
66
- return n < 1e12 ? n * 1000 : n;
67
- }
66
+ // One normalizer for the store and the sign-in predicate (isSignedInTokenDocument).
67
+ const _normalizeExpiresAt = normalizeExpiresAtMs;
68
68
 
69
69
  export function extractAccountId(token) {
70
70
  return decodeJwtPayload(token)?.['https://api.openai.com/auth']?.chatgpt_account_id;
@@ -117,7 +117,7 @@ export function loadTokens() {
117
117
  try {
118
118
  const stat = statSync(ownPath);
119
119
  const own = JSON.parse(readFileSync(ownPath, 'utf-8'));
120
- if (own.access_token && own.refresh_token) {
120
+ if (isSignedInTokenDocument(own)) {
121
121
  const identity = identityFromTokens(own.id_token, own.access_token);
122
122
  return {
123
123
  ...own,
@@ -144,8 +144,7 @@ export function saveTokens(tokens) {
144
144
  // same loader the runtime uses (loadTokens), no parallel hard-coded path probe.
145
145
  export function hasOpenAIOAuthCredentials() {
146
146
  try {
147
- const tokens = loadTokens();
148
- return !!(tokens?.access_token && tokens?.refresh_token);
147
+ return !!loadTokens()?.access_token;
149
148
  } catch {
150
149
  return false;
151
150
  }
@@ -256,7 +255,9 @@ async function exchangeRefreshToken(refreshToken, previousIdentity = null) {
256
255
  return tokens;
257
256
  } catch (err) {
258
257
  if (err?.name === 'AbortError')
259
- throw new Error(`OpenAI OAuth token refresh timed out after ${TOKEN_REFRESH_TIMEOUT_MS}ms`);
258
+ throw Object.assign(new Error(`OpenAI OAuth token refresh timed out after ${TOKEN_REFRESH_TIMEOUT_MS}ms`), {
259
+ timeoutPhase: TIMEOUT_PHASE.AUTH,
260
+ });
260
261
  throw err;
261
262
  } finally {
262
263
  clearTimeout(timeout);
@@ -7,13 +7,17 @@ import { createStreamSafetyStamps, resolveStallRetryBudget } from '../retry-clas
7
7
  import { _codexWsCompatibilityHeaders } from '../openai-codex-metadata.mjs';
8
8
  import { createWsSendAttempts } from '../openai-ws-send-attempts.mjs';
9
9
  import { createWsSendSpan } from '../openai-ws-send-span.mjs';
10
+ import { emitProviderRetryStage } from '../retry-executor.mjs';
11
+ import { retryReasonClass } from '../provider-error-kind.mjs';
10
12
 
11
13
  /** Stage callbacks are observers; a throwing one never fails the send. */
12
14
  export function notifyStage(onStageChange, stage, detail) {
13
15
  try {
14
16
  if (detail === undefined) onStageChange?.(stage);
15
17
  else onStageChange?.(stage, detail);
16
- } catch {}
18
+ } catch {
19
+ // the stage observer is advisory
20
+ }
17
21
  }
18
22
 
19
23
  export function createWsSendContext(opts) {
@@ -81,10 +85,24 @@ export function createWsSendContext(opts) {
81
85
  // Single caller-visible recovery path for both handshake/acquire retries
82
86
  // and retryable stream failures. The session/TUI stage bridge renders this
83
87
  // as non-terminal reconnect progress; transport code must not also print it
84
- // to stderr.
85
- const emitReconnectProgress = ({ attempt, max, classifier }) => {
88
+ // to stderr. A retry that knows its failure carries the reason and the wait
89
+ // (retryAt), so a long server-advised wait counts down with its cause
90
+ // instead of sitting silently at "n/max".
91
+ const emitReconnectProgress = ({ attempt, max, classifier, error = null, waitMs = 0 }) => {
86
92
  const retryAttempt = Number(attempt) || 1;
87
93
  const retryMax = Number(max) || 1;
94
+ if (error) {
95
+ // The reason shown is the fixed class, never the server's own text.
96
+ emitProviderRetryStage(onStageChange, {
97
+ attempt: retryAttempt,
98
+ maxAttempts: retryMax,
99
+ lastErr: error,
100
+ delayMs: waitMs,
101
+ classifier,
102
+ reasonClass: retryReasonClass(error),
103
+ });
104
+ return;
105
+ }
88
106
  notifyStage(onStageChange, 'reconnecting', {
89
107
  attempt: retryAttempt,
90
108
  max: retryMax,
@@ -105,6 +123,7 @@ export function createWsSendContext(opts) {
105
123
  retry429,
106
124
  stallRetryBudget,
107
125
  trace: { poolKey, traceProvider, useModel },
126
+ traceFn: _agentTraceFn,
108
127
  auth,
109
128
  body,
110
129
  });
@@ -3,7 +3,34 @@
3
3
  * WS send, plus the surface/backoff primitives both failure resolvers use.
4
4
  */
5
5
  import { performance } from 'node:perf_hooks';
6
+ import { appendAgentTrace } from '../../agent-trace.mjs';
6
7
  import { _sleepWithAbort, MIDSTREAM_WS_TRANSIENT_RETRY_LIMIT, midstreamBackoffFor, tag } from './policy.mjs';
8
+ import { retryReasonClass, safeRetryErrorCode } from '../provider-error-kind.mjs';
9
+
10
+ /** One row per WS retry: the server's typed verdict (status, close code, an
11
+ * identifier-shaped code, the reason class) and the wait it bought, so a
12
+ * long advised wait is diagnosable after the fact. Never the server's free
13
+ * text: it can echo request content. Diagnostics only; never throws. */
14
+ function traceRetry(traceFn, trace, err, { attempt, max, classifier, waitMs }) {
15
+ try {
16
+ const payload = {
17
+ provider: trace?.traceProvider || null,
18
+ model: trace?.useModel || null,
19
+ transport: 'websocket',
20
+ attempt,
21
+ max,
22
+ classifier: classifier || null,
23
+ reason: retryReasonClass(err),
24
+ wait_ms: Math.round(Number(waitMs) || 0),
25
+ http_status: Number(err?.httpStatus || err?.status || 0) || null,
26
+ code: safeRetryErrorCode(err),
27
+ ws_close_code: Number(err?.wsCloseCode || 0) || null,
28
+ };
29
+ traceFn({ sessionId: trace?.poolKey || null, kind: 'ws_retry', ...payload, payload });
30
+ } catch {
31
+ // the retry trace sink is advisory
32
+ }
33
+ }
7
34
 
8
35
  export function createAttemptContext(deps) {
9
36
  const { sendSpan, externalSignal, sleepFn, safetyStamps } = deps;
@@ -31,10 +58,13 @@ export function createAttemptContext(deps) {
31
58
  };
32
59
  // Server advice (Retry-After, or a typed rate limit's "try again in")
33
60
  // replaces the local curve, like the reference client's retry deadline.
34
- const backoff = async (retryNumber, err = null) => {
61
+ // Resolved before the retry is announced so the reconnect stage can name
62
+ // the wait it is about to sit through.
63
+ const retryWaitMs = (retryNumber, err = null) => midstreamBackoffFor(retryNumber, err);
64
+ const backoff = async (waitMs) => {
35
65
  const sleepStart = performance.now();
36
66
  try {
37
- await _sleepWithAbort(midstreamBackoffFor(retryNumber, err), externalSignal, sleepFn);
67
+ await _sleepWithAbort(waitMs, externalSignal, sleepFn);
38
68
  } catch (sleepErr) {
39
69
  sendSpan.retryBackoffMs += performance.now() - sleepStart;
40
70
  sendSpan.emit('error');
@@ -53,14 +83,21 @@ export function createAttemptContext(deps) {
53
83
  }
54
84
  return first;
55
85
  };
56
- /** Stash the error a retry is about to leave behind and announce the retry. */
57
- const scheduleRetry = (err, { attemptIndex, classifier, retryLimit, remember }) => {
86
+ /** Stash the error a retry is about to leave behind and announce the retry
87
+ * with its cause and the wait before the next attempt. */
88
+ const scheduleRetry = (err, { attemptIndex, classifier, retryLimit, remember, waitMs = 0 }) => {
58
89
  if (remember) {
59
90
  state.firstAttemptError = err;
60
91
  state.firstAttemptClassifier = classifier;
61
92
  }
62
93
  tag(err, { midstreamClassifier: classifier });
63
- deps.emitReconnectProgress({ attempt: attemptIndex + 1, max: retryLimit, classifier });
94
+ deps.emitReconnectProgress({ attempt: attemptIndex + 1, max: retryLimit, classifier, error: err, waitMs });
95
+ traceRetry(deps.traceFn || appendAgentTrace, deps.trace, err, {
96
+ attempt: attemptIndex + 1,
97
+ max: retryLimit,
98
+ classifier,
99
+ waitMs,
100
+ });
64
101
  };
65
102
 
66
103
  return {
@@ -69,6 +106,7 @@ export function createAttemptContext(deps) {
69
106
  maxMidstreamRetries: MIDSTREAM_WS_TRANSIENT_RETRY_LIMIT,
70
107
  stampAll,
71
108
  surface,
109
+ retryWaitMs,
72
110
  backoff,
73
111
  surfaceFirstAttempt,
74
112
  scheduleRetry,
@@ -81,13 +81,15 @@ export async function resolveHandshakeFailure(
81
81
  const retryable =
82
82
  classifier && status !== 401 && status !== 426 && err?.unsafeToRetry !== true && !externalSignal?.aborted;
83
83
  if (retryable && attemptIndex < ctx.maxMidstreamRetries) {
84
+ const waitMs = ctx.retryWaitMs(attemptIndex + 1, err);
84
85
  ctx.scheduleRetry(err, {
85
86
  attemptIndex,
86
87
  classifier,
87
88
  retryLimit: ctx.maxMidstreamRetries,
88
89
  remember: !state.firstAttemptError,
90
+ waitMs,
89
91
  });
90
- await ctx.backoff(attemptIndex + 1, err);
92
+ await ctx.backoff(waitMs);
91
93
  return true;
92
94
  }
93
95
  // A later auth/upgrade decision must win over an earlier transient
@@ -77,7 +77,9 @@ export async function resolveStreamFailure(ctx, err, { attemptIndex, entry, midS
77
77
  process.stderr.write(
78
78
  `[openai-oauth] stall retry budget exhausted (${STREAM_STALL_RETRY_BUDGET_MS}ms since first stall) — surfacing provider-terminal failure\n`
79
79
  );
80
- } catch {}
80
+ } catch {
81
+ // diagnostic write only; the provider-terminal failure is thrown next
82
+ }
81
83
  throw ctx.surface(markProviderRecoveryExhausted(err, { owner: 'openai-oauth-ws-stall-budget' }));
82
84
  }
83
85
  const retryLimit = classifier ? midstreamRetryLimit(classifier) : 0;
@@ -89,8 +91,9 @@ export async function resolveStreamFailure(ctx, err, { attemptIndex, entry, midS
89
91
  state.carryForwardCache = null;
90
92
  tag(entry, { lastResponseId: null });
91
93
  }
92
- ctx.scheduleRetry(err, { attemptIndex, classifier, retryLimit, remember: true });
93
- await ctx.backoff(attemptIndex + 1, err);
94
+ const waitMs = ctx.retryWaitMs(attemptIndex + 1, err);
95
+ ctx.scheduleRetry(err, { attemptIndex, classifier, retryLimit, remember: true, waitMs });
96
+ await ctx.backoff(waitMs);
94
97
  return true;
95
98
  }
96
99
  // Not retryable, OR we've already exhausted the retry budget. Do not
@@ -144,6 +144,7 @@ export class OpenCodeGoGeminiEngine extends GeminiProvider {
144
144
  );
145
145
  },
146
146
  afterToolResult: geminiEndsWithFunctionResponse(contents),
147
+ failureUsage: geminiFailureUsage(opts, null, useModel),
147
148
  run: async ({ signal: attemptSignal, connected }) => {
148
149
  try {
149
150
  opts.onStageChange?.('requesting');
@@ -0,0 +1,19 @@
1
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
2
+ import { isOAuthProviderAvailable } from './oauth-credential-probes.mjs';
3
+
4
+ /**
5
+ * Whether the named provider may be looked up or sent through right now. Remote
6
+ * client mode serves the PC's providers, whose availability the PC already
7
+ * decided; only a locally run provider answers to the local Developer option.
8
+ */
9
+ export function providerAvailable(name) {
10
+ return modelProxyClientMode() || isOAuthProviderAvailable(name);
11
+ }
12
+
13
+ /** The error a send raises when its provider was switched off after lookup. */
14
+ export function providerUnavailableError(name) {
15
+ return Object.assign(
16
+ new Error(`Provider "${name}" is disabled: its Developer option was switched off before the request was sent.`),
17
+ { code: 'EPROVIDERUNAVAILABLE', provider: name }
18
+ );
19
+ }