mixdog 1.3.1 → 1.3.2

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 (377) hide show
  1. package/README.md +8 -4
  2. package/package.json +2 -2
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/src/defaults/shipped-history.json +5 -1
  5. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  6. package/src/defaults/skills/setup/SKILL.md +23 -1
  7. package/src/defaults/skills/setup/references/actions.md +50 -3
  8. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  9. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  10. package/src/defaults/skills/visualize/SKILL.md +9 -6
  11. package/src/defaults/skills/visualize/references/craft.md +8 -1
  12. package/src/help.mjs +1 -1
  13. package/src/lib/keychain-cjs.cjs +4 -1
  14. package/src/lib/rules-builder.cjs +14 -7
  15. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  16. package/src/rules/agent-mode/MIXY.md +6 -8
  17. package/src/rules/lead/LEAD.md +2 -2
  18. package/src/rules/shared/10-tool-workflow.md +4 -0
  19. package/src/rules/shared/20-research.md +2 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  23. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  24. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  25. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  26. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  27. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  28. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  29. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  30. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  31. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  32. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  33. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  34. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  35. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  36. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  39. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  40. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  41. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  45. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  46. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  47. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  48. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  49. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  50. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  51. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  55. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  56. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  57. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  59. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  60. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  61. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  66. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  67. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  69. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -5
  70. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  71. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  73. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  75. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  76. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  77. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +17 -2
  78. package/src/runtime/agent/orchestrator/session/agent-room.mjs +133 -38
  79. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  80. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  82. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  83. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  84. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  85. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  88. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  91. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  92. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +136 -32
  93. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  94. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  95. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  96. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  97. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  98. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  99. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  100. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  101. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  102. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
  103. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  104. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  105. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  108. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  109. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  110. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  113. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  114. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  115. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  116. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  117. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  119. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  120. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  121. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  122. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  123. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +3 -8
  124. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  125. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  126. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  127. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  128. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  129. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  130. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  131. package/src/runtime/agent/orchestrator/session/routine-result.mjs +120 -9
  132. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  134. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  135. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  136. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  137. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  138. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  139. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  140. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  141. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  142. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  143. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  144. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  145. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  146. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  147. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  149. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  150. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  151. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  152. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  154. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  156. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  157. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  158. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  159. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  160. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  161. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  162. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  163. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  164. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  165. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  166. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  167. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  168. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  169. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  170. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  171. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  173. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  174. package/src/runtime/attachments/limits.mjs +5 -1
  175. package/src/runtime/attachments/office-extract.mjs +3 -6
  176. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  177. package/src/runtime/attachments/store.mjs +49 -30
  178. package/src/runtime/browser-bridge/client.mjs +73 -20
  179. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  180. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  181. package/src/runtime/channels/lib/state-file.mjs +15 -15
  182. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  183. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  184. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  185. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  186. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  187. package/src/runtime/effort-judge/judge-client.mjs +5 -2
  188. package/src/runtime/effort-judge/model-install.mjs +16 -8
  189. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  190. package/src/runtime/local-provider/catalog.mjs +5 -2
  191. package/src/runtime/local-provider/registered-models.mjs +64 -27
  192. package/src/runtime/local-provider/server.mjs +2 -3
  193. package/src/runtime/media/adapters/image-api.mjs +89 -0
  194. package/src/runtime/media/auth.mjs +64 -1
  195. package/src/runtime/media/catalog.mjs +66 -4
  196. package/src/runtime/media/jobs.mjs +4 -0
  197. package/src/runtime/media/lanes.mjs +46 -5
  198. package/src/runtime/media/media-usage.mjs +17 -0
  199. package/src/runtime/media/renditions.mjs +15 -15
  200. package/src/runtime/media/store.mjs +36 -35
  201. package/src/runtime/media/tool.mjs +8 -1
  202. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  203. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  204. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  205. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  206. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  207. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  208. package/src/runtime/office/core/journal.mjs +7 -14
  209. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  210. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  211. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  212. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  213. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  214. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  215. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  216. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  217. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  218. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  219. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  220. package/src/runtime/shared/agent-store.mjs +24 -7
  221. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  222. package/src/runtime/shared/atomic-file.d.mts +50 -0
  223. package/src/runtime/shared/atomic-file.mjs +394 -132
  224. package/src/runtime/shared/background-tasks.mjs +7 -4
  225. package/src/runtime/shared/bot-hops.mjs +30 -0
  226. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  227. package/src/runtime/shared/bounded-download.mjs +4 -2
  228. package/src/runtime/shared/config.mjs +39 -9
  229. package/src/runtime/shared/defaults-separation.mjs +6 -2
  230. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  231. package/src/runtime/shared/err-text.mjs +4 -0
  232. package/src/runtime/shared/error-presentation.mjs +22 -4
  233. package/src/runtime/shared/file-has-data.mjs +28 -0
  234. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  235. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  236. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  237. package/src/runtime/shared/projects.mjs +50 -41
  238. package/src/runtime/shared/provider-accounts.mjs +17 -12
  239. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  240. package/src/runtime/shared/routine-trigger.mjs +14 -6
  241. package/src/runtime/shared/run-child.d.mts +3 -0
  242. package/src/runtime/shared/run-child.mjs +24 -3
  243. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  244. package/src/runtime/shared/singleton-owner.mjs +7 -6
  245. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  246. package/src/runtime/shared/staged-update.mjs +4 -2
  247. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  248. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  249. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  250. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  251. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  252. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  253. package/src/runtime/shared/user-cwd.mjs +9 -4
  254. package/src/runtime/shared/user-data-guard.mjs +41 -42
  255. package/src/runtime/shared/utf8-cut.mjs +10 -0
  256. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  257. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  258. package/src/runtime/tidy/core-install.mjs +26 -4
  259. package/src/runtime/tidy/install.mjs +91 -23
  260. package/src/runtime/web-search/lib/config.mjs +4 -16
  261. package/src/session-runtime/agent-home-delivery.mjs +555 -199
  262. package/src/session-runtime/agent-room-delivery.mjs +143 -36
  263. package/src/session-runtime/agent-task-sessions.mjs +667 -165
  264. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  265. package/src/session-runtime/boot/apis.mjs +45 -24
  266. package/src/session-runtime/boot/config.mjs +1 -0
  267. package/src/session-runtime/boot/facade.mjs +11 -1
  268. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  269. package/src/session-runtime/boot/providers.mjs +2 -2
  270. package/src/session-runtime/boot/shared.mjs +12 -6
  271. package/src/session-runtime/boot/tools.mjs +2 -2
  272. package/src/session-runtime/bot-status.mjs +432 -0
  273. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  274. package/src/session-runtime/config-lifecycle.mjs +60 -55
  275. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  276. package/src/session-runtime/held-requests.mjs +36 -33
  277. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  278. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  279. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +19 -4
  280. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  281. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  282. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  283. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  284. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  285. package/src/session-runtime/media-api.mjs +5 -2
  286. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  287. package/src/session-runtime/model-route/set-route.mjs +61 -1
  288. package/src/session-runtime/owned-automation-run.mjs +8 -1
  289. package/src/session-runtime/room-member-gate.mjs +16 -3
  290. package/src/session-runtime/route-resolution.mjs +16 -8
  291. package/src/session-runtime/route-state.mjs +1 -59
  292. package/src/session-runtime/routine-home-turn.mjs +5 -3
  293. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  294. package/src/session-runtime/runtime-paths.mjs +11 -5
  295. package/src/session-runtime/runtime-tunables.mjs +6 -0
  296. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  297. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  298. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  299. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  300. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  301. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  302. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  303. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  304. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  305. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  306. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  307. package/src/session-runtime/services/agent-tool.mjs +6 -0
  308. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  309. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  310. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  311. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  312. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  313. package/src/session-runtime/services/provider-admin.mjs +14 -4
  314. package/src/session-runtime/services/schedule-access.mjs +33 -4
  315. package/src/session-runtime/services/seeds.mjs +4 -3
  316. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  317. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  318. package/src/session-runtime/session-create/materialize.mjs +2 -3
  319. package/src/session-runtime/session-create/session-options.mjs +15 -2
  320. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  321. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  322. package/src/session-runtime/setup-tool/executor.mjs +8 -2
  323. package/src/session-runtime/setup-tool/settings-contract.mjs +31 -1
  324. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  325. package/src/session-runtime/skills-api.mjs +12 -5
  326. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  327. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  328. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  329. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  330. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  331. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  332. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  333. package/src/session-runtime/tool-policy-refresh.mjs +47 -369
  334. package/src/session-runtime/tool-surface.mjs +5 -3
  335. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  336. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  337. package/src/session-runtime/turn/turn-run.mjs +2 -1
  338. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  339. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +519 -333
  340. package/src/session-runtime/workflow-agents-api/agent-route.mjs +46 -46
  341. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  342. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  343. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  344. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  345. package/src/standalone/daemon-agent-control.mjs +4 -1
  346. package/src/standalone/daemon-session-control.mjs +51 -10
  347. package/src/standalone/daemon.mjs +28 -10
  348. package/src/standalone/local-session-runtime.mjs +22 -14
  349. package/src/standalone/session-client.mjs +7 -2
  350. package/src/standalone/session-protocol.mjs +7 -0
  351. package/src/standalone/session-service.mjs +11 -0
  352. package/src/standalone/session-state-projection.mjs +19 -9
  353. package/src/standalone/session-tool-output.mjs +35 -9
  354. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  355. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  356. package/src/tui/app/slash-commands.mjs +1 -1
  357. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  358. package/src/tui/dist/index.mjs +529 -333
  359. package/src/tui/prompt-history-store.mjs +16 -21
  360. package/src/tui/session/context-state.mjs +4 -0
  361. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  362. package/src/tui/session/session-api/media.mjs +2 -1
  363. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  364. package/src/tui/session/session-api/routes-write.mjs +203 -0
  365. package/src/tui/session/session-api/routes.mjs +23 -247
  366. package/src/tui/session/session-api/settings.mjs +14 -1
  367. package/src/tui/session/session-flow/drain.mjs +28 -3
  368. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  369. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  370. package/src/tui/session/session-flow/steering.mjs +8 -3
  371. package/src/tui/session/tool-approval.mjs +185 -20
  372. package/src/tui/session/transcript-intake.mjs +2 -0
  373. package/src/tui/session/turn.mjs +23 -14
  374. package/src/tui/transcript-tool-failures.mjs +8 -2
  375. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  376. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  377. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -2,6 +2,7 @@
