mixdog 1.4.0 → 1.4.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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
@@ -1,6 +1,6 @@
1
1
  // cache-warm.mjs — keeps a session's prompt cache readable for its next turn
2
2
  // — every session, Lead, bot or sub-agent, while warming is on
3
- // (`promptCacheWarming`, default on) — one interval at a time while the
3
+ // (`promptCacheWarming`, default off) — one interval at a time while the
4
4
  // session idles, as far as the session's own history of returns says it
5
5
  // pays. A warm re-sends the session's last real request
6
6
  // (side-question-store.mjs) through the provider's cache-warm mode
@@ -36,7 +36,11 @@
36
36
  // warm counts as cache use for the idle auto-clear (sessionActivityTimestamp
37
37
  // in idle-auto-clear-gate.mjs), so its deadline moves with each one.
38
38
  // Any fact the rule needs that is not known (written tier, prices, prompt
39
- // size, the account holding the entry) means no warm. The chain stops on a
39
+ // size, the account holding the entry) means no warm. A warm never goes into
40
+ // an entry past its lifetime (counted from the last confirmed read or write,
41
+ // checked at the refresh point and again right before dispatch), and a
42
+ // session whose warm missed inside the lifetime gets no further chain until
43
+ // its cached prefix is dropped. The chain stops on a
40
44
  // miss, a failure, a late timer, a turn in flight, a new real request
41
45
  // (side-question-store.mjs cancels), a compaction, a clear, a close, or a
42
46
  // delete; the auto-clear then runs from the last real or successful warm
@@ -54,6 +58,7 @@
54
58
  // /btw side questions, memory recap, prewarms, warms). The planner counts
55
59
  // idle time from the same anchor. Each session learns from its own gaps. A gap still open at the idle window is recorded as
56
60
  // censored there.
61
+ import { loadConfig } from '../config.mjs';
57
62
  import { ACCOUNT_PROVIDERS } from '../../../shared/provider-accounts.mjs';
58
63
  import { setCacheWarmUseSource } from '../../../shared/idle-auto-clear-gate.mjs';
59
64
  import {
@@ -97,9 +102,9 @@ export const IDLE_RETURN_LOOKBACK_MS = 7 * 24 * 60 * 60 * 1000;
97
102
  const WILSON_Z_95 = 1.959963984540054;
98
103
 
99
104
  /** Whether prompt-cache warming is on (config `promptCacheWarming`, default
100
- * on; config.mjs migrates the earlier target list). */
105
+ * off; config.mjs migrates the earlier target list). */
101
106
  export function cacheWarmingEnabled(config) {
102
- return config?.promptCacheWarming !== false;
107
+ return config?.promptCacheWarming === true;
103
108
  }
104
109
 
105
110
  /** The lower bound of the 95% Wilson score interval of `hits` in `n`
@@ -134,6 +139,26 @@ export function sessionReturnChance(gaps, elapsedMs, withinMs) {
134
139
  return { idle, returns, p: wilsonLowerBound(returns, idle) };
135
140
  }
136
141
 
142
+ // A write below this many tokens is prompt growth, never a rewrite: 4096 is
143
+ // the largest minimum cacheable prompt length of the supported models.
144
+ export const CACHE_WARM_REWRITE_FLOOR_TOKENS = 4096;
145
+
146
+ /** Whether a warm's `usage` shows a substantial rewrite with poor read
147
+ * coverage: the write reaches the floor and the read covers less than half
148
+ * of read + write. A read of most of the prompt plus the new tail is a hit. */
149
+ export function warmRewrote(usage) {
150
+ const read = Number(usage?.cachedTokens) || 0;
151
+ const write = Number(usage?.cacheWriteTokens) || 0;
152
+ return write >= CACHE_WARM_REWRITE_FLOOR_TOKENS && read < write;
153
+ }
154
+
155
+ /** Whether an entry last used at `touchAt` and living `ttlMs` will have
156
+ * expired by the time a warm sent at `now` reaches the provider (`latencyMs`
157
+ * later): a warm into an expired entry rewrites the whole prompt. */
158
+ export function cacheEntryLapsed(touchAt, ttlMs, now, latencyMs = null) {
159
+ return touchAt + ttlMs - now <= (Number(latencyMs) || 0);
160
+ }
161
+
137
162
  /** When a warm goes out after the last cache use of an entry living `ttlMs`. */
