mixdog 1.2.3 → 1.2.5

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 (469) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/mobile-release.mjs +5 -4
  5. package/scripts/probe-provider-catalog.mjs +31 -0
  6. package/scripts/provider-pingpong-live.mjs +128 -38
  7. package/scripts/session-sweep.mjs +5 -25
  8. package/src/agents/maintainer/AGENT.md +1 -1
  9. package/src/agents/maintainer/agent.json +1 -1
  10. package/src/agents/reviewer/AGENT.md +2 -4
  11. package/src/agents/security/AGENT.md +1 -2
  12. package/src/agents/writer/AGENT.md +2 -3
  13. package/src/defaults/shipped-history.json +7 -1
  14. package/src/defaults/skills/image/SKILL.md +21 -23
  15. package/src/lib/keychain-cjs.cjs +13 -11
  16. package/src/output-styles/common.md +2 -1
  17. package/src/rules/agent/AGENT.md +7 -5
  18. package/src/rules/lead/LEAD.md +13 -13
  19. package/src/rules/shared/45-destructive.md +4 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  23. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  24. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  25. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  31. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  34. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  35. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  36. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  38. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  41. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  46. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  48. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  50. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  51. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  52. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  53. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  54. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  56. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  57. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  59. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  60. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  61. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  62. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  64. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  65. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  66. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  67. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  68. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  69. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  70. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  80. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  82. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  86. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  87. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  88. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  89. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  90. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  91. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  92. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  93. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  96. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  97. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  98. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  99. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  100. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  101. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  103. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  109. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  110. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  111. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  112. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  113. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  114. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  115. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  116. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  117. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  118. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  119. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  120. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  121. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  123. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  124. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  125. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  128. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  129. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  131. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  132. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  133. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  134. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  135. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  141. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  146. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  147. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  148. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  149. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  150. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  151. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  155. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  157. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  158. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  161. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  162. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  163. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  164. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  165. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  167. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  169. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  173. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  174. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  178. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  179. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  180. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  181. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +160 -212
  182. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  183. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  184. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  185. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  186. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  187. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  188. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  189. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  190. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  191. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  192. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  193. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  194. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  197. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  199. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  200. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  208. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  209. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  210. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  211. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  212. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  216. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  217. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  218. package/src/runtime/attachments/store.d.mts +9 -0
  219. package/src/runtime/attachments/store.mjs +80 -9
  220. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  221. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  222. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  223. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  224. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  225. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  226. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  227. package/src/runtime/github/tool.mjs +118 -17
  228. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  229. package/src/runtime/media/catalog.mjs +4 -4
  230. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  231. package/src/runtime/media/defaults.mjs +37 -0
  232. package/src/runtime/media/jobs.mjs +12 -2
  233. package/src/runtime/media/lanes.mjs +12 -0
  234. package/src/runtime/media/tool-defs.mjs +15 -4
  235. package/src/runtime/media/tool.mjs +41 -12
  236. package/src/runtime/media/upstream-error.mjs +72 -6
  237. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  238. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  240. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  242. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  243. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  244. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  245. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  246. package/src/runtime/office/index.mjs +2 -3
  247. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  248. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  249. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  250. package/src/runtime/shared/atomic-file.d.mts +1 -1
  251. package/src/runtime/shared/background-tasks.mjs +6 -1
  252. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  253. package/src/runtime/shared/browser-launch.mjs +15 -0
  254. package/src/runtime/shared/err-text.mjs +42 -4
  255. package/src/runtime/shared/file-lock.mjs +6 -5
  256. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  257. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  258. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  259. package/src/runtime/shared/model-route-chain.mjs +48 -0
  260. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  261. package/src/runtime/shared/pid-liveness.mjs +12 -0
  262. package/src/runtime/shared/provider-api-key.mjs +6 -0
  263. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  264. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  265. package/src/runtime/shared/quiet-progress.mjs +101 -0
  266. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  267. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  268. package/src/runtime/shared/remote-intent.mjs +2 -42
  269. package/src/runtime/shared/slow-span.mjs +8 -1
  270. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  271. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  272. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  273. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  274. package/src/runtime/shared/tool-status.mjs +3 -2
  275. package/src/runtime/shared/tool-surface.mjs +2 -3
  276. package/src/runtime/shared/tool-work-units.mjs +22 -15
  277. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  278. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  279. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  280. package/src/runtime/shared/transcript-writer.mjs +14 -4
  281. package/src/runtime/shared/turn-changes.mjs +2 -1
  282. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  283. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  284. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  285. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  286. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  287. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  288. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  289. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  290. package/src/runtime/shared/worktrees/include.mjs +125 -7
  291. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  292. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  293. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  294. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  295. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  296. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  297. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  298. package/src/runtime/terminal-bridge/client.mjs +14 -10
  299. package/src/session-runtime/boot/apis.mjs +7 -1
  300. package/src/session-runtime/boot/config.mjs +2 -0
  301. package/src/session-runtime/boot/facade.mjs +3 -0
  302. package/src/session-runtime/boot/session.mjs +3 -2
  303. package/src/session-runtime/boot/tools.mjs +3 -0
  304. package/src/session-runtime/conversation-import/service.mjs +1 -1
  305. package/src/session-runtime/goal-facade-api.mjs +2 -2
  306. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  307. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  308. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  309. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  310. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  311. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  312. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  313. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  314. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  315. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  316. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  317. package/src/session-runtime/media-api.mjs +8 -0
  318. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  319. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  320. package/src/session-runtime/model-route/set-route.mjs +114 -34
  321. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  322. package/src/session-runtime/model-route-api.mjs +4 -2
  323. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  324. package/src/session-runtime/native-web-search.mjs +9 -8
  325. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  326. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  327. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  328. package/src/session-runtime/provider-models.mjs +1 -0
  329. package/src/session-runtime/provider-usage.mjs +4 -0
  330. package/src/session-runtime/route-resolution.mjs +3 -1
  331. package/src/session-runtime/runtime-core.mjs +4 -0
  332. package/src/session-runtime/runtime-facade.mjs +6 -0
  333. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  334. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  335. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  336. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  337. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  338. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  339. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  340. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  341. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  343. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  344. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  345. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  346. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  347. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  348. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  349. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  350. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  351. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  352. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  353. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  354. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  355. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  356. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  357. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  358. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  359. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  360. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  361. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  362. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  363. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  364. package/src/session-runtime/services/agent-tool.mjs +4 -0
  365. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  366. package/src/session-runtime/services/provider-admin.mjs +77 -0
  367. package/src/session-runtime/session-create/session-options.mjs +7 -5
  368. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  369. package/src/session-runtime/session-transcript.mjs +4 -5
  370. package/src/session-runtime/session-turn-api.mjs +3 -9
  371. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  372. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  373. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  374. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  375. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  376. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  377. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  378. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  379. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  380. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  381. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  382. package/src/session-runtime/turn/turn-run.mjs +1 -36
  383. package/src/session-runtime/usage-stats-api.mjs +4 -1
  384. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  385. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  386. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  387. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  388. package/src/session-runtime/workspace/transitions.mjs +248 -68
  389. package/src/standalone/channel-transport.mjs +3 -4
  390. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  391. package/src/standalone/daemon-pg-stop.mjs +1 -3
  392. package/src/standalone/daemon-session-control.mjs +88 -0
  393. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  394. package/src/standalone/daemon.mjs +2 -0
  395. package/src/standalone/desktop-service-registry.mjs +32 -5
  396. package/src/standalone/local-session-runtime.mjs +11 -12
  397. package/src/standalone/session-protocol.mjs +7 -0
  398. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  399. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  400. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  401. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  402. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  403. package/src/standalone/session-service/agent-tree.mjs +28 -2
  404. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  405. package/src/standalone/session-service/entries.mjs +1 -0
  406. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  407. package/src/standalone/session-service/quota-resume.mjs +28 -13
  408. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  409. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  410. package/src/standalone/session-tool-output.mjs +82 -0
  411. package/src/tui/app/app-view.jsx +2 -1
  412. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  413. package/src/tui/app/provider-setup-picker.mjs +15 -3
  414. package/src/tui/app/slash-commands.mjs +6 -0
  415. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  416. package/src/tui/components/Spinner.jsx +4 -0
  417. package/src/tui/components/SpinnerByline.jsx +21 -2
  418. package/src/tui/components/status-line/local-line.mjs +10 -2
  419. package/src/tui/dist/index.mjs +529 -270
  420. package/src/tui/session/active-tool-summary.mjs +1 -1
  421. package/src/tui/session/context-state.mjs +0 -3
  422. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  423. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  424. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  425. package/src/tui/session/quota-resume.mjs +140 -61
  426. package/src/tui/session/session-api/commands.mjs +4 -10
  427. package/src/tui/session/session-api/controls.mjs +12 -0
  428. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  429. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  430. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  431. package/src/tui/session/session-api/media.mjs +2 -0
  432. package/src/tui/session/session-api/routes.mjs +81 -57
  433. package/src/tui/session/session-api/settings.mjs +3 -3
  434. package/src/tui/session/session-api/worktree.mjs +3 -2
  435. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  436. package/src/tui/session/session-flow/drain.mjs +6 -4
  437. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  438. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  439. package/src/tui/session/session-flow/queue.mjs +1 -1
  440. package/src/tui/session/tool-approval.mjs +17 -8
  441. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  442. package/src/tui/session/tui-steering-persist.mjs +3 -5
  443. package/src/tui/session/turn-quiet.mjs +53 -0
  444. package/src/tui/session/turn-stream.mjs +5 -1
  445. package/src/tui/session/turn.mjs +38 -31
  446. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  447. package/src/tui/spinner-meta.mjs +10 -0
  448. package/src/tui/spinner-verbs.mjs +3 -0
  449. package/src/ui/statusline.mjs +13 -3
  450. package/src/ui/streaming-markdown-heal.mjs +70 -18
  451. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  452. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  453. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  454. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  455. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  456. package/src/session-runtime/turn/share-ops.mjs +0 -77
  457. package/src/tui/session/cross-surface-share.mjs +0 -406
  458. package/src/tui/session/live-share/delta.mjs +0 -153
  459. package/src/tui/session/live-share/mirror.mjs +0 -106
  460. package/src/tui/session/live-share/owner.mjs +0 -254
  461. package/src/tui/session/live-share/retry.mjs +0 -54
  462. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  463. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  464. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  465. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  466. package/src/tui/session/live-share/viewer.mjs +0 -99
  467. package/src/tui/session/live-share/wire.mjs +0 -44
  468. package/src/tui/session/live-share.mjs +0 -154
  469. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -1,17 +1,27 @@