2
2
  // or a project may use. Every installed connector is on by default for every
3
3
  // agent and project, existing and future ones; a policy only switches some off:
4
4
  // agents/<id>/agent.json connectors: { off: [serverId…], tools?: { [serverId]: { [tool]: permission } } }
5
+ // (bots/<id>/ for a store bot; Mixy — runtime id `lead` — in her identity folder)
5
6
  // <data>/project-connectors.json { projects: { [projectRoot]: { off: [serverId…] } } }
6
7
  // `tools` keeps per-tool overrides (auto | needs_approval | disabled); a tool
7
8
  // without one is auto, except a catalog connector's high-risk tools, which
@@ -12,11 +13,16 @@
12
13
  // connected tool, so the schema and the BP2 manifest stay shared.
13
14
  import { readFileSync, statSync } from 'node:fs';
14
15
  import { join } from 'node:path';
16
+ import { holdsNoData, parseJsonText, readJsonStateSync } from '../../../shared/atomic-file.mjs';
17
+
18
+ const NO_DATA = new Error('the file holds no data');
15
19
  import { cwdWithinProjects } from '../../../shared/extension-scopes.mjs';
16
20
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
17
- import { userDefinitionDir } from '../../../shared/agent-store.mjs';
21
+ import { mixyIdentityDir, userDefinitionDir } from '../../../shared/agent-store.mjs';
18
22
  import { isSelfMcpToolName, parseMcpToolName } from '../../../shared/tool-primitives.mjs';
