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
@@ -23,6 +23,7 @@
23
23
  * Replay stays gated by the stream-outcome contract: a stream that exposed
24
24
  * text or dispatched a tool is never re-issued.
25
25
  */
26
+ import { noteAbandonedUsage } from '../../../shared/llm/usage-context.mjs';
26
27
  import { readStreamOutcome } from './lib/stream-outcome.mjs';
27
28
  import { DETERMINISTIC_KINDS, providerErrorKind, typedCodeKind } from './provider-error-kind.mjs';
28
29
  import { midstreamRetryDelayMs } from './retry-backoff.mjs';
@@ -42,7 +43,7 @@ export const GEMINI_REQUEST_RETRY = Object.freeze({
42
43
  });
43
44
 
44
45
  // A server wait longer than this is a lock-out, not a retry.
45
- export const GEMINI_MAX_RETRYABLE_ADVICE_MS = 300_000;
46
+ const GEMINI_MAX_RETRYABLE_ADVICE_MS = 300_000;
46
47
 
47
48
  function hasLongAdvice(err) {
48
49
  const { kind, retryAfterMs } = providerErrorKind(err);
@@ -56,7 +57,7 @@ export function geminiEndsWithFunctionResponse(contents) {
56
57
  }
57
58
 
58
59
  export const GEMINI_MIDSTREAM_MAX_ATTEMPTS = 4;
59
- export const GEMINI_MIDSTREAM_INITIAL_DELAY_MS = 1000;
60
+ const GEMINI_MIDSTREAM_INITIAL_DELAY_MS = 1000;
60
61
 
61
62
  const midstreamStepMs = (streamAttempt) => GEMINI_MIDSTREAM_INITIAL_DELAY_MS * 2 ** streamAttempt;
62
63
 
@@ -116,7 +117,14 @@ export function geminiInvalidStreamReason(response, textLeakGuard = null, { afte
116
117
  * open + consume and calls `connected()` once the response opened; it resolves
117
118
  * `{ response, textLeakGuard }`.
118
119
  */
119
- export async function runGeminiStreamAttempts({ run, signal, onRetry, sleepFn, afterToolResult = false }) {
120
+ export async function runGeminiStreamAttempts({
121
+ run,
122
+ signal,
123
+ onRetry,
124
+ sleepFn,
125
+ afterToolResult = false,
126
+ failureUsage = null,
127
+ }) {
120
128
  for (let streamAttempt = 0; ; streamAttempt++) {
121
129
  let connected = false;
122
130
  let result;
@@ -157,29 +165,60 @@ export async function runGeminiStreamAttempts({ run, signal, onRetry, sleepFn, a
157
165
  await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
158
166
  continue;
159
167
  }
160
- const invalid = geminiInvalidStreamReason(result.response, result.textLeakGuard, { afterToolResult });
161
- if (!invalid || signal?.aborted) return result;
162
- const unusable = Object.assign(new Error(`Gemini stream unusable: ${invalid}`), {
163
- name: 'InvalidStreamError',
164
- code: invalid.toLowerCase(),
168
+ if (signal?.aborted) return result;
169
+ const reask = geminiInvalidStreamReask(result.response, result.textLeakGuard, {
170
+ afterToolResult,
171
+ streamAttempt,
172
+ failureUsage,
165
173
  });
166
- if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS) {
167
- // A spent ladder on an empty answer is a failure, not an answer; a
168
- // malformed / unexpected tool call keeps its finish-reason error.
169
- if (invalid !== 'NO_RESPONSE_TEXT') return result;
170
- throw markProviderRecoveryExhausted(unusable, {
171
- owner: 'gemini-stream',
172
- attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
173
- terminal: true,
174
- });
175
- }
176
- const waitMs = midstreamStepMs(streamAttempt);
174
+ if (!reask) return result;
177
175
  onRetry?.({
178
176
  attempt: streamAttempt + 1,
179
177
  maxAttempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
180
- lastErr: unusable,
181
- delayMs: waitMs,
178
+ lastErr: reask.unusable,
179
+ delayMs: reask.waitMs,
180
+ });
181
+ await sleepWithAbort(reask.waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
182
+ }
183
+ }
184
+
185
+ /**
186
+ * The bounded re-ask for a finished but unusable response (empty STOP,
187
+ * malformed / unexpected tool call), shared by every Gemini-shaped transport:
188
+ * `{ unusable, waitMs }` when the request should be re-issued after `waitMs`,
189
+ * null when the response stands (usable, or a spent ladder on a malformed
190
+ * call, which keeps its finish-reason error). A spent ladder on an empty
191
+ * answer throws: it is a failure, not an answer.
192
+ *
193
+ * The discarded response was still billed. `failureUsage` ({ model,
194
+ * normalize(usageMetadata) }) records it: a scheduled re-ask notes it as
195
+ * abandoned usage; a spent ladder that throws carries it as
196
+ * partialUsage/partialModel (the send's error accounting records it once).
197
+ */
198
+ export function geminiInvalidStreamReask(
199
+ response,
200
+ textLeakGuard,
201
+ { afterToolResult = false, streamAttempt = 0, failureUsage = null } = {}
202
+ ) {
203
+ const invalid = geminiInvalidStreamReason(response, textLeakGuard, { afterToolResult });
204
+ if (!invalid) return null;
205
+ const unusable = Object.assign(new Error(`Gemini stream unusable: ${invalid}`), {
206
+ name: 'InvalidStreamError',
207
+ code: invalid.toLowerCase(),
208
+ });
209
+ const usage = response?.usageMetadata && failureUsage ? failureUsage.normalize(response.usageMetadata) : null;
210
+ if (streamAttempt + 1 >= GEMINI_MIDSTREAM_MAX_ATTEMPTS) {
211
+ if (invalid !== 'NO_RESPONSE_TEXT') return null;
212
+ if (usage) {
213
+ unusable.partialUsage = usage;
214
+ unusable.partialModel = failureUsage.model;
215
+ }
216
+ throw markProviderRecoveryExhausted(unusable, {
217
+ owner: 'gemini-stream',
218
+ attempts: GEMINI_MIDSTREAM_MAX_ATTEMPTS,
219
+ terminal: true,
182
220
  });
183
- await sleepWithAbort(waitMs, signal, sleepFn, 'Gemini stream retry wait aborted');
184
221
  }
222
+ if (usage) noteAbandonedUsage(usage, failureUsage.model);
223
+ return { unusable, waitMs: midstreamStepMs(streamAttempt) };
185
224
  }
@@ -96,6 +96,7 @@ export async function streamGeminiSdkAttempt(genModel, stream, attemptSignal, on
96
96
  signal: attemptSignal,
97
97
  onStreamDelta: callbacks.onStreamDelta,
98
98
  onTextDelta: callbacks.onTextDelta,
99
+ onReasoningDelta: callbacks.onReasoningDelta,
99
100
  textLeakGuard,
100
101
  label: 'Gemini SDK streamGenerateContent',
101
102
  failureUsage: geminiFailureUsage(stream.opts, stream.cachedContent, stream.useModel),
@@ -51,7 +51,7 @@ const GEMINI_RPC_CODES = new Set(['UNAVAILABLE', 'DEADLINE_EXCEEDED', 'ABORTED',
51
51
  * Copy a typed Gemini/gRPC status onto the error so classifyError can retry
52
52
  * UNAVAILABLE/DEADLINE_EXCEEDED/ABORTED/INTERNAL without an HTTP status.
53
53
  */
54
- export function stampGeminiRpcError(err) {
54
+ function stampGeminiRpcError(err) {
55
55
  if (!err || typeof err !== 'object') return err;
56
56
  if (typeof err.geminiStatus === 'string' && err.geminiStatus) return err;
57
57
  for (const field of [err.status, err.error?.status, err.cause?.status, err.cause?.geminiStatus]) {
@@ -326,16 +326,43 @@ function mergeStreamMetadata(previous, next) {
326
326
 
327
327
  // A stream that ends with no finishReason is unfinished, whatever it carried
328
328
  // (thoughts only, an empty body, even right after a tool result); only a
329
- // STOP that carries no text is a completed, empty answer.
330
- function assertGeminiStreamCompleted({ sawStreamChunk, finishReason, promptBlockReason, label }) {
329
+ // STOP that carries no text is a completed, empty answer. The one exception
330
+ // is a stream that delivered at least one whole functionCall part and no
331
+ // explicitly unfinished one: the gateway ends such a tool-use turn without a
332
+ // finishReason, and every call is intact.
333
+ function assertGeminiStreamCompleted({ sawStreamChunk, finishReason, promptBlockReason, parts, label }) {
331
334
  if (!sawStreamChunk) {
332
335
  throw geminiTruncatedStreamError(`${label} truncated: empty stream`);
333
336
  }
334
337
  if (geminiFinishReasonKind(finishReason) === 'none' && geminiFinishReasonKind(promptBlockReason) === 'none') {
338
+ if (Array.isArray(parts)) {
339
+ const calls = parts.filter((part) => part?.functionCall);
340
+ if (calls.length > 0 && calls.every(isCompleteFunctionCallPart)) return;
341
+ }
335
342
  throw geminiTruncatedStreamError(`${label} truncated: no finishReason`);
336
343
  }
337
344
  }
338
345
 
346
+ // A functionCall part is whole when it names the function and its arguments
347
+ // are not still streaming in pieces.
348
+ function isIncompleteFunctionCallPart(part) {
349
+ const call = part?.functionCall;
350
+ if (!call) return false;
351
+ return (
352
+ call.willContinue === true ||
353
+ call.will_continue === true ||
354
+ call.partialArgs !== undefined ||
355
+ call.partial_args !== undefined
356
+ );
357
+ }
358
+
359
+ function isCompleteFunctionCallPart(part) {
360
+ const call = part?.functionCall;
361
+ if (!call || typeof call.name !== 'string' || call.name === '') return false;
362
+ if (isIncompleteFunctionCallPart(part)) return false;
363
+ return call.args === undefined || (call.args !== null && typeof call.args === 'object' && !Array.isArray(call.args));
364
+ }
365
+
339
366
  // Concatenate the text parts of a single streamed Gemini chunk. Used to feed
340
367
  // the gateway live-text relay (onTextDelta) with the incremental text payload
341
368
  // as each SSE/SDK chunk arrives. Returns '' for tool-call / thought-only /
@@ -351,6 +378,27 @@ function geminiChunkText(chunk) {
351
378
  return text;
352
379
  }
353
380
 
381
+ // The thought-summary text of a streamed chunk, relayed as reasoning and never
382
+ // mixed into the answer text.
383
+ function geminiChunkThoughtText(chunk) {
384
+ const parts = chunk?.candidates?.[0]?.content?.parts;
385
+ if (!Array.isArray(parts)) return '';
386
+ let text = '';
387
+ for (const p of parts) {
388
+ if (p && p.thought === true && typeof p.text === 'string') text += p.text;
389
+ }
390
+ return text;
391
+ }
392
+
393
+ function relayGeminiReasoning(chunk, onReasoningDelta) {
394
+ if (!onReasoningDelta) return;
395
+ const thought = geminiChunkThoughtText(chunk);
396
+ if (!thought) return;
397
+ try {
398
+ onReasoningDelta(thought);
399
+ } catch {}
400
+ }
401
+
354
402
  // Returns the text actually RELAYED to the live sink ('' when it was only
355
403
  // buffered), so callers can track visible output rather than raw stream text.
356
404
  function relayGeminiStreamText(t, { onTextDelta, textLeakGuard }) {
@@ -384,6 +432,9 @@ export function createGeminiTextLeakGuard({
384
432
  const _enabled = _knownTools.size > 0;
385
433
  const _isKnownTool = (name) => _knownTools.has(name);
386
434
  let leakBuffer = '';
435
+ // Markdown fence / inline-code state carried across chunks: a tag inside a
436
+ // code fence opened in an earlier chunk is an example, not a call.
437
+ let fenceState = null;
387
438
  let relayedText = '';
388
439
  const leakedCalls = [];
389
440
  // With `deferToolCalls`, recovered calls wait here until the caller has seen
@@ -425,8 +476,18 @@ export function createGeminiTextLeakGuard({
425
476
  const pumpLeakBuffer = (final) => {
426
477
  if (!_enabled) return;
427
478
  if (!leakBuffer && !final) return;
428
- const { emit, calls, rest } = scanLeakedToolCalls(leakBuffer, { isKnownTool: _isKnownTool, final });
479
+ const {
480
+ emit,
481
+ calls,
482
+ rest,
483
+ fenceState: nextFence,
484
+ } = scanLeakedToolCalls(leakBuffer, {
485
+ isKnownTool: _isKnownTool,
486
+ final,
487
+ fenceState,
488
+ });
429
489
  leakBuffer = rest;
490
+ fenceState = nextFence;
430
491
  // Only text handed to the live sink counts as relayed/visible. Without
431
492
  // an onTextDelta sink the scrubbed text is buffered for the final
432
493
  // result and never shown, so it must not deny a replay.
@@ -521,6 +582,7 @@ export async function consumeGeminiRestStreamResponse(
521
582
  signal,
522
583
  onStreamDelta,
523
584
  onTextDelta,
585
+ onReasoningDelta = null,
524
586
  textLeakGuard,
525
587
  label,
526
588
  unwrapChunk = null,
@@ -568,6 +630,7 @@ export async function consumeGeminiRestStreamResponse(
568
630
  try {
569
631
  onStreamDelta?.(geminiChunkProgressKind(parsed));
570
632
  } catch {}
633
+ relayGeminiReasoning(parsed, onReasoningDelta);
571
634
  if (onTextDelta || textLeakGuard) {
572
635
  relayedText += relayGeminiStreamText(geminiChunkText(parsed), { onTextDelta, textLeakGuard });
573
636
  }
@@ -665,6 +728,7 @@ export async function consumeGeminiRestStreamResponse(
665
728
  sawStreamChunk,
666
729
  finishReason,
667
730
  promptBlockReason,
731
+ parts: aggregated.candidates?.[0]?.content?.parts,
668
732
  label,
669
733
  });
670
734
  } catch (err) {
@@ -679,6 +743,7 @@ export async function consumeGeminiSdkStream(
679
743
  signal,
680
744
  onStreamDelta,
681
745
  onTextDelta,
746
+ onReasoningDelta = null,
682
747
  textLeakGuard,
683
748
  label,
684
749
  cancelGeneration,
@@ -741,6 +806,7 @@ export async function consumeGeminiSdkStream(
741
806
  try {
742
807
  onStreamDelta?.(geminiChunkProgressKind(step.value));
743
808
  } catch {}
809
+ relayGeminiReasoning(step.value, onReasoningDelta);
744
810
  if (onTextDelta || textLeakGuard) {
745
811
  const t = geminiChunkText(step.value);
746
812
  relayedText += relayGeminiStreamText(t, { onTextDelta, textLeakGuard });
@@ -770,6 +836,7 @@ export async function consumeGeminiSdkStream(
770
836
  sawStreamChunk: reader.sawStreamChunk,
771
837
  finishReason,
772
838
  promptBlockReason,
839
+ parts: raw?.candidates?.[0]?.content?.parts,
773
840
  label,
774
841
  });
775
842
  return raw;
@@ -31,6 +31,7 @@ import {
31
31
  } from './gemini-retry-policy.mjs';
32
32
  import { geminiTextLeakGuardFor, streamGeminiSdkAttempt } from './gemini-sdk-request.mjs';
33
33
  import { emitGeminiToolCalls } from './gemini-schema.mjs';
34
+ import { createGeminiReasoningRelay } from './gemini-reasoning-relay.mjs';
34
35
  import { buildGeminiRequest, geminiCachedRestBody } from './gemini-request-body.mjs';
35
36
  import {
36
37
  geminiIncompleteError,
@@ -134,10 +135,14 @@ function geminiRetryLogger(opts, tag) {
134
135
  }
135
136
 
136
137
  function geminiSendCallbacks(opts) {
138
+ const reasoning = createGeminiReasoningRelay(opts);
137
139
  return {
138
140
  onStreamDelta: typeof opts.onStreamDelta === 'function' ? opts.onStreamDelta : null,
139
141
  onToolCall: typeof opts.onToolCall === 'function' ? opts.onToolCall : null,
140
142
  onTextDelta: typeof opts.onTextDelta === 'function' ? opts.onTextDelta : null,
143
+ onReasoningDelta: reasoning.onReasoningDelta,
144
+ // Thinking already shown by a dead or discarded attempt.
145
+ retractReasoning: reasoning.retract,
141
146
  };
142
147
  }
143
148
 
@@ -220,11 +225,12 @@ export class GeminiProvider {
220
225
  * dispatched tool can never run twice. Returns the aggregated response, or
221
226
  * null when the failure is ineligible or the fallback itself fails.
222
227
  */
223
- async _recoverGeminiNonStreaming({ streamErr, signal, opts, model, generate }) {
228
+ async _recoverGeminiNonStreaming({ streamErr, signal, opts, model, generate, retractReasoning }) {
224
229
  if (!canFallbackNonStreaming(streamErr, { signal })) return null;
225
230
  noteErrorUsage(streamErr);
226
231
  let aggregated;
227
232
  try {
233
+ retractReasoning?.();
228
234
  signalRequesting(opts);
229
235
  aggregated = await generate(signal || undefined);
230
236
  } catch {
@@ -549,6 +555,16 @@ export class GeminiProvider {
549
555
  }
550
556
  }
551
557
 
558
+ // The output limit the live model roster declared for this model, or null.
559
+ _rosterOutputLimit(model) {
560
+ try {
561
+ const id = geminiBareModelId(model);
562
+ return this._modelCache.loadSync()?.find?.((entry) => entry?.id === id)?.outputTokens ?? null;
563
+ } catch {
564
+ return null;
565
+ }
566
+ }
567
+
552
568
  async _doSend(messages, model, tools, sendOpts, internal = {}) {
553
569
  const opts = sendOpts || {};
554
570
  const signal = opts.signal || null;
@@ -556,7 +572,7 @@ export class GeminiProvider {
556
572
  throwIfGeminiAborted(signal);
557
573
 
558
574
  const useModel = model || (await ensureLatestGeminiModel(this));
559
- const request = buildGeminiRequest(messages, useModel, tools, opts);
575
+ const request = buildGeminiRequest(messages, useModel, tools, opts, this._rosterOutputLimit(useModel));
560
576
  signalRequesting(opts);
561
577
 
562
578
  // Explicit cachedContents (system + tools + prior-turn transcript).
@@ -690,7 +706,9 @@ export class GeminiProvider {
690
706
  sleepFn: this.config?.sleepFn,
691
707
  onRetry: geminiRetryLogger(opts, '[gemini-rest]'),
692
708
  afterToolResult,
709
+ failureUsage: geminiFailureUsage(opts, cachedContent, useModel),
693
710
  run: async ({ signal: attemptSignal, connected }) => {
711
+ callbacks.retractReasoning();
694
712
  signalRequesting(opts);
695
713
  const res = await this._openRestStream(genUrl, body, attemptSignal);
696
714
  connected();
@@ -699,6 +717,7 @@ export class GeminiProvider {
699
717
  signal: attemptSignal,
700
718
  onStreamDelta: callbacks.onStreamDelta,
701
719
  onTextDelta: callbacks.onTextDelta,
720
+ onReasoningDelta: callbacks.onReasoningDelta,
702
721
  textLeakGuard,
703
722
  label: 'Gemini REST streamGenerateContent',
704
723
  failureUsage: geminiFailureUsage(opts, cachedContent, useModel),
@@ -722,6 +741,7 @@ export class GeminiProvider {
722
741
  opts,
723
742
  model: useModel,
724
743
  generate: (fallbackSignal) => this._restGenerateContent(useModel, apiKey, body, fallbackSignal),
744
+ retractReasoning: callbacks.retractReasoning,
725
745
  });
726
746
  if (!recovered) throw err;
727
747
  return { response: recovered, textLeakGuard: null };
@@ -760,7 +780,9 @@ export class GeminiProvider {
760
780
  sleepFn: this.config?.sleepFn,
761
781
  onRetry: geminiRetryLogger(opts, '[gemini]'),
762
782
  afterToolResult: geminiEndsWithFunctionResponse(contents),
783
+ failureUsage: geminiFailureUsage(opts, stream.cachedContent, useModel),
763
784
  run: async ({ signal: attemptSignal, connected }) => {
785
+ stream.callbacks.retractReasoning();
764
786
  signalRequesting(opts);
765
787
  return await streamGeminiSdkAttempt(genModel, stream, attemptSignal, connected);
766
788
  },
@@ -775,6 +797,7 @@ export class GeminiProvider {
775
797
  const result = await genModel.generateContent({ contents }, { signal: fallbackSignal });
776
798
  return result?.response ?? null;
777
799
  },
800
+ retractReasoning: stream.callbacks.retractReasoning,
778
801
  });
779
802
  if (!recovered) throw err;
780
803
  return { response: recovered, textLeakGuard: null };
@@ -3,7 +3,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
3
3
  // Grok CLI client version for the cli-chat-proxy version gate (HTTP 426).
4
4
  // Effective = env override, else max(floor, learned-from-426, live npm latest
5
5
  // of the official @xai-official/grok CLI).
6
- export const GROK_CLI_VERSION_FLOOR = '1.0.67';
6
+ export const GROK_CLI_VERSION_FLOOR = '1.0.70';
7
7
  export const GROK_CLI_NPM_PACKAGE = '@xai-official/grok';
8
8
 
9
9
  const live = createNpmVersionSource(GROK_CLI_NPM_PACKAGE, { persistKey: 'grok-cli' });
@@ -11,12 +11,13 @@ import { reportedCostUsd } from './usage-primitives.mjs';
11
11
  import { reportInvalidTool, wireInputSchema } from './claude-bridge-schema.mjs';
12
12
  import { normalizeServerToolUse } from './anthropic-server-tool-usage.mjs';
13
13
  import { ANTHROPIC_TOOL_CALL_IDS, withWireToolCallIds } from './tool-call-wire-ids.mjs';
14
+ import { supportsAnthropicTurnScopedSystemMessages } from '../../../../shared/llm/anthropic-thinking-contract.mjs';
14
15
  export const ANTHROPIC_CACHE_TTL_STABLE = { type: 'ephemeral', ttl: '1h' };
15
16
  const ANTHROPIC_CACHE_TTL_VOLATILE = { type: 'ephemeral' };
16
17
 
17
18
  // Anthropic allows at most 4 cache_control breakpoints across the whole
18
19
  // serialized request (tools + system + messages).
19
- export const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4;
20
+ const ANTHROPIC_MAX_CACHE_BREAKPOINTS = 4;
20
21
 
21
22
  // A block can carry a breakpoint unless it is thinking/redacted_thinking or an
22
23
  // empty text block (both are rejected by the API).
@@ -98,6 +99,84 @@ export function appendAnthropicTailReminder(anthropicMessages, reminder) {
98
99
  return anthropicMessages;
99
100
  }
100
101
 
102
+ // Turn-scoped tail reminders (see tail-reminder-split.mjs). The capability:
103
+ // the first-party Messages routes (no gateway base URL, beta headers allowed)
104
+ // on a model that takes clear_at system messages. Everything else keeps the
105
+ // legacy appendAnthropicTailReminder layout.
106
+ // Server-side fallback needs no exclusion: live-checked on the OAuth route,
107
+ // a trailing clear_at message is accepted with `fallbacks: "default"` (Opus
108
+ // 5.5, Sonnet 5.5) and with explicit lists naming Sonnet 5 or Opus 4.8, and
109
+ // fallback runs only on a safety-classifier refusal.
110
+ export function anthropicTurnScopedReminders(provider, model, config = {}) {
111
+ return (
112
+ ANTHROPIC_NATIVE_PROVIDERS.has(provider) &&
113
+ config?.disableBetaHeaders !== true &&
114
+ (!config?.baseURL || /^https:\/\/api\.anthropic\.com(?:\/|$)/.test(config.baseURL)) &&
115
+ supportsAnthropicTurnScopedSystemMessages(model)
116
+ );
117
+ }
118
+
119
+ // The stored reminder of a lowered assistant turn (toAnthropicMessages). A
120
+ // symbol key: JSON serialization never puts it on the wire.
121
+ const TURN_REMINDER = Symbol('turnReminder');
122
+
123
+ function assistantWireMessage(message, content) {
124
+ const wire = { role: 'assistant', content };
125
+ if (typeof message.turnReminder === 'string' && message.turnReminder) wire[TURN_REMINDER] = message.turnReminder;
126
+ return wire;
127
+ }
128
+
129
+ // Derived only from the stored text, so a replay is byte-identical to the
130
+ // request that first sent it. Text only, never a cache_control (both 400).
131
+ function turnReminderMessage(text) {
132
+ return { role: 'system', clear_at: 'next_user_message', content: [{ type: 'text', text }] };
133
+ }
134
+
135
+ export function hasAnthropicTurnScopedMessages(messages) {
136
+ return Array.isArray(messages) && messages.some((m) => m?.role === 'system' && m.clear_at);
137
+ }
138
+
139
+ // A turn-scoped message must follow a user turn; an effort control (empty
140
+ // system message, lowerAnthropicEffortHistory) may sit between them. Creation
141
+ // (appendAnthropicTurnReminder) and replay (withAnthropicTurnReminders) both
142
+ // decide by this one rule on the lowered array, so a turn whose request carried
143
+ // no reminder never gets one on replay.
144
+ function followsUserTurn(messages) {
145
+ let index = messages.length - 1;
146
+ const last = messages[index];
147
+ if (last?.role === 'system' && Array.isArray(last.content) && last.content.length === 0) index--;
148
+ return messages[index]?.role === 'user';
149
+ }
150
+
151
+ /** The lowered history with each stored reminder re-sent right before the
152
+ * assistant turn generated under it (after any effort control), exactly where
153
+ * that turn's own request carried it as its last message. String contents
154
+ * become one text block, so a cache marker (which turns a string into a block
155
+ * list on the turn it lands) never changes a message's shape between
156
+ * requests: the wire stays append-only. */
157
+ export function withAnthropicTurnReminders(anthropicMessages) {
158
+ const out = [];
159
+ for (const message of anthropicMessages) {
160
+ const text = message?.role === 'assistant' ? message[TURN_REMINDER] : '';
161
+ if (text && followsUserTurn(out)) out.push(turnReminderMessage(text));
162
+ out.push(
163
+ typeof message?.content === 'string' && message.content.trim()
164
+ ? { ...message, content: [{ type: 'text', text: message.content }] }
165
+ : message
166
+ );
167
+ }
168
+ return out;
169
+ }
170
+
171
+ /** The current reminder as a turn-scoped system message ending the request.
172
+ * A tail that is not a user turn (an assistant continuation) cannot take one
173
+ * and gets no reminder at all: anything else would be missing from the next
174
+ * request, which replays that turn without one. */
175
+ export function appendAnthropicTurnReminder(anthropicMessages, reminder) {
176
+ if (reminder && followsUserTurn(anthropicMessages)) anthropicMessages.push(turnReminderMessage(reminder));
177
+ return anthropicMessages;
178
+ }
179
+
101
180
  export function resolveAnthropicCacheTtls(opts) {
102
181
  const strategy = opts?.cacheStrategy || {};
103
182
  const pick = (layer, fallback) => {
@@ -278,7 +357,8 @@ export function toAnthropicMessages(transcript, availableTools, provider = '') {
278
357
  if (tb && typeof tb === 'object') content.push(tb);
279
358
  }
280
359
  }
281
- if (m.content) content.push({ type: 'text', text: m.content });
360
+ // A blank text block is a hard 400 on every request that replays it.
361
+ if (!isBlankText(m.content)) content.push({ type: 'text', text: m.content });
282
362
  for (const tc of m.toolCalls || []) {
283
363
  content.push({
284
364
  type: 'tool_use',
@@ -288,7 +368,7 @@ export function toAnthropicMessages(transcript, availableTools, provider = '') {
288
368
  });
289
369
  }
290
370
  }
291
- result.push({ role: 'assistant', content });
371
+ result.push(assistantWireMessage(m, content));
292
372
  continue;
293
373
  }
294
374
  if (m.role === 'tool') {
@@ -346,11 +426,20 @@ export function toAnthropicMessages(transcript, availableTools, provider = '') {
346
426
  ) {
347
427
  continue;
348
428
  }
349
- result.push({ role: m.role, content: normalizeContentForAnthropic(m.content) });
429
+ // An assistant turn with no text and no blocks (a refused or empty final
430
+ // kept in the transcript for diagnosis) has nothing to replay; sending it
431
+ // would be a blank text block the API rejects on every later request.
432
+ if (m.role === 'assistant' && isBlankText(m.content)) continue;
433
+ const content = normalizeContentForAnthropic(m.content);
434
+ result.push(m.role === 'assistant' ? assistantWireMessage(m, content) : { role: m.role, content });
350
435
  }
351
436
  return sanitizeAnthropicContentPairs(result);
352
437
  }
353
438
 
439
+ function isBlankText(content) {
440
+ return content == null || (typeof content === 'string' && !content.trim());
441
+ }
442
+
354
443
  export function clampAnthropicThinkingBudget(value, maxTokens) {
355
444
  const desired = Math.floor(Number(value));
356
445
  const max = Math.floor(Number(maxTokens));
@@ -702,9 +791,11 @@ export function applyAnthropicCacheMarkers(
702
791
 
703
792
  // The breakpoint goes on the last message that has an eligible block: a
704
793
  // trailing effort control (empty system message) or a thinking-only tail has
705
- // nothing to mark. -1 when no message does.
794
+ // nothing to mark, and a turn-scoped system message rejects cache_control.
795
+ // -1 when no message does.
706
796
  function lastMarkableMessageIndex(messages) {
707
797
  for (let index = messages.length - 1; index >= 0; index--) {
798
+ if (messages[index].role === 'system') continue;
708
799
  if (lastEligibleBlockIndex(messages[index].content) >= 0) return index;
709
800
  }
710
801
  return -1;
@@ -67,10 +67,10 @@ export function sameAccountIdentity(a, b) {
67
67
  return Boolean(left.email && right.email && left.email.toLowerCase() === right.email.toLowerCase());
68
68
  }
69
69
 
70
- // expires_at may arrive as a unix number (seconds or milliseconds) or an
71
- // ISO-8601 string. Normalize to epoch milliseconds; 0 means unknown.
70
+ // expires_at may arrive as a unix number (seconds or milliseconds), a numeric
71
+ // string, or an ISO-8601 string. Normalize to epoch milliseconds; 0 means unknown.
72
72
  export function normalizeExpiresAtMs(value) {
73
- if (typeof value === 'string') {
73
+ if (typeof value === 'string' && !Number.isFinite(Number(value.trim() || NaN))) {
74
74
  const ms = Date.parse(value);
75
75
  return Number.isFinite(ms) ? ms : 0;
76
76
  }
@@ -79,6 +79,20 @@ export function normalizeExpiresAtMs(value) {
79
79
  return n < 1e12 ? n * 1000 : n;
80
80
  }
81
81
 
82
+ /**
83
+ * Whether a stored `{ access_token, refresh_token?, expires_at }` document is a
84
+ * sign-in: it can refresh, or its access token is known to be unexpired. A copy
85
+ * made without a refresh token (an isolated dev instance, so it cannot rotate
86
+ * the original's) coasts on the access token until then; one with no known
87
+ * expiry and no refresh token is incomplete, not a sign-in.
88
+ */
89
+ export function isSignedInTokenDocument(raw) {
90
+ if (!raw?.access_token) return false;
91
+ if (raw.refresh_token) return true;
92
+ const expiresAt = normalizeExpiresAtMs(raw.expires_at ?? raw.expiresAt) || expiryFromAccessToken(raw.access_token);
93
+ return expiresAt > Date.now();
94
+ }
95
+
82
96
  /**
83
97
  * Status record for a stored OAuth credential that has an access token.
84
98
  * `expiresAt` is epoch ms (0 = unknown); `detail` labels the credential source.
@@ -1,9 +1,27 @@
1
1
  // The tail reminder (session/tail-reminder-rules.mjs) rides the outgoing request
2
- // as its last message, marked meta.source 'tail-reminder'. It is ephemeral: a
3
- // provider builder that keeps per-message state (cache breakpoints, message
4
- // counts, continuation indexes, tool-loop closure) lifts it off first with
5
- // splitTailReminder, builds from the real history, and appends the reminder as
6
- // the final wire element, so the history-derived prefix never contains it.
2
+ // as its last message, marked meta.source 'tail-reminder'. A provider builder
3
+ // that keeps per-message state (cache breakpoints, message counts, continuation
4
+ // indexes, tool-loop closure) lifts it off first with splitTailReminder, builds
5
+ // from the real history, and then places it in one of two modes:
6
+ //
7
+ // - Legacy (default, every provider): the reminder is the final wire element
8
+ // (Anthropic: a text block on the last user message) and is never sent again,
9
+ // so the next request's history no longer holds it. That rewrites what came
10
+ // before the assistant turn it produced; Anthropic then drops that turn's
11
+ // thinking (prefix_binding_mismatch).
12
+ // - Turn-scoped (append-only): the request ends with a turn-scoped system
13
+ // message ({role:'system', clear_at:'next_user_message'}), and every later
14
+ // request re-sends it byte-identically right before the assistant turn it
15
+ // produced. send-with-recovery stores the reminder on that assistant message
16
+ // (`turnReminder`, all providers), so the wire only ever grows.
17
+ //
18
+ // Opting in is per provider builder: decide with a capability predicate
19
+ // (Anthropic: anthropicTurnScopedReminders in anthropic-request-utils.mjs —
20
+ // first-party Messages routes on a supported model),
21
+ // re-emit stored `turnReminder`s from the lowered history and append the
22
+ // current one in the same shape, both by one placement rule: a request whose
23
+ // tail cannot take the message gets no reminder, and replay skips that turn. Builders that have not opted in ignore `turnReminder` and keep
24
+ // the legacy layout; a stored reminder never reaches their wire.
7
25
  export const TAIL_REMINDER_META = 'tail-reminder';
8
26
 
9
27
  export function splitTailReminder(messages) {