1
1
  /**
2
2
  * ChatGPT OAuth image adapter (`openai-oauth` lane).
3
3
  *
4
- * Runs the hosted `image_generation` tool on the Codex responses backend — the
5
- * same endpoint/headers the chat provider uses, so the subscription session is
6
- * the only credential involved. The final image arrives as base64 on the
7
- * image_generation_call item; partial frames are kept as a fallback.
4
+ * Primary path: the subscription's Images endpoints on the Codex backend
5
+ * (`images/generations`, or `images/edits` when reference images are given),
6
+ * a plain JSON request answered with the finished image as base64.
7
+ *
8
+ * Fallback path, used only when those endpoints are not served (404/405/501,
9
+ * or the edge's HTML 403 for an unknown route): the hosted `image_generation`
10
+ * tool on the responses backend. Its final image arrives on the
11
+ * image_generation_call item; a stream that only produced partial preview
12
+ * frames fails instead of saving a draft as the result.
13
+ *
14
+ * Both paths use the chat provider's session, so the subscription login is the
15
+ * only credential involved. Usage-limit failures name the reset time, looked up
16
+ * from the account's usage summary when the failure itself omits it.
8
17
  */
9
18
  import { randomBytes } from 'node:crypto';
10
19
  import { resolveCodexAuth } from '../auth.mjs';