138
163
  export function cacheWarmDelayMs(ttlMs) {
139
164
  return Math.max(1, ttlMs - CACHE_WARM_LEAD_MS);
@@ -223,7 +248,7 @@ export function planCacheWarm({ now, touchAt, ttlMs, lastCallAt, gaps, promptTok
223
248
  // fires at or after the refresh point, TTL − lead, so the lead itself cannot
224
249
  // be the floor; the chain's measured warm latency (none before the first
225
250
  // warm) is.
226
- if (touchAt + ttlMs - now <= (Number(latencyMs) || 0)) return { stop: 'late timer' };
251
+ if (cacheEntryLapsed(touchAt, ttlMs, now, latencyMs)) return { stop: 'late timer' };
227
252
  const economics = cacheWarmEconomics(promptTokens, prices);
228
253
  if (!economics) return { stop: 'prompt size or cache prices unknown' };
229
254
  const interval = cacheWarmDelayMs(ttlMs);
@@ -238,11 +263,35 @@ export function planCacheWarm({ now, touchAt, ttlMs, lastCallAt, gaps, promptTok
238
263
 
239
264
  // Defaults of the injectable dependencies.
240
265
 
241
- async function defaultEnabled() {
242
- const { loadConfig } = await import('../config.mjs');
266
+ // The synchronous on/off state `armCacheWarm` and the restart re-arm gate on.
267
+ // It is always definite: the first call reads the config (synchronous, from
268
+ // disk in a fresh process, where it is authoritative) and caches the value;
269
+ // from then on only the setting call (noteCacheWarmingEnabled) writes it.
270
+ // Refresh-point reads (`deps.enabled`) never do, so a stale read cannot
271
+ // switch warming off globally.
272
+ let warmingOn = null;
273
+
274
+ function enabledNow() {
275
+ warmingOn ??= deps.readConfigEnabled();
276
+ return warmingOn;
277
+ }
278
+
279
+ /** The setting changed (settings-builtin-tools-api.mjs
280
+ * setCacheWarmingEnabled): authoritative; off cancels every armed chain. */
281
+ export function noteCacheWarmingEnabled(enabled) {
282
+ warmingOn = enabled === true;
283
+ if (warmingOn) return;
284
+ for (const sessionId of [...chains.keys()]) cancelCacheWarm(sessionId);
285
+ }
286
+
287
+ function defaultReadConfigEnabled() {
243
288
  return cacheWarmingEnabled(loadConfig({ secrets: false }));
244
289
  }
245
290
 
291
+ async function defaultEnabled() {
292
+ return defaultReadConfigEnabled();
293
+ }
294
+
246
295
  // Per-token dollars of a cache read, a cache write of `ttlMs`'s tier and an
247
296
  // output token for the request's model, from the pricing catalog (null when
248
297
  // unpriced), at the rates of a prompt of `promptTokens`: a model priced by
@@ -351,6 +400,7 @@ const DEFAULT_DEPS = Object.freeze({
351
400
  now: () => Date.now(),
352
401
  timers: globalThis,
353
402
  enabled: defaultEnabled,
403
+ readConfigEnabled: defaultReadConfigEnabled,
354
404
  turnInFlight: (sessionId) => isTurnInFlight(runtimeState.get(sessionId)),
355
405
  lastSentRequest: defaultLastSentRequest,
356
406
  providersReady: defaultProvidersReady,
@@ -366,12 +416,15 @@ let deps = DEFAULT_DEPS;
366
416
 
367
417
  // The idle auto-clear measures idleness from the last cache use, a
368
418
  // successful warm included (durable in the touch store).
369
- setCacheWarmUseSource((sessionId) => (sessionId ? deps.touches().lastWarmUse(sessionId) : 0));
419
+ // The record names the route and account the warm read, so only a request
420
+ // on that same prefix counts it as fresh cache (compact-policy.mjs).
421
+ setCacheWarmUseSource((sessionId) => (sessionId ? deps.touches().warmUse(sessionId) : null));
370
422
 
371
423
  // A session deleted for good drops everything it learned and touched.
372
424
  registerSessionPurgeHook((sessionId) => {
373
425
  cancelCacheWarm(sessionId);
374
426
  turns.delete(sessionId);
427
+ missedSessions.delete(sessionId);
375
428
  deps.touches().forget(sessionId);
376
429
  deps.touches().forgetWarmUse(sessionId);
377
430
  deps.gaps().forget(sessionId);
@@ -411,6 +464,8 @@ async function planFor(sessionId, chain, at) {
411
464
  /** Test seam: replace some dependencies; call with no argument to restore. */
412
465
  export function _setCacheWarmDepsForTest(overrides = null) {
413
466
  for (const sessionId of [...chains.keys()]) cancelCacheWarm(sessionId);
467
+ missedSessions.clear();
468
+ warmingOn = null;
414
469
  deps = overrides ? { ...DEFAULT_DEPS, ...overrides } : DEFAULT_DEPS;
415
470
  }
416
471
 
@@ -418,6 +473,13 @@ export function _setCacheWarmDepsForTest(overrides = null) {
418
473
  // accountId, latencyMs, refreshAt, timer, controller }.
419
474
  const chains = new Map();
420
475
 
476
+ // Sessions whose warm read little of what it rewrote while the entry was
477
+ // still inside its lifetime: the warm does not match what the real requests
478
+ // cache, so no later chain of the session is armed (each would rewrite the
479
+ // prompt once more) until its cached prefix is dropped (forgetCacheWarm) or
480
+ // the session is deleted.
481
+ const missedSessions = new Set();
482
+
421
483
  /** The armed chain of `sessionId` (tests and diagnostics). */
422
484
  export function cacheWarmChain(sessionId) {
423
485
  return chains.get(sessionId) || null;
@@ -596,6 +658,7 @@ export function cancelCacheWarm(sessionId) {
596
658
  export function forgetCacheWarm(sessionId) {
597
659
  cancelCacheWarm(sessionId);
598
660
  if (!sessionId) return;
661
+ missedSessions.delete(sessionId);
599
662
  deps.touches().forget(sessionId);
600
663
  deps.touches().forgetWarmUse(sessionId);
601
664
  }
@@ -648,7 +711,8 @@ function warmTarget(chain) {
648
711
  /**
649
712
  * Arm the session's warm chain from a real request that settled with
650
713
  * `response`, and record the confirmed touch durably; replaces any chain.
651
- * Nothing is armed — and the durable touch is dropped — for an ineligible
714
+ * Nothing is armed while warming is off (enabledNow). Nothing is armed
715
+ * — and the durable touch is dropped — for an ineligible
652
716
  * request, one without a send time, or a response that does not prove the
653
717
  * tier it wrote or the prompt size.
654
718
  */
@@ -663,7 +727,17 @@ export function armCacheWarm(sessionId, request, response) {
663
727
  if (sessionId && request && realActivity(sessionId, request)) {
664
728
  deps.gaps().open(sessionId, Number(request.sentAt));
665
729
  }
730
+ // Warming off: no chain, no timer and no durable touch (the idle gap above
731
+ // is still learned); the touch of an earlier cache use no longer holds.
732
+ if (!enabledNow()) {
733
+ if (sessionId) deps.touches().forget(sessionId);
734
+ return;
735
+ }
666
736
  if (!sessionId || !request || cacheWarmIneligibility(request)) return;
737
+ if (missedSessions.has(sessionId)) {
738
+ deps.touches().forget(sessionId);
739
+ return;
740
+ }
667
741
  const usage = response?.usage || {};
668
742
  const touchAt = Number(request.sentAt);
669
743
  const ttlMs = writtenCacheTtlMs(usage, request.opts.cacheStrategy);
@@ -695,7 +769,7 @@ export function armCacheWarm(sessionId, request, response) {
695
769
  * many chains were armed.
696
770
  */
697
771
  export async function rearmCacheWarmsAfterRestart() {
698
- if (!(await deps.enabled())) return 0;
772
+ if (!enabledNow()) return 0;
699
773
  const candidates = [];
700
774
  for (const sessionId of deps.touches().ids()) {
701
775
  if (chains.has(sessionId)) continue;
@@ -711,12 +785,16 @@ export async function rearmCacheWarmsAfterRestart() {
711
785
  await deps.providersReady();
712
786
  let armed = 0;
713
787
  for (const [sessionId, touch] of candidates) {
788
+ // Re-checked after every await: a setter that turned warming off since
789
+ // has cancelled the chains and nothing more may start.
790
+ if (!enabledNow()) return armed;
714
791
  let request = null;
715
792
  try {
716
793
  request = await deps.lastSentRequest(sessionId);
717
794
  } catch (error) {
718
795
  deps.log(`${sessionId}: restart re-arm skipped (${error?.message || error})`);
719
796
  }
797
+ if (!enabledNow()) return armed;
720
798
  if (!request || cacheWarmIneligibility(request) || chains.has(sessionId)) continue;
721
799
  startChain(sessionId, {
722
800
  request,
@@ -758,7 +836,12 @@ async function fireWarm(sessionId, chain) {
758
836
  // Last check before dispatch, after the final await: a turn that began
759
837
  // during the lookups has the cache to itself.
760
838
  if (deps.turnInFlight(sessionId)) return stopChain(sessionId, chain, 'turn in flight');
839
+ // The lookups above took time: the entry must still live when the warm goes
840
+ // out, counted from its last confirmed read or write.
761
841
  const sentAt = deps.now();
842
+ if (cacheEntryLapsed(chain.touchAt, chain.ttlMs, sentAt, chain.latencyMs)) {
843
+ return stopChain(sessionId, chain, 'entry expired before dispatch');
844
+ }
762
845
  let usage = null;
763
846
  let reportedAt = null;
764
847
  let failure = null;
@@ -779,14 +862,21 @@ async function fireWarm(sessionId, chain) {
779
862
  }
780
863
  if (chains.get(sessionId) !== chain) return;
781
864
  if (failure) return stopChain(sessionId, chain, `warm failed: ${failure?.message || failure}`);
782
- if (!(Number(usage?.cachedTokens) > 0) || Number(usage?.cacheWriteTokens) > 0) {
865
+ const read = Number(usage?.cachedTokens) || 0;
866
+ const rewrite = warmRewrote(usage);
867
+ if (!(read > 0) || rewrite) {
783
868
  deps.touches().forget(sessionId);
869
+ if (rewrite) missedSessions.add(sessionId);
784
870
  return stopChain(sessionId, chain, 'warm missed the cache');
785
871
  }
786
872
  chain.touchAt = sentAt;
787
873
  chain.latencyMs = (reportedAt ?? deps.now()) - sentAt;
788
874
  recordTouch(sessionId, chain);
789
875
  // A read without a write is cache use: the idle auto-clear counts from it.
790
- deps.touches().recordWarmUse(sessionId, sentAt);
876
+ deps.touches().recordWarmUse(sessionId, sentAt, {
877
+ provider: chain.request.provider.name,
878
+ model: chain.request.model,
879
+ accountId: target.accountId,
880
+ });
791
881
  scheduleWarm(sessionId, chain);
792
882
  }
@@ -11,7 +11,7 @@ import { createHash } from 'node:crypto';
11
11
 
12
12
  /** Bumped whenever the table layout or a codec changes: the DB is then
13
13
  * dropped and backfilled again from the JSON catalog. */
14
- export const SCHEMA_VERSION = 10;
14
+ export const SCHEMA_VERSION = 11;
15
15
  /** PRAGMA application_id ('MXSC'): a file without it is not this catalog. */
16
16
  export const APPLICATION_ID = 0x4d585343;
17
17
 
@@ -68,6 +68,9 @@ const FIELDS = [
68
68
  // Places of the incoming messages with text among the counted ones, for the
69
69
  // unread count of a bot home / room log; absent otherwise (schema 10).
70
70
  ['incomingTail', 'incoming_tail', 'optjson'],
71
+ // Compactions the session has had: the epoch of its message numbering;
72
+ // absent for a row written before schema 11.
73
+ ['compactCount', 'compact_count', 'optjson'],
71
74
  ];
72
75
 
73
76
  const COLUMN_TYPES = { text: 'TEXT', real: 'REAL', int: 'INTEGER', bool: 'INTEGER', json: 'TEXT', optjson: 'TEXT' };
@@ -9,6 +9,7 @@ import {
9
9
  isProtectedContextAckMessage,
10
10
  latestActualUserInstructionIndex,
11
11
  pendingTaskNotificationIndexes,
12
+ requestStart,
12
13
  } from './messages.mjs';
13
14
  import { staleObservations, supersedeObservation } from './observation-supersession.mjs';
14
15
  import { carryMessageId, withoutMessageIdsReplacer } from '../message-ids.mjs';
@@ -101,14 +102,6 @@ export function executionTokens(messages) {
101
102
  );
102
103
  }
103
104
 
104
- function requestStart(messages, index) {
105
- for (let i = index; i >= 0; i -= 1) {
106
- if (!isActualUserInstructionMessage(messages[i])) continue;
107
- if (messages[i]?.meta?.source !== 'steering') return i;
108
- }
109
- return latestActualUserInstructionIndex(messages.slice(0, index + 1));
110
- }
111
-
112
105
  // Each assistant tool-call turn with the tool results that directly follow it.
113
106
  function executionGroups(messages) {
114
107
  const groups = [];
@@ -43,6 +43,29 @@ export function latestActualUserInstructionIndex(messages) {
43
43
  return -1;
44
44
  }
45
45
 
46
+ // The user instruction that began the request `index` belongs to; a steering
47
+ // message continues the request instead of starting one.
48
+ export function requestStart(messages, index) {
49
+ for (let i = index; i >= 0; i -= 1) {
50
+ if (!isActualUserInstructionMessage(messages[i])) continue;
51
+ if (messages[i]?.meta?.source !== 'steering') return i;
52
+ }
53
+ return latestActualUserInstructionIndex(messages.slice(0, index + 1));
54
+ }
55
+
56
+ // A tool call's arguments as an object, whether stored parsed or as JSON text.
57
+ export function callArguments(call) {
58
+ let args = call?.arguments;
59
+ if (typeof args === 'string') {
60
+ try {
61
+ args = JSON.parse(args);
62
+ } catch {
63
+ return {};
64
+ }
65
+ }
66
+ return args && typeof args === 'object' ? args : {};
67
+ }
68
+
46
69
  export function latestActualUserInstructionMessage(messages) {
47
70
  const index = latestActualUserInstructionIndex(messages);
48
71
  return index >= 0 ? { ...messages[index] } : null;
@@ -1,9 +1,12 @@
1
- // Observations that a later one of the same browser page or desktop window
2
- // replaced. Such a result's refs have expired, and its element list and pixels
3
- // describe a state that no longer exists. Only compaction drops them: between
4
- // compactions the transcript stays append-only so the provider cache prefix
5
- // holds, and compaction rewrites that prefix anyway. The execution archive
6
- // keeps every original.
1
+ // Observations that a later one of the same browser page, desktop window,
2
+ // visualize tab (inspect / check) or image file replaced. Such a result's refs
3
+ // have expired, and its element list and pixels describe a state that no
4
+ // longer exists. Only compaction drops them: between compactions the
5
+ // transcript stays append-only so the provider cache prefix holds, and
6
+ // compaction rewrites that prefix anyway. The execution archive keeps every
7
+ // original.
8
+ import { callArguments, requestStart } from './messages.mjs';
9
+
7
10
  const BROWSER_TOOLS = new Set(['browser', 'browser_devtools']);
8
11
  const SNAPSHOT_LINE = /(?:^|\n)Snapshot:\s+((p\d+)-s\d+)\b/;
9
12
  const TERMINAL_LINE = /(?:^|\n)Terminal:\s+(session-terminal:\S+)/;
@@ -52,13 +55,40 @@ function containsRun(newer, older) {
52
55
  return false;
53
56
  }
54
57
 
55
- // What one tool result observed, keyed by the page or window it describes.
56
- function observationOf(message, toolName) {
58
+ const VISUALIZE_READS = new Set(['inspect', 'check']);
59
+ const IMAGE_FILE = /\.(?:png|jpe?g|gif|webp)$/i;
60
+ // What a superseded computer frame keeps besides its reference.
61
+ const FRAME_OUTCOME_KEYS = ['ok', 'action', 'actions', 'status', 'error', 'window_id'];
62
+
63
+ // A visualize read-out is the same observation only for the same tab, kind of
64
+ // read, viewport and scope: a mobile inspect does not replace a desktop one.
65
+ function visualizeKey(args) {
66
+ const input = args.input && typeof args.input === 'object' ? args.input : {};
67
+ if (!VISUALIZE_READS.has(args.action) || typeof input.tab !== 'string' || !input.tab) return null;
68
+ const scope = args.action === 'inspect' ? `:${JSON.stringify(input.viewport ?? null)}:${input.selector ?? ''}` : '';
69
+ return { key: `visualize:${args.action}:${input.tab}${scope}`, action: args.action, tab: input.tab };
70
+ }
71
+
72
+ // What one tool result observed, keyed by the page, window, tab or file it
73
+ // describes.
74
+ function observationOf(message, call) {
57
75
  if (message?.role !== 'tool' || message.toolKind === 'error') return null;
76
+ const toolName = String(call?.name || '');
58
77
  const parts = contentParts(message.content);
78
+ if (toolName === 'read') {
79
+ const args = callArguments(call);
80
+ const path = typeof args.path === 'string' ? args.path : args.file_path;
81
+ if (typeof path !== 'string' || !IMAGE_FILE.test(path) || !parts.some(isImagePart)) return null;
82
+ return { key: `image:${path.replace(/\\/g, '/')}`, imagePath: path, requestScoped: true };
83
+ }
59
84
  const textIndex = parts.findIndex((part) => part?.type === 'text' && !isImagePart(part));
60
85
  if (textIndex < 0) return null;
61
86
  const text = String(parts[textIndex].text);
87
+ if (toolName === 'visualize') {
88
+ if (/^\[Visualize \w+ of tab \S+ superseded:/.test(text)) return null;
89
+ const read = visualizeKey(callArguments(call));
90
+ return read ? { key: read.key, parts, textIndex, visualize: read, requestScoped: true } : null;
91
+ }
62
92
  if (BROWSER_TOOLS.has(toolName)) {
63
93
  const snapshot = SNAPSHOT_LINE.exec(text);
64
94
  return snapshot ? { key: `browser:${snapshot[2]}`, parts, textIndex, text, snapshot } : null;
@@ -82,40 +112,90 @@ function observationOf(message, toolName) {
82
112
  const frame = value?.observation && typeof value.observation === 'object' ? value.observation : value;
83
113
  const windowId = frame?.window_id || value?.window_id;
84
114
  const observed = Array.isArray(frame?.elements) || frame?.ocr !== undefined || parts.some(isImagePart);
85
- return windowId && observed ? { key: `computer:${windowId}`, parts, textIndex, value, frame } : null;
115
+ if (windowId && observed) return { key: `computer:${windowId}`, parts, textIndex, value, frame };
116
+ // A stub an earlier compaction wrote in the fuller format.
117
+ const slim =
118
+ typeof frame?.superseded === 'string' &&
119
+ Object.keys(frame).some((name) => name !== 'superseded' && !FRAME_OUTCOME_KEYS.includes(name));
120
+ return slim ? { stub: true, parts, textIndex, value, frame, reference: frame.superseded } : null;
86
121
  }
87
122
 
88
- /** Tool results that a later observation of the same page or window
89
- * replaced, keyed by transcript index. */
123
+ // Transcript indices whose results are still in use and must stay verbatim:
124
+ // the latest tool-call group (the batch the session continues from, which
125
+ // compaction always retains) and any group with a call that has no result yet.
126
+ function protectedObservationIndices(messages) {
127
+ const protectedIndices = new Set();
128
+ const protect = (from, to) => {
129
+ for (let index = from; index < to; index += 1) protectedIndices.add(index);
130
+ };
131
+ let latest = null;
132
+ for (let index = 0; index < messages.length; index += 1) {
133
+ const message = messages[index];
134
+ if (message?.role !== 'assistant' || !message.toolCalls?.length) continue;
135
+ const start = index;
136
+ const answered = new Set();
137
+ while (messages[index + 1]?.role === 'tool') {
138
+ index += 1;
139
+ answered.add(messages[index].toolCallId);
140
+ }
141
+ latest = [start, index + 1];
142
+ if (message.toolCalls.some((call) => !answered.has(call?.id))) protect(...latest);
143
+ }
144
+ if (latest) protect(...latest);
145
+ return protectedIndices;
146
+ }
147
+
148
+ /** Tool results that a later observation of the same page, window, tab or
149
+ * file replaced, keyed by transcript index. Protected results are never
150
+ * listed, though they still count as the newer observation of what they
151
+ * describe. An image file read or visualize read-out is also kept for the
152
+ * whole active request (everything since the last user instruction), since
153
+ * the request may compare it with a later one; once a newer user message
154
+ * exists it is replaceable. */
90
155
  export function staleObservations(messages) {
91
- const toolNames = new Map();
156
+ const protectedIndices = protectedObservationIndices(messages);
157
+ const activeRequest = Math.max(requestStart(messages, messages.length - 1), 0);
158
+ const calls = new Map();
92
159
  for (const message of messages) {
93
160
  if (message?.role !== 'assistant' || !Array.isArray(message.toolCalls)) continue;
94
- for (const call of message.toolCalls) if (call?.id) toolNames.set(call.id, String(call.name || ''));
161
+ for (const call of message.toolCalls) if (call?.id) calls.set(call.id, call);
95
162
  }
96
163
  const newest = new Set();
97
164
  const terminalReads = new Map();
98
165
  const stale = new Map();
99
166
  for (let index = messages.length - 1; index >= 0; index -= 1) {
100
- const found = observationOf(messages[index], toolNames.get(messages[index]?.toolCallId));
167
+ const found = observationOf(messages[index], calls.get(messages[index]?.toolCallId));
101
168
  if (!found) continue;
102
- if (found.body) {
169
+ const replaceable = !protectedIndices.has(index) && !(found.requestScoped && index >= activeRequest);
170
+ if (found.stub) {
171
+ if (replaceable) stale.set(index, found);
172
+ } else if (found.body) {
103
173
  const newer = terminalReads.get(found.key) ?? [];
104
- if (newer.some((body) => containsRun(body, found.body))) stale.set(index, found);
174
+ if (replaceable && newer.some((body) => containsRun(body, found.body))) stale.set(index, found);
105
175
  newer.push(found.body);
106
176
  terminalReads.set(found.key, newer);
107
- } else if (newest.has(found.key)) stale.set(index, found);
108
- else newest.add(found.key);
177
+ } else if (newest.has(found.key)) {
178
+ if (replaceable) stale.set(index, found);
179
+ } else newest.add(found.key);
109
180
  }
110
181
  return stale;
111
182
  }
112
183
 
113
184
  /** The result without its replaced observation. A browser result keeps what
114
185
  * precedes its snapshot (effect notes, a script's value) and a computer
115
- * result its action outcome; neither keeps elements, OCR text or pixels. */
186
+ * result its action outcome plus a reference; none keeps elements, OCR text,
187
+ * frame metadata or pixels. A visualize read-out or image file read becomes a
188
+ * one-line placeholder naming what it was and how to get it again. */
116
189
  export function supersedeObservation(message, found, origin) {
117
190
  let text;
118
- if (found.terminal) {
191
+ if (found.imagePath) {
192
+ text = `[Image read of ${found.imagePath} superseded: a newer read of this file follows. Read the file again to view it. ${origin}]`;
193
+ return { ...message, content: withParts(message.content, [{ type: 'text', text }]) };
194
+ }
195
+ if (found.visualize) {
196
+ const { action, tab } = found.visualize;
197
+ text = `[Visualize ${action} of tab ${tab} superseded: a newer ${action} of this tab follows. Run visualize ${action} on the tab again to get it. ${origin}]`;
198
+ } else if (found.terminal) {
119
199
  text = `[Terminal ${found.terminal} read superseded: a newer read of this tab follows. ${origin}]`;
120
200
  } else if (found.snapshot) {
121
201
  const { snapshot } = found;
@@ -126,9 +206,9 @@ export function supersedeObservation(message, found, origin) {
126
206
  const note = `[Snapshot ${snapshot[1]} superseded: a newer snapshot of this page follows. ${origin}]`;
127
207
  text = before ? `${before}\n\n${note}` : note;
128
208
  } else {
129
- const frame = { ...found.frame, superseded: `a newer observation of this window follows. ${origin}` };
130
- delete frame.elements;
131
- delete frame.ocr;
209
+ const frame = {};
210
+ for (const name of FRAME_OUTCOME_KEYS) if (found.frame[name] !== undefined) frame[name] = found.frame[name];
211
+ frame.superseded = found.reference ?? `a newer observation of this window follows. ${origin}`;
132
212
  text = JSON.stringify(found.frame === found.value ? frame : { ...found.value, observation: frame });
133
213
  }
134
214
  const parts = found.parts.flatMap((part, index) => {