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
@@ -24,6 +24,8 @@ import {
24
24
  inheritedFromSessionId,
25
25
  isStoredSessionId,
26
26
  positiveNumber as _positiveNumber,
27
+ compactCount,
28
+ compactCountField,
27
29
  realTurnCount,
28
30
  workspaceSummary,
29
31
  } from './store-summary-fields.mjs';
@@ -54,26 +56,50 @@ const _computeCleanPreview = cleanSummaryPreview;
54
56
  // cleaning runs a chain of regex replaces that each allocate a copy. Those
55
57
  // short strings repeat from flush to flush, so the pure result is memoized per
56
58
  // (max, text). Long raw message text is not kept here (see _previewFromMessage).
59
+ // Every new message's raw text (up to the text limit) also passes through, so
60
+ // the memo is bounded by the characters it holds, least recently used first
61
+ // out: at 8192 entries alone it kept ~16–32 MB of message text per `max`.
57
62
  const CLEAN_PREVIEW_MEMO_TEXT_LIMIT = 2048;
58
63
  const CLEAN_PREVIEW_MEMO_ENTRIES = 8192;
59
- const cleanPreviewMemo = new Map(); // max → Map<text, cleaned>
64
+ const CLEAN_PREVIEW_MEMO_MAX_CHARS = 512 * 1024;
65
+ const cleanPreviewMemo = new Map(); // max → { byText: Map<text, cleaned>, chars }
60
66
 