11
20
  import { timeoutSignal } from '../bounded-signal.mjs';
21
+ import { chatgptLimitResetAt } from '../chatgpt-usage.mjs';
12
22
  import { decodeBase64Media } from '../download.mjs';
13
23
  import { mediaError } from '../lanes.mjs';
14
- import { upstreamError } from '../upstream-error.mjs';
24
+ import { upstreamError, usageLimitError, usageLimitFromResponse, usageLimitInfo } from '../upstream-error.mjs';
15
25
  import { responsesUsage, withReportedUsage } from '../media-usage.mjs';
16
26
  import { CODEX_OAUTH_ORIGINATOR, CODEX_RESPONSES_URL } from '../../agent/orchestrator/providers/openai-oauth.mjs';
17
27
  import {
@@ -21,6 +31,13 @@ import {
21
31
  } from '../../agent/orchestrator/providers/codex-client-meta.mjs';
22
32
 
23
33
  const REQUEST_TIMEOUT_MS = 400_000;
34
+ const LABEL = 'ChatGPT image';
35
+ export const CODEX_IMAGE_MODEL = 'gpt-image-2';
36
+ export const CODEX_IMAGES_GENERATIONS_URL = new URL('images/generations', CODEX_RESPONSES_URL).href;
37
+ export const CODEX_IMAGES_EDITS_URL = new URL('images/edits', CODEX_RESPONSES_URL).href;
38
+ const BACKGROUNDS = new Set(['transparent', 'opaque', 'auto']);
39
+
40
+ const dataUrl = (ref) => `data:${ref.mime || 'image/png'};base64,${ref.base64}`;
24
41
 
25
42
  function imageTool(options = {}) {
26
43
  const tool = { type: 'image_generation' };
@@ -28,9 +45,91 @@ function imageTool(options = {}) {
28
45
  if (size && size !== 'auto') tool.size = size;
29
46
  const quality = String(options.quality || 'auto');
30
47
  if (quality && quality !== 'auto') tool.quality = quality;
48
+ if (options.background && options.background !== 'auto') tool.background = options.background;
31
49
  return tool;
32
50
  }
33
51
 
52
+ /** Images endpoint request: generation, or an edit when references are given. */
53
+ export function codexImagesRequest({ prompt, options = {}, references = [] }) {
54
+ const body = { prompt, model: CODEX_IMAGE_MODEL, size: 'auto', quality: 'auto' };
55
+ if (options.background) body.background = options.background;
56
+ if (!references.length) return { url: CODEX_IMAGES_GENERATIONS_URL, body };
57
+ return {
58
+ url: CODEX_IMAGES_EDITS_URL,
59
+ body: { images: references.map((ref) => ({ image_url: dataUrl(ref) })), ...body },
60
+ };
61
+ }
62
+
63
+ export function codexImagesRequestHeaders(auth) {
64
+ const headers = {
65
+ Authorization: `Bearer ${auth.access_token}`,
66
+ 'Content-Type': 'application/json',
67
+ Accept: 'application/json',
68
+ originator: CODEX_OAUTH_ORIGINATOR,
69
+ 'x-client-request-id': randomBytes(16).toString('hex'),
70
+ version: codexVersionHeader(),
71
+ 'User-Agent': codexUserAgent(),
72
+ };
73
+ if (auth.account_id) headers['ChatGPT-Account-ID'] = auth.account_id;
74
+ return headers;
75
+ }
76
+
77
+ // The endpoint is not served for this backend: a missing route or method, or
78
+ // the edge's HTML 403 page that unknown backend routes answer with. Auth,
79
+ // billing and limit failures come back as JSON and are reported, not retried.
80
+ function imagesEndpointUnavailable(res) {
81
+ if ([404, 405, 501].includes(res.status)) return true;
82
+ return res.status === 403 && !/json/i.test(res.headers.get('content-type') || '');
83
+ }
84
+
85
+ function imagesUsage(usage) {
86
+ const tokens = responsesUsage(usage);
87
+ const imageTokens = Math.trunc(Number(usage?.output_tokens_details?.image_tokens) || 0);
88
+ return imageTokens > 0 ? { ...tokens, outputImageTokens: imageTokens } : tokens;
89
+ }
90
+
91
+ async function limitError(limit, { auth, fetchFn, lookupReset, signal }) {
92
+ const resetsAt = limit.resetsAt || (await lookupReset({ auth, limitId: limit.limitId, fetchFn, signal }));
93
+ return usageLimitError(LABEL, { limitId: limit.limitId, resetsAt });
94
+ }
95
+
96
+ async function failedResponse(res, ctx) {
97
+ const text = await res.text().catch(() => '');
98
+ const limit = usageLimitFromResponse(res.status, text, res.headers);
99
+ if (limit) return await limitError(limit, ctx);
100
+ return upstreamError(LABEL, res.status, text);
101
+ }
102
+
103
+ /** Returns the image, or null when the Images endpoint is unavailable. */
104
+ async function generateViaImagesEndpoint({ prompt, options, references, signal }, ctx) {
105
+ const { url, body } = codexImagesRequest({ prompt, options, references });
106
+ const res = await ctx.fetchFn(url, {
107
+ method: 'POST',
108
+ headers: codexImagesRequestHeaders(ctx.auth),
109
+ body: JSON.stringify(body),
110
+ redirect: 'error',
111
+ signal: timeoutSignal(signal, REQUEST_TIMEOUT_MS),
112
+ });
113
+ if (imagesEndpointUnavailable(res)) {
114
+ await res.body?.cancel().catch(() => {});
115
+ return null;
116
+ }
117
+ if (!res.ok) throw await failedResponse(res, ctx);
118
+ const json = await res.json().catch(() => null);
119
+ const usage = imagesUsage(json?.usage);
120
+ const b64 = json?.data?.[0]?.b64_json;
121
+ if (typeof b64 !== 'string' || !b64) {
122
+ throw withReportedUsage(mediaError(`${LABEL}: returned no image data`, 'MEDIA_EMPTY_RESULT', 502), usage);
123
+ }
124
+ return {
125
+ bytes: decodeBase64Media(b64, LABEL),
126
+ mime: 'image/png',
127
+ revisedPrompt: null,
128
+ actualModel: CODEX_IMAGE_MODEL,
129
+ usage: usage || null,
130
+ };
131
+ }
132
+
34
133
  function collectImage(event, state) {
35
134
  const item = event?.item;
36
135
  // Terminal Responses events carry the orchestrator's token usage. It does
@@ -55,10 +154,7 @@ export function codexImageRequestBody({ model, prompt, options = {}, references
55
154
  // Reference images ride as input_image parts on the user turn — the same
56
155
  // shape the chat path uses for pasted images.
57
156
  const content = [
58
- ...references.map((ref) => ({
59
- type: 'input_image',
60
- image_url: `data:${ref.mime || 'image/png'};base64,${ref.base64}`,
61
- })),
157
+ ...references.map((ref) => ({ type: 'input_image', image_url: dataUrl(ref) })),
62
158
  { type: 'input_text', text: prompt },
63
159
  ];
64
160
  return {
@@ -91,7 +187,12 @@ export function codexImageRequestHeaders(auth) {
91
187
 
92
188
  export async function generateImage(
93
189
  { model, prompt, options = {}, references = [], signal },
94
- { fetchFn = fetch, resolveAuth = resolveCodexAuth, warmVersion = warmCodexClientVersion } = {}
190
+ {
191
+ fetchFn = fetch,
192
+ resolveAuth = resolveCodexAuth,
193
+ warmVersion = warmCodexClientVersion,
194
+ lookupReset = chatgptLimitResetAt,
195
+ } = {}
95
196
  ) {
96
197
  if (['size', 'quality', 'resolution', 'aspectRatio'].some((key) => options[key] && options[key] !== 'auto')) {
97
198
  throw mediaError(
@@ -99,19 +200,28 @@ export async function generateImage(
99
200
  'MEDIA_OPTION_UNSUPPORTED'
100
201
  );
101
202
  }
203
+ if (options.background && !BACKGROUNDS.has(options.background)) {
204
+ throw mediaError('background must be transparent, opaque or auto.', 'MEDIA_OPTION_UNSUPPORTED');
205
+ }
102
206
  const auth = await resolveAuth();
103
207
  // Discovery and generation must report the same client version; otherwise
104
208
  // a newly discovered model can still be rejected by the Codex version gate.
105
209
  await warmVersion();
210
+ const ctx = { auth, fetchFn, lookupReset, signal };
211
+ const image = await generateViaImagesEndpoint({ prompt, options, references, signal }, ctx);
212
+ return image || (await generateViaResponses({ model, prompt, options, references, signal }, ctx));
213
+ }
214
+
215
+ async function generateViaResponses({ model, prompt, options, references, signal }, ctx) {
106
216
  const body = codexImageRequestBody({ model, prompt, options, references });
107
- const res = await fetchFn(CODEX_RESPONSES_URL, {
217
+ const res = await ctx.fetchFn(CODEX_RESPONSES_URL, {
108
218
  method: 'POST',
109
- headers: codexImageRequestHeaders(auth),
219
+ headers: codexImageRequestHeaders(ctx.auth),
110
220
  body: JSON.stringify(body),
111
221
  redirect: 'error',
112
222
  signal: timeoutSignal(signal, REQUEST_TIMEOUT_MS),
113
223
  });
114
- if (!res.ok || !res.body) throw upstreamError('ChatGPT image', res.status, await res.text().catch(() => ''));
224
+ if (!res.ok || !res.body) throw await failedResponse(res, ctx);
115
225
 
116
226
  const reader = res.body.getReader();
117
227
  const decoder = new TextDecoder();
@@ -136,7 +246,8 @@ export async function generateImage(
136
246
  continue;
137
247
  }
138
248
  if (event?.type === 'response.failed' || event?.type === 'error') {
139
- failure = event?.response?.error?.message || event?.message || 'stream failed';
249
+ // A top-level `error` event carries code/type/resets_at on itself.
250
+ failure = event?.response?.error || event?.error || event;
140
251
  }
141
252
  collectImage(event, state);
142
253
  }
@@ -147,15 +258,23 @@ export async function generateImage(
147
258
  } catch {}
148
259
  }
149
260
 
150
- const b64 = state.final || state.partial;
151
- if (!b64) {
152
- throw withReportedUsage(
153
- mediaError(`ChatGPT returned no image data${failure ? `: ${failure}` : ''}`, 'MEDIA_EMPTY_RESULT', 502),
154
- state.usage
155
- );
261
+ if (!state.final) {
262
+ const limit = failure && usageLimitInfo(failure, res.headers);
263
+ if (limit) throw withReportedUsage(await limitError(limit, ctx), state.usage);
264
+ const reason = (typeof failure === 'string' ? failure : failure?.message) || 'stream failed';
265
+ // A partial frame is a low-fidelity preview of an unfinished render; it is
266
+ // never saved as the result.
267
+ const error = state.partial
268
+ ? mediaError(
269
+ `${LABEL}: generation stopped after a partial preview; no final image was produced${failure ? ` (${reason})` : ''}`,
270
+ 'MEDIA_PARTIAL_ONLY',
271
+ 502
272
+ )
273
+ : mediaError(`ChatGPT returned no image data${failure ? `: ${reason}` : ''}`, 'MEDIA_EMPTY_RESULT', 502);
274
+ throw withReportedUsage(error, state.usage);
156
275
  }
157
276
  return {
158
- bytes: decodeBase64Media(b64, 'ChatGPT image'),
277
+ bytes: decodeBase64Media(state.final, LABEL),
159
278
  mime: 'image/png',
160
279
  revisedPrompt: state.revisedPrompt,
161
280
  actualModel: state.actualModel || null,
@@ -145,12 +145,12 @@ export function projectMediaModels(lane, rows) {
145
145
  result.image = orchestrator
146
146
  ? [
147
147
  {
148
- id: 'chatgpt-image-auto',
149
- label: 'ChatGPT Image · Auto',
148
+ id: 'gpt-image-2',
149
+ label: 'GPT Image 2',
150
150
  description:
151
- 'ChatGPT selects the image engine. GPT Image 2.5 selection and output size/quality are not verified on this connection.',
151
+ 'ChatGPT image engine. Output size/quality are chosen by ChatGPT; background can be transparent.',
152
152
  requestModel: orchestrator.id,
153
- controls: { maxReferences: 5 },
153
+ controls: { maxReferences: 5, background: ['auto', 'transparent', 'opaque'] },
154
154
  },
155
155
  ]
156
156
  : [];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * ChatGPT subscription usage lookup.
3
+ *
4
+ * A usage-limit failure does not always say when the window reopens. The
5
+ * account's usage summary does: each metered limit lists its windows with
6
+ * `used_percent` and an epoch `reset_at`.
7
+ */
8
+ import { timeoutSignal } from './bounded-signal.mjs';
9
+ import { codexUserAgent } from '../agent/orchestrator/providers/codex-client-meta.mjs';
10
+
11
+ export const CHATGPT_USAGE_URL = 'https://chatgpt.com/backend-api/wham/usage';
12
+ const LOOKUP_TIMEOUT_MS = 10_000;
13
+
14
+ function exhaustedResetAt(rateLimit) {
15
+ if (!rateLimit || typeof rateLimit !== 'object') return null;
16
+ const windows = [rateLimit.primary_window, rateLimit.secondary_window].filter((w) => Number(w?.reset_at) > 0);
17
+ const full = windows.filter((w) => Number(w.used_percent) >= 100);
18
+ const candidates = full.length ? full : rateLimit.limit_reached ? windows : [];
19
+ return candidates.length ? Math.max(...candidates.map((w) => Math.trunc(Number(w.reset_at)))) : null;
20
+ }
21
+
22
+ /**
23
+ * Reset time (epoch seconds) of the exhausted limit in a usage summary. The
24
+ * limit named `limitId` wins; an image limit is next; the account's main
25
+ * window is last.
26
+ */
27
+ export function limitResetFromUsage(payload, limitId = '') {
28
+ const extra = Array.isArray(payload?.additional_rate_limits) ? payload.additional_rate_limits : [];
29
+ const id = String(limitId).toLowerCase();
30
+ const names = (entry) => [entry?.metered_feature, entry?.limit_name].map((name) => String(name || '').toLowerCase());
31
+ const named =
32
+ (id && extra.find((entry) => names(entry).includes(id))) ||
33
+ extra.find((entry) => names(entry).some((name) => name.includes('image')));
34
+ return exhaustedResetAt(named ? named.rate_limit : payload?.rate_limit);
35
+ }
36
+
37
+ /**
38
+ * Look the reset time up for the signed-in account. A failed lookup yields
39
+ * null: the caller still reports the limit, just without a time.
40
+ */
41
+ export async function chatgptLimitResetAt({ auth, limitId = '', fetchFn = fetch, signal } = {}) {
42
+ try {
43
+ const headers = { Authorization: `Bearer ${auth.access_token}`, 'User-Agent': codexUserAgent() };
44
+ if (auth.account_id) headers['ChatGPT-Account-Id'] = auth.account_id;
45
+ const res = await fetchFn(CHATGPT_USAGE_URL, {
46
+ headers,
47
+ redirect: 'error',
48
+ signal: timeoutSignal(signal, LOOKUP_TIMEOUT_MS),
49
+ });
50
+ if (!res.ok) return null;
51
+ return limitResetFromUsage(await res.json(), limitId);
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
@@ -41,6 +41,43 @@ export function getMediaDefault(kind) {
41
41
  return lane ? { kind: key, lane, model: text(row?.model) } : null;
42
42
  }
43
43
 
44
+ // Settings → Built-in → Media generation. Unlike the remembered selection above
45
+ // (rewritten by every generation), a pin is only ever written by the user:
46
+ // `{ lane, model, account }` per kind, where an empty model is the lane default
47
+ // and an empty account follows that provider's currently selected account.
48
+ const PINNED = 'pinned';
49
+
50
+ function readPins() {
51
+ const pins = readAll()[PINNED];
52
+ return pins && typeof pins === 'object' && !Array.isArray(pins) ? pins : {};
53
+ }
54
+
55
+ /** `{ kind, lane, model, account }` for the kind, or null when it is on auto. */
56
+ export function getMediaPin(kind) {
57
+ const key = text(kind);
58
+ if (!MEDIA_KINDS.includes(key)) return null;
59
+ const row = readPins()[key];
60
+ const lane = text(row?.lane);
61
+ return lane ? { kind: key, lane, model: text(row?.model), account: text(row?.account) } : null;
62
+ }
63
+
64
+ export function getMediaPins() {
65
+ return Object.fromEntries(MEDIA_KINDS.map((kind) => [kind, getMediaPin(kind)]));
66
+ }
67
+
68
+ /** Pin one kind to a lane/model/account; an empty `lane` returns it to auto. */
69
+ export function setMediaPin({ kind, lane, model, account } = {}) {
70
+ const key = text(kind);
71
+ if (!MEDIA_KINDS.includes(key)) throw new TypeError(`kind must be one of ${MEDIA_KINDS.join(', ')}`);
72
+ const laneId = text(lane);
73
+ const { [key]: _previous, ...others } = readPins();
74
+ const pins = laneId ? { ...others, [key]: { lane: laneId, model: text(model), account: text(account) } } : others;
75
+ const path = defaultsPath();
76
+ mkdirSync(dirname(path), { recursive: true });
77
+ writeJsonAtomicSync(path, { ...readAll(), [PINNED]: pins });
78
+ return getMediaPin(key);
79
+ }
80
+
44
81
  /** Remember the lane (and model) for one kind. `model` may be empty: the lane default then applies. */
45
82
  export function setMediaDefault({ kind, lane, model } = {}) {
46
83
  const key = text(kind);
@@ -8,7 +8,8 @@
8
8
  */
9
9
  import { randomUUID } from 'node:crypto';
10
10
  import { MAX_GENERATED_MEDIA_BYTES } from './download.mjs';
11
- import { mediaError, resolveMediaRequest } from './lanes.mjs';
11
+ import { mediaError, mediaLaneAccountProvider, resolveMediaRequest } from './lanes.mjs';
12
+ import { withProviderAccount } from '../shared/provider-auth-binding.mjs';
12
13
  import { saveMediaAsset } from './store.mjs';
13
14
  import { setMediaDefault } from './defaults.mjs';
14
15
  import { recordMediaUsage } from './media-usage.mjs';
@@ -77,8 +78,17 @@ async function runAdapter({ lane, kind, model, requestModel, prompt, options, re
77
78
  /**
78
79
  * Validate + start one generation. Returns the initial snapshot immediately;
79
80
  * the caller polls getMediaJob for progress and the finished asset id.
81
+ * `account` runs the whole job (catalog check, credentials, adapter) as that
82
+ * connected account of the lane's provider, whatever the chat is using; an
83
+ * account that cannot serve the call fails it, never another account.
80
84
  */
81
- export async function startMediaJob({
85
+ export async function startMediaJob(input = {}) {
86
+ const account = String(input?.account || '').trim();
87
+ const provider = account ? mediaLaneAccountProvider(input.lane) : '';
88
+ return provider ? withProviderAccount(provider, account, () => beginMediaJob(input)) : beginMediaJob(input);
89
+ }
90
+
91
+ async function beginMediaJob({
82
92
  lane: laneId,
83
93
  kind,
84
94
  model,
@@ -8,6 +8,7 @@
8
8
  * an unauthenticated lane fails closed.
9
9
  */
10
10
  import { hasAgentApiKey } from '../shared/provider-api-key.mjs';
11
+ import { ACCOUNT_PROVIDERS } from '../shared/provider-accounts.mjs';
11
12
  // Credential probes are file-level checks: the catalog must not drag the whole
12
13
  // provider graph in just to render lane availability.
13
14
  import {
@@ -64,6 +65,7 @@ const LANES = Object.freeze([
64
65
  image: Object.freeze({
65
66
  controls: Object.freeze({
66
67
  maxReferences: 5,
68
+ background: Object.freeze(['auto', 'transparent', 'opaque']),
67
69
  }),
68
70
  }),
69
71
  }),
@@ -100,6 +102,13 @@ const LANES = Object.freeze([
100
102
 
101
103
  const LANE_BY_ID = new Map(LANES.map((lane) => [lane.id, lane]));
102
104
 
105
+ /** The provider whose connected accounts a lane can generate through, or ''
106
+ * for a lane that authenticates with a single API key. */
107
+ export function mediaLaneAccountProvider(laneId) {
108
+ const provider = LANE_BY_ID.get(String(laneId || '').trim())?.auth.provider;
109
+ return ACCOUNT_PROVIDERS.includes(provider) ? provider : '';
110
+ }
111
+
103
112
  const OAUTH_PROBES = Object.freeze({
104
113
  'grok-oauth': hasGrokOAuthCredentials,
105
114
  'openai-oauth': hasOpenAIOAuthCredentials,
@@ -143,6 +152,9 @@ export function createMediaLaneCatalog({ loadModels = loadMediaModels, authentic
143
152
  authType: lane.auth.type,
144
153
  authProvider: lane.auth.provider,
145
154
  authenticated: signedIn,
155
+ // What the lane can generate once signed in; `kinds` lists only what its
156
+ // live catalog offers right now.
157
+ supportedKinds: MEDIA_KINDS.filter((kind) => lane[kind]),
146
158
  kinds: [],
147
159
  image: null,
148
160
  video: null,
@@ -31,7 +31,7 @@ export const TOOL_DEFS = [
31
31
  lane: {
32
32
  type: 'string',
33
33
  description:
34
- 'Lane id from list; omitted = the lane the user last chose for the kind (Studio selection or last generation), else the first signed-in lane.',
34
+ 'Lane id from list; pass it when the user names a source. Omitted = the lane the user last chose for the kind, else the default signed-in lane.',
35
35
  },
36
36
  model: {
37
37
  type: 'string',
@@ -44,10 +44,21 @@ export const TOOL_DEFS = [
44
44
  description:
45
45
  'generate: output file (png/jpg/mp4); relative to the project. status: copy a finished asset here.',
46
46
  },
47
- aspect: { type: 'string', description: 'Aspect ratio, e.g. 16:9, 9:16, 1:1; must be one the model lists.' },
48
- resolution: { type: 'string', description: 'Model resolution token from list (1k, 2k, 720p, 1080p).' },
47
+ aspect: {
48
+ type: 'string',
49
+ description: 'Aspect ratio, e.g. 16:9, 9:16, 1:1; only when the model lists aspectRatio, else omit.',
50
+ },
51
+ resolution: {
52
+ type: 'string',
53
+ description: 'Resolution token (1k, 2k, 720p, 1080p); only when the model lists it, else omit.',
54
+ },
49
55
  duration: { type: 'number', description: "video: seconds, within the model's listed durations." },
50
- quality: { type: 'string', description: 'Quality token when the model lists one.' },
56
+ quality: { type: 'string', description: 'Quality token; only when the model lists one, else omit.' },
57
+ background: {
58
+ type: 'string',
59
+ enum: ['auto', 'transparent', 'opaque'],
60
+ description: 'image: transparent for real alpha; only when the model lists background, else omit.',
61
+ },
51
62
  references: {
52
63
  type: 'array',
53
64
  items: { type: 'string' },
@@ -8,6 +8,7 @@ import { MEDIA_ACTIONS, MEDIA_KINDS } from './tool-defs.mjs';
8
8
  import { clean } from '../shared/clean.mjs';
9
9
 
10
10
  const POLL_MS = 750;
11
+ const DEFAULT_LANE = 'openai-oauth';
11
12
  const TIMEOUT_MS = Object.freeze({ image: 180_000, video: 900_000 });
12
13
  // OpenAI's image lane takes pixel sizes, not ratios; the placement still speaks in ratios.
13
14
  const OPENAI_SIZE = Object.freeze({
@@ -36,6 +37,7 @@ async function mediaGraph(deps) {
36
37
  jobs: await import('./jobs.mjs'),
37
38
  store: await import('./store.mjs'),
38
39
  defaults: await import('./defaults.mjs'),
40
+ accounts: await import('../shared/provider-auth-binding.mjs'),
39
41
  };
40
42
  return graph;
41
43
  }
@@ -130,8 +132,9 @@ export function listMediaCatalog(lanes, { kind = '', model = '' } = {}, remember
130
132
 
131
133
  /**
132
134
  * Lane for a generate: the requested one, else the lane the user last chose
133
- * for this kind (Studio selection or last generation), else the first
134
- * signed-in lane. `laneSource` tells the caller which of the three applied.
135
+ * for this kind (Studio selection or last generation), else the signed-in
136
+ * ChatGPT-subscription lane, else the first signed-in lane. `laneSource` is
137
+ * `first` for either default.
135
138
  */
136
139
  function pickLane(lanes, kind, requested, remembered = null) {
137
140
  const available = lanes.filter((lane) => lane.authenticated && lane.kinds.includes(kind));
@@ -153,9 +156,8 @@ function pickLane(lanes, kind, requested, remembered = null) {
153
156
  );
154
157
  }
155
158
  const rememberedLane = remembered?.lane ? available.find((entry) => entry.id === remembered.lane) : null;
156
- return rememberedLane
157
- ? { lane: rememberedLane, laneSource: 'remembered' }
158
- : { lane: available[0], laneSource: 'first' };
159
+ if (rememberedLane) return { lane: rememberedLane, laneSource: 'remembered' };
160
+ return { lane: available.find((entry) => entry.id === DEFAULT_LANE) || available[0], laneSource: 'first' };
159
161
  }
160
162
 
161
163
  function readRemembered(defaults, kind) {
@@ -166,6 +168,14 @@ function readRemembered(defaults, kind) {
166
168
  }
167
169
  }
168
170
 
171
+ function readPin(defaults, kind) {
172
+ try {
173
+ return defaults?.getMediaPin?.(kind) || null;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
169
179
  function allowed(controls, key) {
170
180
  const list = controls?.[key];
171
181
  return Array.isArray(list) && list.length ? list : null;
@@ -185,9 +195,13 @@ export function buildOptions(
185
195
  lane,
186
196
  kind,
187
197
  controls,
188
- { aspect = '', resolution = '', duration = null, quality = '' } = {}
198
+ { aspect = '', resolution = '', duration = null, quality = '', background = '' } = {}
189
199
  ) {
190
200
  const options = {};
201
+ if (background) {
202
+ validateChoice(controls, 'background', background, 'background');
203
+ if (background !== 'auto') options.background = background;
204
+ }
191
205
  if (lane.id === 'openai-oauth') {
192
206
  if (!allowed(controls, 'size')) {
193
207
  if ([aspect, resolution, quality].some((value) => value && value !== 'auto')) {
@@ -291,9 +305,21 @@ async function generate(args, { cwd, signal, deps, sessionId, sourceType }) {
291
305
  const requestedPath = clean(args.path);
292
306
  if (!requestedPath) throw new MediaToolError('generate requires path: where to write the file');
293
307
  const target = fullPath(requestedPath, cwd);
294
- const { lanes, jobs, store, defaults } = await mediaGraph(deps);
295
- const remembered = readRemembered(defaults, kind);
296
- const { lane, laneSource } = pickLane(await lanes.listMediaLanes(), kind, clean(args.lane), remembered);
308
+ const { lanes, jobs, store, defaults, accounts } = await mediaGraph(deps);
309
+ // A generator the chat names (lane or model) wins for this call; otherwise
310
+ // the pinned default for the kind applies, and with none the remembered
311
+ // choice / ordered default. A pin never falls back: its lane, model and
312
+ // account errors surface as they are.
313
+ const pin = clean(args.lane) || clean(args.model) ? null : readPin(defaults, kind);
314
+ const remembered = pin ? null : readRemembered(defaults, kind);
315
+ const accountProvider = pin?.account ? lanes.mediaLaneAccountProvider?.(pin.lane) || '' : '';
316
+ const listLanes = () =>
317
+ accountProvider
318
+ ? accounts.withProviderAccount(accountProvider, pin.account, () => lanes.listMediaLanes())
319
+ : lanes.listMediaLanes();
320
+ const picked = pickLane(await listLanes(), kind, pin ? pin.lane : clean(args.lane), remembered);
321
+ const { lane } = picked;
322
+ const laneSource = pin ? 'pinned' : picked.laneSource;
297
323
  const spec = lane[kind];
298
324
  // The remembered model only applies on the remembered lane and only while
299
325
  // that lane still lists it; otherwise the lane default is the safe choice.
@@ -301,7 +327,7 @@ async function generate(args, { cwd, signal, deps, sessionId, sourceType }) {
301
327
  laneSource === 'remembered' && remembered.model && spec.models.some((entry) => entry.id === remembered.model)
302
328
  ? remembered.model
303
329
  : '';
304
- const modelId = clean(args.model) || rememberedModel || spec.defaultModel;
330
+ const modelId = clean(args.model) || pin?.model || rememberedModel || spec.defaultModel;
305
331
  const model = modelControls(lane, kind, modelId);
306
332
  if (!model)
307
333
  throw new MediaToolError(`model "${modelId}" is not available on ${lane.id} for ${kind}`, {
@@ -312,6 +338,7 @@ async function generate(args, { cwd, signal, deps, sessionId, sourceType }) {
312
338
  resolution: clean(args.resolution),
313
339
  duration: args.duration ?? null,
314
340
  quality: clean(args.quality),
341
+ background: clean(args.background),
315
342
  });
316
343
  const references = await readReferences(args.references, cwd, model.controls);
317
344
  const started = await jobs.startMediaJob({
@@ -323,6 +350,7 @@ async function generate(args, { cwd, signal, deps, sessionId, sourceType }) {
323
350
  references,
324
351
  sessionId,
325
352
  sourceType,
353
+ ...(accountProvider ? { account: pin.account } : {}),
326
354
  });
327
355
  const base = { lane: lane.id, model: modelId, laneSource, options, referenceCount: references.length, prompt };
328
356
  if (args.wait === false) {
@@ -396,11 +424,12 @@ export async function executeMediaTool(
396
424
  ...listMediaCatalog(
397
425
  await lanes.listMediaLanes(),
398
426
  { kind, model: clean(args.model) },
399
- readRemembered(defaults, kind)
427
+ readPin(defaults, kind) || readRemembered(defaults, kind)
400
428
  ),
401
429
  });
402
430
  }
403
- if (action === 'generate') return mediaToolResult(await generate(args, { cwd, signal, deps, sessionId, sourceType }));
431
+ if (action === 'generate')
432
+ return mediaToolResult(await generate(args, { cwd, signal, deps, sessionId, sourceType }));
404
433
  if (action === 'status') return mediaToolResult(await status(args, { cwd, deps }));
405
434
  return mediaToolResult(await cancel(args, { deps }));
406
435
  } catch (error) {