19
23
  import { connectorToolDefault } from '../../../shared/connector-catalog.mjs';
24
+ import { MIXY_AGENT_ID } from '../session/agent-home.mjs';
25
+ import { AGENT_MODE, sessionModeOf } from '../session/agent-mode.mjs';
20
26
  import { serverConnector } from './server-connectors.mjs';
21
27
 
22
28
  export const CONNECTOR_PERMISSIONS = Object.freeze(['auto', 'needs_approval', 'disabled']);
@@ -127,9 +133,25 @@ function readJson(path, parse) {
127
133
  return error?.code === 'ENOENT' || error?.code === 'ENOTDIR' ? null : unreadable(path, error);
128
134
  }
129
135
  try {
130
- return parse(JSON.parse(text));
136
+ if (holdsNoData(text)) throw NO_DATA;
137
+ return parse(parseJsonText(text));
131
138
  } catch (error) {
132
- return unreadable(path, error);
139
+ return recoverDamaged(path, parse, error);
140
+ }
141
+ }
142
+
143
+ // A damaged policy (empty, NUL-filled, torn, invalid) must never fail open.
144
+ // With a valid last good copy `<file>.bak` the damaged file is kept aside and
145
+ // the copy restored, so the user's deny list survives. Without one the file
146
+ // stays where it is (`keepWithoutBackup`) and every connector stays off until
147
+ // the policy is saved again — moving it aside would make the next read "no
148
+ // policy" (all on). A busy recovery lock also answers deny-all.
149
+ function recoverDamaged(path, parse, error) {
150
+ try {
151
+ const value = readJsonStateSync(path, { validate: parse, fallback: DENY_ALL_CONNECTORS, keepWithoutBackup: true });
152
+ return value === DENY_ALL_CONNECTORS ? unreadable(path, error) : value;
153
+ } catch (recoveryError) {
154
+ return unreadable(path, recoveryError);
133
155
  }
134
156
  }
135
157
 
@@ -146,11 +168,22 @@ export function readConnectorsFile(path) {
146
168
  return readJson(path, (manifest) => parseConnectorPolicy(manifest?.connectors));
147
169
  }
148
170
 
149
- /** The agent's saved policy under `dataDir`, or null when it has none. */
171
+ /** The agent's saved policy under `dataDir`, or null when it has none. Mixy's
172
+ * runtime id is `lead`: her policy is in her identity folder (bots/mixy once
173
+ * the bot store is active, agents/lead before), the file her profile writes. */
150
174
  export function agentConnectors(agentId, dataDir = resolvePluginData()) {
151
175
  const id = clean(agentId);
152
176
  if (!id || !AGENT_DIR_ID.test(id)) return null;
153
- return cachedRead(join(userDefinitionDir(dataDir, id), 'agent.json'), readConnectorsFile);
177
+ const dir = id === MIXY_AGENT_ID ? mixyIdentityDir(dataDir) : userDefinitionDir(dataDir, id);
178
+ return cachedRead(join(dir, 'agent.json'), readConnectorsFile);
179
+ }
180
+
181
+ /** The agent whose connector policy `session` runs under. A coding Lead shares
182
+ * Mixy's runtime id `lead` but is not her: the policy her profile edits is
183
+ * hers alone, so a coding Lead has none ('') and only its Project's applies. */
184
+ export function connectorPolicyAgentOf(session) {
185
+ const agent = clean(session?.agent);
186
+ return agent === MIXY_AGENT_ID && sessionModeOf(session) !== AGENT_MODE ? '' : agent;
154
187
  }
155
188
 
156
189
  /** Uncached: the project policy file → `[{ root, off }]`, null without one,
@@ -186,6 +219,65 @@ export function connectorToolPermission(policy, toolOrName) {
186
219
  return policy?.tools?.[mcp.server]?.[mcp.tool] || connectorToolDefault(serverConnector(mcp.server), mcp.tool);
187
220
  }
188
221
 
222
+ const PERMISSION_RANK = Object.freeze({ disabled: 0, needs_approval: 1, auto: 2 });
223
+ export const NO_WIDEN_MESSAGE = 'This change would widen what the bot may do without asking; make it on the desktop.';
224
+
225
+ /** Whether replacing the stored policy `before` (a stored value, a parsed
226
+ * policy or null) with `after` (`{ off, tools }` or null) could give any
227
+ * tool more authority, judged on EFFECTIVE permissions: a connector switched
228
+ * back on, a tool allowed, or an override removed (the tool then runs on its
229
+ * default, which is `needs_approval` for a catalog connector's high-risk
230
+ * tools and `auto` otherwise). A legacy allow list or an unreadable policy is
231
+ * not compared: it counts as widening. */
232
+ export function connectorPolicyWidens(before, after) {
233
+ let old;
234
+ try {
235
+ old = before == null ? { off: [], tools: {} } : parseConnectorPolicy(before);
236
+ } catch {
237
+ return true;
238
+ }
239
+ if (old.allow) return true;
240
+ const next = after == null ? { off: [], tools: {} } : after;
241
+ const offBefore = new Set(old.off);
242
+ const offAfter = new Set(next.off || []);
243
+ for (const server of offBefore) if (!offAfter.has(server)) return true;
244
+ const toolsBefore = old.tools || {};
245
+ const toolsAfter = next.tools || {};
246
+ for (const server of new Set([...Object.keys(toolsBefore), ...Object.keys(toolsAfter)])) {
247
+ const tools = new Set([...Object.keys(toolsBefore[server] || {}), ...Object.keys(toolsAfter[server] || {})]);
248
+ for (const tool of tools) {
249
+ // No override: at least asks / at most runs freely.
250
+ const was = offBefore.has(server) ? 0 : (PERMISSION_RANK[toolsBefore[server]?.[tool]] ?? 1);
251
+ const now = offAfter.has(server) ? 0 : (PERMISSION_RANK[toolsAfter[server]?.[tool]] ?? 2);
252
+ if (now > was) return true;
253
+ }
254
+ }
255
+ return false;
256
+ }
257
+
258
+ /** Throws NO_WIDEN_MESSAGE when `after` could widen `before` (see connectorPolicyWidens). */
259
+ export function assertConnectorPolicyNotWidened(before, after) {
260
+ if (connectorPolicyWidens(before, after)) throw new Error(NO_WIDEN_MESSAGE);
261
+ }
262
+
263
+ let toolPermissionWriter = null;
264
+ /** The host attaches the one writer of an agent's per-tool permission (the
265
+ * agent editor keeps every other agent.json key). */
266
+ export function setConnectorToolPermissionWriter(write) {
267
+ toolPermissionWriter = typeof write === 'function' ? write : null;
268
+ }
269
+
270
+ /** Save `permission` for the connector tool `toolOrName` under `agentId`'s own
271
+ * policy. False when it is no connector tool or no writer is attached. */
272
+ export async function writeAgentMcpToolPermission(agentId, toolOrName, permission) {
273
+ const mcp = parseMcpToolName(toolOrName);
274
+ if (!toolPermissionWriter || !mcp || isSelfMcpToolName(toolOrName) || !CONNECTOR_PERMISSIONS.includes(permission)) {
275
+ return false;
276
+ }
277
+ await toolPermissionWriter(agentId, mcp.server, mcp.tool, permission);
278
+ return true;
279
+ }
280
+
189
281
  /** The permission `agentId` has for the MCP tool `toolOrName`. */