61
67
  function _cleanPreview(text, max = 240) {
62
68
  if (typeof text !== 'string' || text.length > CLEAN_PREVIEW_MEMO_TEXT_LIMIT) return _computeCleanPreview(text, max);
63
- let byText = cleanPreviewMemo.get(max);
64
- if (!byText) {
65
- byText = new Map();
66
- cleanPreviewMemo.set(max, byText);
69
+ let memo = cleanPreviewMemo.get(max);
70
+ if (!memo) {
71
+ memo = { byText: new Map(), chars: 0 };
72
+ cleanPreviewMemo.set(max, memo);
67
73
  }
74
+ const { byText } = memo;
68
75
  let cleaned = byText.get(text);
69
- if (cleaned === undefined) {
70
- cleaned = _computeCleanPreview(text, max);
71
- if (byText.size >= CLEAN_PREVIEW_MEMO_ENTRIES) byText.clear();
76
+ if (cleaned !== undefined) {
77
+ byText.delete(text);
72
78
  byText.set(text, cleaned);
79
+ return cleaned;
80
+ }
81
+ cleaned = _computeCleanPreview(text, max);
82
+ byText.set(text, cleaned);
83
+ memo.chars += text.length + cleaned.length;
84
+ for (const [oldText, oldCleaned] of byText) {
85
+ if (byText.size <= CLEAN_PREVIEW_MEMO_ENTRIES && memo.chars <= CLEAN_PREVIEW_MEMO_MAX_CHARS) break;
86
+ byText.delete(oldText);
87
+ memo.chars -= oldText.length + oldCleaned.length;
73
88
  }
74
89
  return cleaned;
75
90
  }
76
91
 
92
+ /** Entries and characters the preview memo holds, summed over every `max` (tests). */
93
+ export function _cleanPreviewMemoStatsForTest() {
94
+ let entries = 0;
95
+ let chars = 0;
96
+ for (const memo of cleanPreviewMemo.values()) {
97
+ entries += memo.byText.size;
98
+ chars += memo.chars;
99
+ }
100
+ return { entries, chars };
101
+ }
102
+
77
103
  const sessionMessageProjectionMemo = new WeakMap();
78
104
  // message → { raw, preview }. A session's first real user message is
79
105
  // re-checked on every save; its text is re-derived (cheap) and the preview is
@@ -265,6 +291,8 @@ export function _sessionSummary(session) {
265
291
  taskType: session.taskType || null,
266
292
  messageCount: messageProjection.count,
267
293
  ...(messageProjection.incoming ? { incomingTail: messageProjection.incoming.slice() } : {}),
294
+ // The numbering epoch of messageCount: a compaction restarts it.
295
+ compactCount: compactCount(session.compaction) ?? 0,
268
296
  realTurnCount: messageProjection.real,
269
297
  title: _cleanPreview(session.title || '', 100),
270
298
  preview: messageProjection.preview,
@@ -324,6 +352,7 @@ function _normalizeSummaryRow(row) {
324
352
  taskType: row.taskType || null,
325
353
  messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
326
354
  ...(Array.isArray(row.incomingTail) ? { incomingTail: normalizedIncomingTail(row.incomingTail) } : {}),
355
+ ...compactCountField(row),
327
356
  realTurnCount: realTurnCount(row),
328
357
  title: _cleanPreview(row.title || '', 100),
329
358
  preview: _cleanPreview(row.preview || ''),
@@ -24,27 +24,44 @@ import {
24
24
  stampedDerivation,
25
25
  storedSessionCatalogRow,
26
26
  } from './store-summary-catalog.mjs';
27
- import { createStoredTranscriptCache, nextProjectionStamp } from './store-transcript-cache.mjs';
27
+ import {
28
+ createStoredTranscriptCache,
29
+ historyStampOf,
30
+ producedSlice,
31
+ projectionStampEpoch,
32
+ transcriptTextHash,
33
+ } from './store-transcript-cache.mjs';
28
34
  import {
29
35
  forgetStoredTranscriptOffThread,
30
36
  OFFLOAD_MIN_CHARS,
31
37
  projectStoredTranscriptOffThread,
32
38
  storedTranscriptWorkerRunning,
33
39
  } from './store-transcript-worker.mjs';
34
- import { projectStoredTranscript } from './store-transcript-projection.mjs';
40
+ import {
41
+ cursorPageRows,
42
+ heldRowsDigester,
43
+ prepareStoredTranscriptBase,
44
+ projectStoredTranscript,
45
+ storedHistorySlice,
46
+ windowStoredTranscript,
47
+ } from './store-transcript-projection.mjs';
35
48
  import { dataDir } from './store-summary-locations.mjs';
36
49
  import { isStoredSessionId, workspaceSummary } from './store-summary-fields.mjs';
37
50
  import { readArchivedAgentResult } from './store-summary-archived-agent.mjs';
38
51
  import {
52
+ compactionArchivePageFromText,
39
53
  compactionArchivePath,
40
54
  compactionArchiveRange,
41
- readCompactionArchivePage,
42
55
  visitCompactionArchive,
43
56
  } from './compaction-archive.mjs';
44
57
  import { attachmentHistoryRows } from './stored-attachment-history.mjs';
45
58
  import { currentSessionCatalogDb, onSessionCatalogDbChange, sessionCatalogDb } from './catalog-db/index.mjs';
46
59
  import { decodeCatalogCursor, encodeCatalogCursor, isAfterCursor } from './catalog-db/schema.mjs';
47
- import { onSessionActivityChange, onSessionApprovalCardChange } from '../../../shared/session-activity-signal.mjs';
60
+ import {
61
+ onSessionActivityChange,
62
+ onSessionApprovalCardChange,
63
+ onSessionBotWorkChange,
64
+ } from '../../../shared/session-activity-signal.mjs';
48
65
  import { inFlightSessionActivity } from './manager/runtime-state.mjs';
49
66
  import {
50
67
  readCanonicalSessionRecord as readCanonicalLifecycle,
@@ -72,15 +89,55 @@ const storedTranscriptCache = createStoredTranscriptCache();
72
89
  // lookup can neither fill nor evict.
73
90
  const attachmentHistoryCache = createStoredTranscriptCache();
74
91
 
92
+ // The display histories of SMALL sessions (record and archive under
93
+ // OFFLOAD_MIN_CHARS, projected on this thread), kept for slicing within a
94
+ // source-character budget; a larger one never stays on this thread (its
95
+ // history lives in the projection worker, or is projected per read as a
96
+ // bounded window).
97
+ const SMALL_HISTORY_MAX_CHARS = 4 * 1024 * 1024;
98
+ const smallHistories = (() => {
99
+ const entries = new Map();
100
+ let chars = 0;
101
+ const drop = (sessionId) => {
102
+ const entry = entries.get(sessionId);
103
+ if (!entry) return;
104
+ entries.delete(sessionId);
105
+ chars -= entry.chars;
106
+ };
107
+ return {
108
+ get(sessionId, identity) {
109
+ const entry = entries.get(sessionId);
110
+ if (!entry || entry.identity !== identity) return null;
111
+ entries.delete(sessionId);
112
+ entries.set(sessionId, entry);
113
+ return entry.history;
114
+ },
115
+ retain(sessionId, identity, sourceChars, history) {
116
+ drop(sessionId);
117
+ if (sourceChars > SMALL_HISTORY_MAX_CHARS) return;
118
+ entries.set(sessionId, { identity, chars: sourceChars, history });
119
+ chars += sourceChars;
120
+ while (chars > SMALL_HISTORY_MAX_CHARS && entries.size > 1) drop(entries.keys().next().value);
121
+ },
122
+ forget: drop,
123
+ clear() {
124
+ entries.clear();
125
+ chars = 0;
126
+ },
127
+ };
128
+ })();
129
+
75
130
  /** Test seam: forget every cached projection. */
76
131
  export function clearStoredTranscriptCache() {
77
132
  storedTranscriptCache.clear();
133
+ smallHistories.clear();
78
134
  }
79
135
 
80
136
  /** Drop one session's cold projections: it went live, or its last view left. */
81
137
  export function forgetStoredSessionTranscript(sessionId) {
82
138
  const id = String(sessionId || '').trim();
83
139
  storedTranscriptCache.forget(`${id}|`);
140
+ smallHistories.forget(id);
84
141
  if (id) forgetStoredTranscriptOffThread(id);
85
142
  }
86
143
 
@@ -136,6 +193,10 @@ onSessionActivityChange(() => {
136
193
  // A session started or stopped waiting on the user's approval: its catalog row
137
194
  // (`awaitingApproval`) changed, though no store file did.
138
195
  onSessionApprovalCardChange(() => notifyCatalogListeners('approval'));
196
+ // A bot started or stopped having a send in flight in a session: its catalog
197
+ // row (`handedBy`) changed, though neither a file nor the set of working
198
+ // sessions did (the work may already be running, or continue past the end).
199
+ onSessionBotWorkChange(() => notifyCatalogListeners('bot-work'));
139
200
 
140
201
  /** `listener()` runs when a background pass finds a session file that changed
141
202
  * without a directory or index change, i.e. when an ordinary listing would
@@ -378,10 +439,16 @@ export function storedSessionTranscriptStamp(id) {
378
439
  * itself). The answer is the cache's `project` contract; a worker failure
379
440
  * rejects and the cache runs the in-process pipeline instead. */
380
441
  async function projectStoredTranscriptElsewhere(known, context) {
381
- const { sessionId, recordPath, itemLimit, timing } = context;
442
+ const { sessionId, recordPath, timing, fingerprint, slice } = context;
382
443
  timing.worker = storedTranscriptWorkerRunning(sessionId) ? 'warm' : 'cold';
383
444
  const workerStartedAt = performance.now();
384
- const answer = await projectStoredTranscriptOffThread({ sessionId, recordPath, itemLimit, known });
445
+ const answer = await projectStoredTranscriptOffThread({
446
+ sessionId,
447
+ recordPath,
448
+ known,
449
+ fingerprint,
450
+ slice: { ...slice, epoch: projectionStampEpoch() },
451
+ });
385
452
  timing.workerMs = performance.now() - workerStartedAt;
386
453
  Object.assign(timing, answer.timing);
387
454
  context.readState = answer.state;
@@ -389,15 +456,66 @@ async function projectStoredTranscriptElsewhere(known, context) {
389
456
  if (answer.same) return answer;
390
457
  if (answer.unreadable) return { ...answer, value: null };
391
458
  if (answer.stamp) readCanonicalLifecycle.rememberStrictVerdict(recordPath, answer.stamp, answer.lifecycle);
392
- // Stamps are process-unique per module realm; re-issue in this one.
393
- answer.value.projectionStamp = nextProjectionStamp();
459
+ if (answer.historyBuilt === false) timing.history = 'retained';
394
460
  return answer;
395
461
  }
396
462
 
463
+ /** A projector (projectStoredTranscript's signature) for a historical-position
464
+ * read on this thread (a checkpoint sidecar, or the worker unavailable): the
465
+ * base once, then tail windows doubling from twice the page until one holds
466
+ * the cursor's row with the page beside it, or history ends. Never the whole
467
+ * display history: only as deep as the row itself. */
468
+ const CURSOR_WINDOW_MAX_ITEMS = 8_192;
469
+
470
+ function cursorWindowProjector(cursor, timing) {
471
+ return async (sessionId, doc, options) => {
472
+ const base = await prepareStoredTranscriptBase(sessionId, doc, options);
473
+ const id = cursor.before ?? cursor.after;
474
+ for (
475
+ let itemLimit = Math.max(64, cursor.maxRows * 2);
476
+ ;
477
+ itemLimit = Math.min(itemLimit * 2, CURSOR_WINDOW_MAX_ITEMS)
478
+ ) {
479
+ timing.cursorWindowItems = itemLimit;
480
+ const window = await windowStoredTranscript(sessionId, base, { itemLimit, includeMessages: false });
481
+ const at = window.items.findIndex((item) => item?.id === id);
482
+ if ((at >= 0 && (cursor.before == null || at >= cursor.maxRows)) || !window.transcriptHasOlder) return window;
483
+ // Never deeper on this thread: a row past the bound answers `reset`
484
+ // (the slice finds no such row), one at its edge a shorter page.
485
+ if (itemLimit >= CURSOR_WINDOW_MAX_ITEMS) return at >= 0 ? window : { ...window, items: [] };
486
+ }
487
+ };
488
+ }
489
+
490
+ /** The slice `request` names of `history` (the display history, or a window
491
+ * of it), stamped `<history stamp>/<slice>`. */
492
+ async function sliceOf(history, request, historyStamp) {
493
+ const proven = Boolean(request.held) && request.baseHistory === historyStamp;
494
+ const value = storedHistorySlice(
495
+ history,
496
+ { ...request, proven },
497
+ { digestOf: request.held && !proven ? await heldRowsDigester() : null }
498
+ );
499
+ const { windowStamp } = value;
500
+ delete value.windowStamp;
501
+ value.projectionStamp = `${historyStamp}/${windowStamp}`;
502
+ return value;
503
+ }
504
+
397
505
  /** Projects the strictly parsed record `text` (read at full stamp `stamp`, or
398
506
  * null when not provably stable) into the cached transcript value. */
399
507
  async function produceStoredTranscript(text, stamp, context) {
400
- const { sessionId, recordPath, itemLimit, includeMessages, mode, checkpointAbsent, timing } = context;
508
+ const { sessionId, recordPath, itemLimit, slice, fingerprint, includeMessages, mode, checkpointAbsent, timing } =
509
+ context;
510
+ const historyStamp =
511
+ mode === 'items' ? historyStampOf(sessionId, transcriptTextHash(text), text.length, fingerprint) : null;
512
+ // A small session's retained history answers every other slice of it.
513
+ const keepHistory = mode === 'items' && checkpointAbsent && context.sourceChars < OFFLOAD_MIN_CHARS;
514
+ const retained = keepHistory ? smallHistories.get(sessionId, historyStamp) : null;
515
+ if (retained) {
516
+ timing.history = 'retained';
517
+ return producedSlice(await sliceOf(retained, slice, historyStamp), { historyBuilt: false });
518
+ }
401
519
  const parseStartedAt = performance.now();
402
520
  const record = readTopLevelLifecycleRecord(text);
403
521
  timing.parseMs = performance.now() - parseStartedAt;
@@ -423,22 +541,29 @@ async function produceStoredTranscript(text, stamp, context) {
423
541
  // load the hand-off serves, and projects the load cache's session
424
542
  // exactly as before; only that projection is detached.
425
543
  const projectStartedAt = performance.now();
426
- const value = await projectStoredTranscript(
427
- sessionId,
428
- handedOver && checkpointAbsent ? JSON.parse(text) : record.doc,
429
- {
430
- itemLimit,
431
- includeMessages,
432
- checkpointAbsent,
433
- }
434
- );
544
+ const project = keepHistory || !slice.cursor ? projectStoredTranscript : cursorWindowProjector(slice.cursor, timing);
545
+ const projected = await project(sessionId, handedOver && checkpointAbsent ? JSON.parse(text) : record.doc, {
546
+ itemLimit,
547
+ // Only a small session's whole display history is built (and kept) on
548
+ // this thread; any other item read projects just its bounded window
549
+ // (a tail of that history) and slices it; a historical-position read
550
+ // projects the tail window reaching its row (cursorWindowProjector).
551
+ history: keepHistory,
552
+ includeMessages,
553
+ checkpointAbsent,
554
+ });
435
555
  timing.projectMs = performance.now() - projectStartedAt;
436
- if (!handedOver || checkpointAbsent || !value || mode !== 'items') return value;
437
- try {
438
- return structuredClone(value);
439
- } catch {
440
- return value;
556
+ if (mode !== 'items' || !projected) return projected;
557
+ if (keepHistory) smallHistories.retain(sessionId, historyStamp, context.sourceChars, projected);
558
+ let value = await sliceOf(projected, slice, historyStamp);
559
+ if (handedOver && !checkpointAbsent) {
560
+ try {
561
+ value = structuredClone(value);
562
+ } catch {
563
+ // Served as projected.
564
+ }
441
565
  }
566
+ return producedSlice(value);
442
567
  }
443
568
 
444
569
  /** The rows of one session's whole stored conversation that name attachments
@@ -484,6 +609,37 @@ async function readStoredAttachmentHistory(sessionId, recordPath, archivePath) {
484
609
  return value ?? null;
485
610
  }
486
611
 
612
+ /** One page of a session's compaction archive (`{ after, before, skip,
613
+ * limit }`, as readCompactionArchivePage pages it), cut from its visible rows
614
+ * built once per archive content in the transcript cache: paging a live
615
+ * session through a large segment slices instead of re-parsing it; `paging`
616
+ * keeps it cached under pressure (the cache's `pin`). */
617
+ async function readCachedCompactionArchivePage(sessionId, archivePath, range, paging) {
618
+ const after = Math.max(0, Math.floor(Number(range.after) || 0));
619
+ const before = Number(range.before);
620
+ const visible = { after, before: Number.isFinite(before) ? before : Number.POSITIVE_INFINITY };
621
+ const archive = probePath(archivePath);
622
+ if (archive.state !== PROBE_PRESENT) return { items: [], hasOlder: false };
623
+ const { value } = await storedTranscriptCache.read({
624
+ key: `${sessionId}|archive|${visible.after}:${visible.before}`,
625
+ group: `${sessionId}|archive`,
626
+ fingerprint: '',
627
+ fileStat: archive,
628
+ pin: paging,
629
+ loadText: () => {
630
+ const body = readTextFile(archivePath);
631
+ return body.state === PROBE_PRESENT ? body.text : null;
632
+ },
633
+ produce: async (text) => ({
634
+ items: (await compactionArchivePageFromText(text, { ...visible, limit: Number.POSITIVE_INFINITY })).items,
635
+ }),
636
+ });
637
+ const items = Array.isArray(value?.items) ? value.items : [];
638
+ const end = Math.max(0, items.length - Math.max(0, Math.floor(Number(range.skip) || 0)));
639
+ const start = Math.max(0, end - Math.max(0, Math.floor(Number(range.limit) || 0)));
640
+ return { items: items.slice(start, end), hasOlder: start > 0 };
641
+ }
642
+
487
643
  /** Read exactly one persisted session for a visible desktop pane. This never
488
644
  * enumerates siblings. Normal reads stay independent of runtime ownership;
489
645
  * interrupted turns conditionally use the same durable reconnect recovery as
@@ -500,7 +656,7 @@ export async function readStoredSessionTranscript(id, options = {}) {
500
656
  // One page of the display-only compaction archive (the live head loader
501
657
  // pages it once a runtime's in-memory rows are exhausted).
502
658
  if (options.compactionArchive && typeof options.compactionArchive === 'object') {
503
- return readCompactionArchivePage(join(dataDir(), 'sessions'), sessionId, options.compactionArchive);
659
+ return readCachedCompactionArchivePage(sessionId, archivePath, options.compactionArchive, options.paging === true);
504
660
  }
505
661
  // The checkpoint sidecar shapes the projection as much as the record does,
506
662
  // so its identity joins the cache fingerprint. Only a PROVABLY absent
@@ -516,6 +672,51 @@ export async function readStoredSessionTranscript(id, options = {}) {
516
672
  const archive = probePath(archivePath);
517
673
  const requestedLimit = Number(options.transcriptItemLimit);
518
674
  const itemLimit = Number.isFinite(requestedLimit) && requestedLimit > 0 ? requestedLimit : Number.POSITIVE_INFINITY;
675
+ // `transcriptSkip`: the window's newest rows to leave out (a range read
676
+ // slices only the rows above them, with `skippedFirstId`).
677
+ const requestedSkip = Number(options.transcriptSkip);
678
+ const skip =
679
+ Number.isFinite(itemLimit) && Number.isSafeInteger(requestedSkip) && requestedSkip > 0
680
+ ? Math.min(requestedSkip, itemLimit)
681
+ : 0;
682
+ // `transcriptHeld` (`{ firstId, count, digest }`): the newest rows a paged
683
+ // caller holds, proven by `transcriptBaseHistory` (the history stamp they
684
+ // were served under) or their digest, where the history is: only the page
685
+ // above them (`transcriptPage`: `{ maxRows, byteBudget }`) is sliced,
686
+ // whatever their count. Unproven, a tail of at most `maxRows` rows.
687
+ const heldOption = options.transcriptHeld;
688
+ const held =
689
+ Number.isFinite(itemLimit) && heldOption && Number.isSafeInteger(heldOption.count) && heldOption.count > 0
690
+ ? { firstId: heldOption.firstId, count: heldOption.count, digest: String(heldOption.digest ?? '') }
691
+ : null;
692
+ const page = options.transcriptPage || {};
693
+ // `transcriptCursor` (`{ before | after, maxRows, byteBudget }`): a
694
+ // historical-position read — the rows next to one row, by its id.
695
+ const cursorOption = options.transcriptCursor;
696
+ const cursor =
697
+ cursorOption && (cursorOption.before != null || cursorOption.after != null)
698
+ ? {
699
+ ...(cursorOption.before != null ? { before: cursorOption.before } : { after: cursorOption.after }),
700
+ maxRows: cursorPageRows(cursorOption.maxRows),
701
+ byteBudget: Number(cursorOption.byteBudget) > 0 ? Number(cursorOption.byteBudget) : null,
702
+ }
703
+ : null;
704
+ const slice = cursor
705
+ ? { cursor }
706
+ : held
707
+ ? {
708
+ limit: itemLimit,
709
+ held,
710
+ baseHistory: typeof options.transcriptBaseHistory === 'string' ? options.transcriptBaseHistory : null,
711
+ maxRows: Math.max(1, Math.floor(Number(page.maxRows) || itemLimit)),
712
+ byteBudget: Number(page.byteBudget) > 0 ? Number(page.byteBudget) : null,
713
+ }
714
+ : { limit: itemLimit, skip };
715
+ const sliceKey = cursor
716
+ ? `cursor:${cursor.before ?? ''}:${cursor.after ?? ''}:${cursor.maxRows}:${cursor.byteBudget}`
717
+ : held
718
+ ? `held:${held.count}:${held.firstId}:${held.digest}:${slice.baseHistory}:${slice.maxRows}:${slice.byteBudget}`
719
+ : skip;
519
720
  // Full stamp the loaded text provably came from (unchanged around the
520
721
  // read), or null. Only such a parse may serve other readers.
521
722
  let readStamp = null;
@@ -525,11 +726,27 @@ export async function readStoredSessionTranscript(id, options = {}) {
525
726
  const mode = includeMessages ? 'messages' : 'items';
526
727
  const checkpointAbsent = checkpoint.state === PROBE_ABSENT;
527
728
  // Large, checkpoint-free item projections run off the daemon event loop.
528
- const offloadable = !includeMessages && checkpointAbsent && recordStat.size >= OFFLOAD_MIN_CHARS;
729
+ // A large compaction archive counts: the display history holds its rows.
730
+ const sourceChars = recordStat.size + (archive.state === PROBE_PRESENT ? Number(archive.size) || 0 : 0);
731
+ const offloadable = !includeMessages && checkpointAbsent && sourceChars >= OFFLOAD_MIN_CHARS;
732
+ const fingerprint = [
733
+ checkpoint.state,
734
+ checkpoint.mtimeMs,
735
+ checkpoint.ctimeMs,
736
+ checkpoint.size,
737
+ checkpoint.ino,
738
+ checkpoint.dev,
739
+ archive.state,
740
+ archive.mtimeMs,
741
+ archive.size,
742
+ ].join(':');
529
743
  const produceContext = {
530
744
  sessionId,
531
745
  recordPath,
532
746
  itemLimit,
747
+ slice,
748
+ sourceChars,
749
+ fingerprint,
533
750
  includeMessages,
534
751
  mode,
535
752
  checkpointAbsent,
@@ -537,21 +754,16 @@ export async function readStoredSessionTranscript(id, options = {}) {
537
754
  // How the record read went (the worker's read or loadText below).
538
755
  readState: PROBE_PRESENT,
539
756
  };
540
- const { value, hit, read } = await storedTranscriptCache.read({
541
- key: `${sessionId}|${itemLimit}|${mode}`,
757
+ const { value, hit, read, built } = await storedTranscriptCache.read({
758
+ // One slice (window or page) per request; the group keeps one per
759
+ // session: the display history it is cut from stays with the thread
760
+ // that built it.
761
+ key: `${sessionId}|${itemLimit}|${sliceKey}|${mode}`,
542
762
  // Growing history windows of one session replace each other.
543
763
  group: `${sessionId}|${mode}`,
544
- fingerprint: [
545
- checkpoint.state,
546
- checkpoint.mtimeMs,
547
- checkpoint.ctimeMs,
548
- checkpoint.size,
549
- checkpoint.ino,
550
- checkpoint.dev,
551
- archive.state,
552
- archive.mtimeMs,
553
- archive.size,
554
- ].join(':'),
764
+ // A page read keeps its slice cached under pressure.
765
+ pin: options.paging === true,
766
+ fingerprint,
555
767
  fileStat: recordStat,
556
768
  project: offloadable ? (known) => projectStoredTranscriptElsewhere(known, produceContext) : null,
557
769
  loadText: async () => {
@@ -581,7 +793,8 @@ export async function readStoredSessionTranscript(id, options = {}) {
581
793
  if (typeof options.trace === 'function') {
582
794
  options.trace({
583
795
  sessionId,
584
- hit,
796
+ // A slice of a history already built counts as a hit: nothing was projected.
797
+ hit: hit || built === false,
585
798
  read,
586
799
  ms: performance.now() - startedAt,
587
800
  chars: recordStat.size,