190
282
  export function agentMcpToolPermission(agentId, toolOrName) {
191
283
  if (!parseMcpToolName(toolOrName)) return 'auto';
@@ -117,14 +117,11 @@ async function enumerate({ strict = false } = {}) {
117
117
  * names and (POSIX) process groups. Rejects instead of returning a partial
118
118
  * table, so callers can tell "nothing found" from "could not look".
119
119
  *
120
- * `shared: true` (pre-kill captures) joins a read already in flight, so many
121
- * jobs promoted together do not each spawn an enumerator; a capture taken
122
- * while its root runs is valid whenever that read began. An observation after
123
- * a kill or exit must see a table taken AFTER it asked: a read that began
124
- * before the kill would report the killed tree alive and defer the
125
- * confirmation by a whole re-check. Such a caller starts a read when none
126
- * runs, else joins the one queued to begin when the running read settles —
127
- * every observer asking meanwhile shares it. At most one enumerator runs at
120
+ * Every caller sees a table from a read begun after it asked: an earlier
121
+ * read may miss newly spawned descendants before a kill, or report a killed
122
+ * tree alive afterwards. A caller starts a read when none runs, else joins
123
+ * the one queued to begin when the running read settles — every caller
124
+ * asking meanwhile shares it. At most one enumerator runs at
128
125
  * a time, however many shells settle or watch at once (each one is a
129
126
  * PowerShell process on Windows; one per settle and watch pass compounded
130
127
  * machine load into multi-second daemon stalls). */
@@ -137,8 +134,7 @@ function startTableRead() {
137
134
  tableInFlight = read;
138
135
  return read;
139
136
  }
140
- export function readProcessTable({ shared = false } = {}) {
141
- if (shared && tableInFlight) return tableInFlight;
137
+ export function readProcessTable() {
142
138
  if (nextTable) return nextTable;
143
139
  if (!tableInFlight) return startTableRead();
144
140
  const settled = () => {};
@@ -1,5 +1,6 @@
1
1
  import { setMaxListeners } from 'node:events';
2
- import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { writeFileAtomicSync } from '../../../shared/atomic-file.mjs';
3
4
  import { writeFile } from 'node:fs/promises';
4
5
  import {
5
6
  MAX_ATTACHMENT_BLOB_BYTES,
@@ -772,9 +773,11 @@ function spillMcpBinary(buffer, extension) {
772
773
  const path = join(dir, `mcp-${hash}.${extension}`);
773
774
  try {
774
775
  mkdirSync(dir, { recursive: true });
775
- writeFileSync(path, buffer, { flag: 'wx' });
776
- } catch (err) {
777
- if (err?.code !== 'EEXIST') return null;
776
+ // Named by its content hash: whole-file create-if-absent (a crash never
777
+ // leaves a torn file under the name that would be trusted as the content).
778
+ writeFileAtomicSync(path, buffer, { createOnly: true });
779
+ } catch {
780
+ return null;
778
781
  }
779
782
  return path;
780
783
  }
@@ -10,6 +10,7 @@
10
10
  * anthropic-turn-result.mjs); nothing here touches the socket.
11
11
  */
12
12
  import { prepareAnthropicImages } from './lib/anthropic-image-input.mjs';
13
+ import { anthropicPrefixFingerprint, recordAnthropicPrefix } from './lib/anthropic-prefix-trace.mjs';
13
14
  import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
14
15
  import { applyAnthropicServerFallback } from './anthropic-server-fallback.mjs';
15
16
  import { fastModeAvailable } from './anthropic-fast-mode.mjs';
@@ -17,6 +18,7 @@ import { applyAnthropicEffortToBody, shouldIncludeEffortBeta } from './anthropic
17
18
  import { isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
18
19
  import {
19
20
  anthropicThinkingActive,
21
+ appendAnthropicTailReminder,
20
22
  applyAnthropicCacheMarkers,
21
23
  clampAnthropicThinkingBudget as clampThinkingBudgetTokens,
22
24
  resolveAnthropicCacheTtls as resolveCacheTtls,
@@ -24,6 +26,7 @@ import {
24
26
  sharedCachePrefixBoundary,
25
27
  toAnthropicToolChoice,
26
28
  } from './lib/anthropic-request-utils.mjs';
29
+ import { splitTailReminder } from './lib/tail-reminder-split.mjs';
27
30
  import {
28
31
  buildSystemBlocks,
29
32
  resolveMaxTokens,
@@ -50,7 +53,7 @@ import {
50
53
  export async function buildAnthropicApiRequest({
51
54
  name,
52
55
  config,
53
- messages,
56
+ messages: requestMessages,
54
57
  useModel,
55
58
  tools,
56
59
  opts,
@@ -67,6 +70,7 @@ export async function buildAnthropicApiRequest({
67
70
  // the global by-model-id capability index and the Claude-id ladders are
68
71
  // first-party knowledge and must not leak onto (or from) a custom endpoint.
69
72
  const isCustom = isCustomProviderName(name);
73
+ const { messages, reminder } = splitTailReminder(requestMessages);
70
74
  const maxTokens = resolveMaxTokens(capabilityModel, { modelMaxOutputTokens });
71
75
  const effortOptions = { ...config, ...opts, disableBetaHeaders: config?.disableBetaHeaders };
72
76
  const gatewayEffort = typeof config?.messagesEffort === 'function' ? config.messagesEffort(opts) : null;
@@ -108,7 +112,9 @@ export async function buildAnthropicApiRequest({
108
112
  model: useModel,
109
113
  max_tokens: maxTokens,
110
114
  system: systemBlocks.length ? systemBlocks : undefined,
111
- messages: await prepareAnthropicImages(anthropicMessages, { signal: opts.signal }),
115
+ messages: await prepareAnthropicImages(appendAnthropicTailReminder(anthropicMessages, reminder), {
116
+ signal: opts.signal,
117
+ }),
112
118
  };
113
119
  // Server-side fallback and fast mode are decided from Claude model ids:
114
120
  // first-party knowledge, never applied to a custom endpoint.
@@ -135,6 +141,55 @@ export async function buildAnthropicApiRequest({
135
141
  .map((t) => (t && typeof t.name === 'string' ? t.name : null))
136
142
  .filter(Boolean)
137
143
  );
144
+ applyRequestReasoning(params, {
145
+ isCustom,
146
+ name,
147
+ config,
148
+ opts,
149
+ modelInfo,
150
+ capabilityModel,
151
+ maxTokens,
152
+ gatewayEffort,
153
+ effortProjection,
154
+ });
155
+ if (params.tools) resolveRequestToolChoice(params, opts, config);
156
+ // Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
157
+ // Suppressed while the fast capacity pool is cooling down.
158
+ let fastLatched = fastModeLatched;
159
+ if (!isCustom && opts.fast === true && supportsAnthropicFastMode(useModel) && fastModeAvailable()) {
160
+ params.speed = 'fast';
161
+ fastLatched = true;
162
+ }
163
+ // NOTE: do NOT sanitize here. params.messages was already sanitized
164
+ // once inside toAnthropicMessages and then had cache markers applied.
165
+ // Re-sanitizing after marking could drop/reorder a marked block and
166
+ // move the provider-visible cache breakpoint off the cached one — the
167
+ // exact COLD-turn bug this change fixes. Order: build → sanitize
168
+ // (once) → mark → prepare image bytes → send.
169
+ params.stream = true;
170
+ // Diagnostic: per-block prefix hashes of exactly what is sent.
171
+ recordAnthropicPrefix(opts.sessionId || opts.session?.id || null, anthropicPrefixFingerprint(params));
172
+
173
+ const requestHeaders = requestHeadersFor(params, {
174
+ config,
175
+ opts,
176
+ isCustom,
177
+ fastLatched,
178
+ hasDeferredTools,
179
+ capabilityModel,
180
+ effortProjection,
181
+ });
182
+
183
+ return { params, requestHeaders, knownToolNames, fastModeLatched: fastLatched };
184
+ }
185
+
186
+ /** The turn's thinking/effort fields on `params` (in place): a custom
187
+ * provider's from its own model row, else the turn effort (or a gateway's
188
+ * own `messagesEffort`; 'none' sends no thinking at all). */
189
+ function applyRequestReasoning(
190
+ params,
191
+ { isCustom, name, config, opts, modelInfo, capabilityModel, maxTokens, gatewayEffort, effortProjection }
192
+ ) {
138
193
  if (isCustom) {
139
194
  const { fields } = resolveCustomReasoning({
140
195
  protocol: 'anthropic',
@@ -156,32 +211,29 @@ export async function buildAnthropicApiRequest({
156
211
  progressUpdates: config?.disableBetaHeaders !== true,
157
212
  });
158
213
  }
159
- if (params.tools) {
160
- const toolChoice = toAnthropicToolChoice(opts.toolChoice, {
161
- thinking: anthropicThinkingActive(params),
162
- disableParallelToolUse: opts.disableParallelToolUse === true,
163
- // A gateway that cannot honour disable_parallel_tool_use declares so.
164
- supportsDisableParallelToolUse: config?.supportsDisableParallelToolUse !== false,
165
- onThinkingConflict: opts.toolChoiceThinkingConflict,
166
- });
167
- if (toolChoice) params.tool_choice = toolChoice;
168
- else delete params.tool_choice;
169
- }
170
- // Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
171
- // Suppressed while the fast capacity pool is cooling down.
172
- let fastLatched = fastModeLatched;
173
- if (!isCustom && opts.fast === true && supportsAnthropicFastMode(useModel) && fastModeAvailable()) {
174
- params.speed = 'fast';
175
- fastLatched = true;
176
- }
177
- // NOTE: do NOT sanitize here. params.messages was already sanitized
178
- // once inside toAnthropicMessages and then had cache markers applied.
179
- // Re-sanitizing after marking could drop/reorder a marked block and
180
- // move the provider-visible cache breakpoint off the cached one — the
181
- // exact COLD-turn bug this change fixes. Order: build → sanitize
182
- // (once) → mark → prepare image bytes → send.
183
- params.stream = true;
214
+ }
184
215
 
216
+ /** The reserved `tool_choice` of a request with tools, resolved once
217
+ * thinking is known (forced any/tool is invalid while thinking is on);
218
+ * dropped when none applies. */
219
+ function resolveRequestToolChoice(params, opts, config) {
220
+ const toolChoice = toAnthropicToolChoice(opts.toolChoice, {
221
+ thinking: anthropicThinkingActive(params),
222
+ disableParallelToolUse: opts.disableParallelToolUse === true,
223
+ // A gateway that cannot honour disable_parallel_tool_use declares so.
224
+ supportsDisableParallelToolUse: config?.supportsDisableParallelToolUse !== false,
225
+ onThinkingConflict: opts.toolChoiceThinkingConflict,
226
+ });
227
+ if (toolChoice) params.tool_choice = toolChoice;
228
+ else delete params.tool_choice;
229
+ }
230
+
231
+ /** The per-request headers of the final body `params`: the anthropic-beta
232
+ * set it needs, then the caller's `opts.requestHeaders`; null when none. */
233
+ function requestHeadersFor(
234
+ params,
235
+ { config, opts, isCustom, fastLatched, hasDeferredTools, capabilityModel, effortProjection }
236
+ ) {
185
237
  // Per-call headers override the client defaultHeaders, so the
186
238
  // constructor-level disableBetaHeaders opt-out must be honoured here
187
239
  // too — otherwise opencode-go's anthropic-compatible routing
@@ -205,8 +257,5 @@ export async function buildAnthropicApiRequest({
205
257
  ].filter(Boolean);
206
258
  if (betas.length) betaHeaders = { 'anthropic-beta': betas.join(',') };
207
259
  }
208
- const requestHeaders =
209
- betaHeaders || opts.requestHeaders ? { ...(betaHeaders || {}), ...(opts.requestHeaders || {}) } : null;
210
-
211
- return { params, requestHeaders, knownToolNames, fastModeLatched: fastLatched };
260
+ return betaHeaders || opts.requestHeaders ? { ...(betaHeaders || {}), ...(opts.requestHeaders || {}) } : null;
212
261
  }
@@ -149,7 +149,7 @@ function _markLatestByFamily(models) {
149
149
  for (const m of byFamily.values()) m.latest = true;
150
150
  }
151
151
 
152
- function _compareVersion(a, b) {
152
+ export function _compareVersion(a, b) {
153
153
  const na = (a.match(/^claude-[a-z]+-(\d+)(?:-(\d+))?/i) || []).slice(1).map(Number);
154
154
  const nb = (b.match(/^claude-[a-z]+-(\d+)(?:-(\d+))?/i) || []).slice(1).map(Number);
155
155
  for (let i = 0; i < Math.max(na.length, nb.length); i++) {
@@ -10,7 +10,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
10
10
  // source release just to advance the user-agent version.
11
11
  // Claude Opus 5.5 requires 2.1.280 or newer. The release workflow raises this
12
12
  // to the published CLI version (scripts/sync-client-version-floors.mjs).
13
- export const DEFAULT_CLI_VERSION = '2.1.304';
13
+ export const DEFAULT_CLI_VERSION = '2.1.307';
14
14
  export const CLAUDE_CLI_NPM_PACKAGE = '@anthropic-ai/claude-code';
15
15
 
16
16
  const CACHE_SCHEMA_VERSION = 1;
@@ -6,6 +6,11 @@
6
6
  * from anthropic.mjs. agent-trace instrumented.
7
7
  */
8
8
  import { traceAgentSse, traceAgentUsage } from '../agent-trace.mjs';
9
+ import {
10
+ anthropicPrefixFingerprint,
11
+ latestAnthropicPrefix,
12
+ recordAnthropicPrefix,
13
+ } from './lib/anthropic-prefix-trace.mjs';
9
14
  import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
10
15
  import { resolveAnthropicMaxTokens } from './anthropic-max-tokens.mjs';
11
16
  import { MODELS } from './lib/anthropic-models.mjs';
@@ -56,6 +61,7 @@ import { ANTHROPIC_EFFORT_LEVELS, withEffortRejectionFallback } from './effort-r
56
61
  import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
57
62
  import {
58
63
  anthropicThinkingActive,
64
+ appendAnthropicTailReminder,
59
65
  applyAnthropicCacheMarkers,
60
66
  clampAnthropicThinkingBudget as clampThinkingBudgetTokens,
61
67
  deferredAnthropicTools as sharedDeferredAnthropicTools,
@@ -67,6 +73,7 @@ import {
67
73
  toAnthropicMessages,
68
74
  toAnthropicToolChoice,
69
75
  } from './lib/anthropic-request-utils.mjs';
76
+ import { splitTailReminder } from './lib/tail-reminder-split.mjs';
70
77
  import {
71
78
  EFFORT_CONFIGURATION_BETA,
72
79
  isEffortConfigurationRejection,
@@ -189,7 +196,8 @@ function requestAnthropicTools(tools, messages, opts) {
189
196
  // the tier3 1h cache_control to that block; BP1/BP2 take the system TTL. No
190
197
  // `<system-reminder>` user message / sentinel scan is involved anymore.
191
198
 
192
- function buildRequestBody(messages, model, tools, sendOpts) {
199
+ function buildRequestBody(requestMessages, model, tools, sendOpts) {
200
+ const { messages, reminder } = splitTailReminder(requestMessages);
193
201
  const systemMsgs = messages.filter((m) => m.role === 'system');
194
202
  const chatMsgs = messages.filter((m) => m.role !== 'system');
195
203
  const opts = sendOpts || {};
@@ -229,7 +237,7 @@ function buildRequestBody(messages, model, tools, sendOpts) {
229
237
  const body = {
230
238
  model,
231
239
  max_tokens: maxTokens,
232
- messages: anthropicMessages,
240
+ messages: appendAnthropicTailReminder(anthropicMessages, reminder),
233
241
  stream: true,
234
242
  };
235
243
  applyAnthropicServerFallback(body, model, {
@@ -463,6 +471,8 @@ export class AnthropicOAuthProvider {
463
471
  const useModel = model || (await ensureLatestAnthropicModel(this));
464
472
  const body = buildRequestBody(messages, useModel, tools, sendOpts);
465
473
  body.messages = await prepareAnthropicImages(body.messages, { signal: externalSignal });
474
+ // Diagnostic: per-block prefix hashes of exactly what is sent.
475
+ recordAnthropicPrefix(opts.sessionId || null, anthropicPrefixFingerprint(body));
466
476
  if (body.speed === 'fast') {
467
477
  this.fastModeBetaHeaderLatched = true;
468
478
  }
@@ -544,42 +554,15 @@ export class AnthropicOAuthProvider {
544
554
  let maxTokensAdjusted = false;
545
555
  try {
546
556
  for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
547
- let response, controller, cancelHandler;
548
- ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
549
-
550
- // Refresh on 401, and only Anthropic's exact official revoked-token
551
- // 403 signature. Ordinary permission/policy 403s are terminal.
552
- let rejectedAuthBody = null;
553
- if (response.status === 401 || response.status === 403) {
554
- rejectedAuthBody = await response.text().catch(() => '');
555
- }
556
- if (isRefreshableAuthRejection(response.status, rejectedAuthBody)) {
557
- process.stderr.write(`[anthropic-oauth] ${response.status} — forcing refresh and retrying once\n`);
558
- cleanupCancelHandler(cancelHandler);
559
- // Body was drained above; abort closes any remaining transport
560
- // resources before the credential-refresh replay.
561
- try {
562
- controller?.abort?.();
563
- } catch {}
564
- auth.creds = await this.ensureAuth({ forceRefresh: true, reason: String(response.status) });
565
- ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
566
- rejectedAuthBody = null;
567
- }
568
-
569
- // "input length and max_tokens exceed context limit": shrink max_tokens
570
- // to what the context has room for and replay once.
571
- if (response.status === 400 && !maxTokensAdjusted) {
572
- rejectedAuthBody = await response.text().catch(() => '');
573
- if (adjustMaxTokensForContextOverflow(body, 400, rejectedAuthBody)) {
574
- maxTokensAdjusted = true;
575
- cleanupCancelHandler(cancelHandler);
576
- try {
577
- controller?.abort?.();
578
- } catch {}
579
- ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
580
- rejectedAuthBody = null;
581
- }
582
- }
557
+ const opened = await this._openResponse({
558
+ requestWithRetry,
559
+ cleanupCancelHandler,
560
+ auth,
561
+ body,
562
+ maxTokensAdjusted,
563
+ });
564
+ const { response, controller, cancelHandler, rejectedAuthBody } = opened;
565
+ maxTokensAdjusted = opened.maxTokensAdjusted;
583
566
  if (!response.ok) {
584
567
  cleanupCancelHandler(cancelHandler);
585
568
  return await this._resendAfterRejection({
@@ -657,6 +640,51 @@ export class AnthropicOAuthProvider {
657
640
  }
658
641
  }
659
642
 
643
+ // One attempt's initial response: a 401 (or Anthropic's exact revoked-token
644
+ // 403) refreshes the credentials in `auth` and replays once; a context
645
+ // overflow 400 shrinks `body.max_tokens` and replays once per send
646
+ // (`maxTokensAdjusted`). `rejectedAuthBody`: the drained text of a final
647
+ // rejected response, null when none was read.
648
+ async _openResponse({ requestWithRetry, cleanupCancelHandler, auth, body, maxTokensAdjusted }) {
649
+ let response, controller, cancelHandler;
650
+ ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
651
+
652
+ // Refresh on 401, and only Anthropic's exact official revoked-token
653
+ // 403 signature. Ordinary permission/policy 403s are terminal.
654
+ let rejectedAuthBody = null;
655
+ if (response.status === 401 || response.status === 403) {
656
+ rejectedAuthBody = await response.text().catch(() => '');
657
+ }
658
+ if (isRefreshableAuthRejection(response.status, rejectedAuthBody)) {
659
+ process.stderr.write(`[anthropic-oauth] ${response.status} — forcing refresh and retrying once\n`);
660
+ cleanupCancelHandler(cancelHandler);
661
+ // Body was drained above; abort closes any remaining transport
662
+ // resources before the credential-refresh replay.
663
+ try {
664
+ controller?.abort?.();
665
+ } catch {}
666
+ auth.creds = await this.ensureAuth({ forceRefresh: true, reason: String(response.status) });
667
+ ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
668
+ rejectedAuthBody = null;
669
+ }
670
+
671
+ // "input length and max_tokens exceed context limit": shrink max_tokens
672
+ // to what the context has room for and replay once.
673
+ if (response.status === 400 && !maxTokensAdjusted) {
674
+ rejectedAuthBody = await response.text().catch(() => '');
675
+ if (adjustMaxTokensForContextOverflow(body, 400, rejectedAuthBody)) {
676
+ maxTokensAdjusted = true;
677
+ cleanupCancelHandler(cancelHandler);
678
+ try {
679
+ controller?.abort?.();
680
+ } catch {}
681
+ ({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
682
+ rejectedAuthBody = null;
683
+ }
684
+ }
685
+ return { response, controller, cancelHandler, rejectedAuthBody, maxTokensAdjusted };
686
+ }
687
+
660
688
  // A non-OK initial response: quota errors surface as-is; a CLI-version
661
689
  // floor or an unknown/retired model replays the turn ONCE through send().
662
690
  async _resendAfterRejection({ response, rejectedAuthBody, messages, model, useModel, tools, opts, onStageChange }) {
@@ -780,6 +808,7 @@ export class AnthropicOAuthProvider {
780
808
  rawUsage: result.usage?.raw || null,
781
809
  provider: 'anthropic-oauth',
782
810
  requestKind,
811
+ prefixBlocks: latestAnthropicPrefix(sessionId),
783
812
  });
784
813
 
785
814
  // Phase I: if the live response surfaced a model id we don't know
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { traceAgentUsage } from '../agent-trace.mjs';
13
13
  import { reportedCostUsd } from './lib/usage-primitives.mjs';
14
+ import { latestAnthropicPrefix } from './lib/anthropic-prefix-trace.mjs';
14
15
 
15
16
  /**
16
17
  * @param {object} parseResult the assembled turn (streaming or normalized non-streaming)
@@ -47,6 +48,7 @@ export function buildAnthropicTurnResult(parseResult, { provider, useModel, opts
47
48
  // Anthropic usage is additive at this inner transport
48
49
  // boundary, even when OpenCode Go supplies the provider id.
49
50
  inputTokensInclusive: false,
51
+ prefixBlocks: latestAnthropicPrefix(opts.sessionId || opts.session?.id || null),
50
52
  });
51
53
  }
52
54
 
@@ -3,6 +3,7 @@ import { createProviderReplay, providerReplayItems } from './lib/provider-replay
3
3
  import { toGeminiContents, toGeminiRequestTools, toGeminiToolConfig } from './gemini-schema.mjs';
4
4
  import { geminiThinkingConfig } from './gemini-thinking.mjs';
5
5
  import { isGeminiFamilyModel } from './gemini-family.mjs';
6
+ import { splitTailReminder } from './lib/tail-reminder-split.mjs';
6
7
 
7
8
  const THOUGHT_SIGNATURE_SENTINEL = 'skip_thought_signature_validator';
8
9
  const CLAUDE_MAX_OUTPUT_TOKENS = 64000;
@@ -219,7 +220,10 @@ function antigravityGeminiThinkingConfig(model, opts) {
219
220
  });
220
221
  }
221
222
 
222
- export function buildAntigravityRequest(messages, model, tools, opts = {}, projectId) {
223
+ export function buildAntigravityRequest(requestMessages, model, tools, opts = {}, projectId) {
224
+ // The tail reminder is not history: the tool-loop closure below must see the
225
+ // real tail, so the reminder is appended to the wire contents last.
226
+ const { messages, reminder } = splitTailReminder(requestMessages);
223
227
  const systemText = messages
224
228
  .filter((message) => message.role === 'system')
225
229
  .map((message) => message.content)
@@ -234,7 +238,7 @@ export function buildAntigravityRequest(messages, model, tools, opts = {}, proje
234
238
  ...(claude ? { maxOutputTokens: CLAUDE_MAX_OUTPUT_TOKENS } : {}),
235
239
  ...(thinkingConfig ? { thinkingConfig } : {}),
236
240
  };
237
- const request = { contents };
241
+ const request = { contents: reminder ? [...contents, { role: 'user', parts: [{ text: reminder }] }] : contents };
238
242
  if (systemText) request.systemInstruction = { role: 'user', parts: [{ text: systemText }] };
239
243
  const requestTools = claude
240
244
  ? claudeRequestTools(tools, opts.nativeTools, opts.onToolExcluded)
@@ -3,24 +3,43 @@
3
3
  // Every turn starts again from the base, so a run of hard turns never
4
4
  // ratchets the effort upward.
5
5
  //
6
- // The base is `medium` on every model: raised only when the request needs it,
7
- // lowered when it is easy. The ladder is the model's supported efforts from
6
+ // The base is the global "Auto reasoning base" setting (`autoEffortBase` in the
7
+ // agent config; low / medium / high, default `medium`), the same on every model
8
+ // and independent of the effort last chosen: raised only when the request needs
9
+ // it, lowered when it is easy. The ladder is the model's supported efforts from
8
10
  // `low` up to the tier below the top: `none`/`minimal` switch thinking off and
9
- // `max`/`ultra` are reserved for an explicit user choice, so auto never picks
10
- // them. A chosen `max`/`ultra` is left untouched.
11
+ // `max`/`ultra` are reserved for an explicit (fixed, Auto off) user choice, so
12
+ // auto never picks them. A base the model does not offer is clamped to the
13
+ // nearest ladder level.
11
14
 
12
15
  const EFFORT_ORDER = Object.freeze(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
13
16
  const FLOOR_INDEX = EFFORT_ORDER.indexOf('low');
14
17
  const RESERVED_TOP = new Set(['max', 'ultra']);
15
18
 
16
19
  export const AUTO_EFFORT_BASE = 'medium';
20
+ /** The "Auto reasoning base" choices: the levels Auto can work around. */
21
+ export const AUTO_EFFORT_BASES = Object.freeze(['low', 'medium', 'high']);
17
22
 
18
- /** The effort auto moves from: `medium`, unless the user chose `max`/`ultra`. */
19
- export function autoEffortBase(chosen) {
20
- const effort = String(chosen ?? '')
23
+ /** The effort auto moves from: the global "Auto reasoning base" setting
24
+ * (`autoEffortBase` in the agent config); absent or invalid is `medium`. */
25
+ export function autoEffortBase(setting) {
26
+ const effort = String(setting ?? '')
21
27
  .trim()
22
28
  .toLowerCase();
23
- return RESERVED_TOP.has(effort) ? effort : AUTO_EFFORT_BASE;
29
+ return AUTO_EFFORT_BASES.includes(effort) ? effort : AUTO_EFFORT_BASE;
30
+ }
31
+
32
+ /** The base on the model's ladder: the setting itself, else the nearest
33
+ * ladder level (the lower one on a tie); null when the ladder is empty. */
34
+ export function clampAutoEffortBase({ base, options }) {
35
+ const ladder = autoEffortLadder(options);
36
+ if (!ladder.length) return null;
37
+ const want = EFFORT_ORDER.indexOf(autoEffortBase(base));
38
+ let best = ladder[0];
39
+ for (const level of ladder) {
40
+ if (Math.abs(EFFORT_ORDER.indexOf(level) - want) < Math.abs(EFFORT_ORDER.indexOf(best) - want)) best = level;
41
+ }
42
+ return best;
24
43
  }
25
44
 
26
45
  export const AUTO_EFFORT_MODES = Object.freeze(['off', 'observe', 'on']);