mixdog 1.3.5 → 1.4.1

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 (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -39,6 +39,8 @@ import { join } from 'node:path';
39
39
  import { getPluginData } from '../../config.mjs';
40
40
  import { writeFileAtomicSync } from '../../../../shared/atomic-file.mjs';
41
41
  import { sanitizeContentForStoredHistory } from '../../providers/media-normalization.mjs';
42
+ import { redactSettledText, sensitiveValuesVersion } from '../sensitive-values.mjs';
43
+ import { redactSensitiveContent, redactSensitiveMessage, redactSensitiveToolCall } from '../sensitive-projection.mjs';
42
44
  import { promptContentText } from './prompt-utils.mjs';
43
45
 
44
46
  const TURN_CHECKPOINT_VERSION = 1;
@@ -96,7 +98,7 @@ export function checkpointMessage(message) {
96
98
  return content === message.content ? message : { ...message, content };
97
99
  }
98
100
 
99
- export function matchingUserMessage(message, currentUserContent) {
101
+ function matchingUserMessage(message, currentUserContent) {
100
102
  return (
101
103
  message?.role === 'user' &&
102
104
  (message.content === currentUserContent ||
@@ -131,16 +133,54 @@ export function emptyInterruptionSnapshot() {
131
133
  };
132
134
  }
133
135
 
134
- // ── Header (write-once, atomic) ─────────────────────────────────────────────
136
+ // Observed tool calls ([id, { call, eagerStarted }]) and early tool results
137
+ // ([id, tool message]) as a checkpoint stores them (sensitive-projection.mjs).
138
+ const storedCallEntries = (sessionId, entries) =>
139
+ entries.map(([id, entry]) => {
140
+ const call = redactSensitiveToolCall(sessionId, entry?.call);
141
+ return [id, call === entry?.call ? entry : { ...entry, call }];
142
+ });
143
+ const storedResultEntries = (sessionId, entries) =>
144
+ entries.map(([id, message]) => [id, redactSensitiveMessage(sessionId, message)]);
145
+
146
+ /** Interruption snapshot `snapshot` as `sessionId`'s checkpoint stores it:
147
+ * sensitive values (sensitive-values.mjs) replaced, and of the partial text
148
+ * only what no value can still complete. */
149
+ function redactInterruptionSnapshot(sessionId, snapshot) {
150
+ const source = snapshot ?? emptyInterruptionSnapshot();
151
+ if (!sensitiveValuesVersion(sessionId)) return source;
152
+ return {
153
+ ...source,
154
+ partialAssistantContent: redactSettledText(sessionId, String(source.partialAssistantContent || '')),
155
+ partialReasoningContent: redactSettledText(sessionId, String(source.partialReasoningContent || '')),
156
+ observedToolCalls: storedCallEntries(
157
+ sessionId,
158
+ Array.isArray(source.observedToolCalls) ? source.observedToolCalls : []
159
+ ),
160
+ observedToolResults: storedResultEntries(
161
+ sessionId,
162
+ Array.isArray(source.observedToolResults) ? source.observedToolResults : []
163
+ ),
164
+ };
165
+ }
166
+
167
+ // ── Header (written once per turn, again when a sensitive value is marked) ──
135
168
 
136
169
  export function writeCheckpointHeader(checkpoint) {
137
- const target = turnCheckpointPath(checkpoint.sessionId);
170
+ const { sessionId } = checkpoint;
171
+ const target = turnCheckpointPath(sessionId);
138
172
  const payload = {
139
173
  ...checkpoint,
140
174
  version: TURN_CHECKPOINT_VERSION,
141
- currentUserContent: sanitizeContentForStoredHistory(checkpoint.currentUserContent),
142
- turnMessages: (Array.isArray(checkpoint.turnMessages) ? checkpoint.turnMessages : []).map(checkpointMessage),
143
- interruption: checkpoint.interruption ?? emptyInterruptionSnapshot(),
175
+ currentUserContent: redactSensitiveContent(
176
+ sessionId,
177
+ sanitizeContentForStoredHistory(checkpoint.currentUserContent)
178
+ ),
179
+ turnMessages: (Array.isArray(checkpoint.turnMessages) ? checkpoint.turnMessages : []).map((message) =>
180
+ redactSensitiveMessage(sessionId, checkpointMessage(message))
181
+ ),
182
+ interruption: redactInterruptionSnapshot(sessionId, checkpoint.interruption),
183
+ // Counts, token readings and signatures only: no text.
144
184
  contextState: checkpoint.contextState ?? null,
145
185
  updatedAt: Date.now(),
146
186
  };
@@ -488,7 +528,15 @@ export function replayTurnJournal(header) {
488
528
  if (seq > expected) break;
489
529
  expected = seq + 1;
490
530
  if (!head) {
491
- if (record.t !== 'h' || record.turnToken !== header.turnToken) return header;
531
+ // A header rewritten for a newly marked sensitive value starts a new
532
+ // journal epoch; the journal it replaces never replays onto it.
533
+ if (
534
+ record.t !== 'h' ||
535
+ record.turnToken !== header.turnToken ||
536
+ (Number(record.e) || 0) !== (Number(header.journalEpoch) || 0)
537
+ ) {
538
+ return header;
539
+ }
492
540
  head = true;
493
541
  continue;
494
542
  }
@@ -529,7 +577,7 @@ export function replayTurnJournal(header) {
529
577
  * turn-relative index) plus an opaque interruption cursor, so each encode()
530
578
  * touches only what changed since the previous flush.
531
579
  */
532
- export function createTurnJournalEncoder({ turnToken } = {}) {
580
+ export function createTurnJournalEncoder({ turnToken, sessionId = null } = {}) {
533
581
  let journaled = [];
534
582
  let fullTranscript = false;
535
583
  let cursor = null;
@@ -537,27 +585,65 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
537
585
  // Per-turn record sequence. It is assigned where records are produced, so
538
586
  // the number line is exactly the enqueue order the write lane preserves.
539
587
  let seq = 0;
588
+ // Set while the session has sensitive values (sensitive-values.mjs): the
589
+ // tracker's state as its deltas build it (`raw`) and the state the journal
590
+ // replays to (`written`). Interruption deltas are then journaled as the
591
+ // difference of the redacted text, so a value that streams in across
592
+ // flushes never lands in pieces. A newly marked value reseeds the encoder
593
+ // (turn-checkpoint.mjs), so the session's values are known whenever this is null.
594
+ let mirror = null;
540
595
  const line = (record) => {
541
596
  const stamped = { ...record, s: seq };
542
597
  seq += 1;
543
598
  return `${JSON.stringify(stamped)}\n`;
544
599
  };
600
+ const stored = (message) => redactSensitiveMessage(sessionId, checkpointMessage(message));
601
+ const interruptionRecord = (delta) => {
602
+ if (!mirror) return delta;
603
+ applyInterruptionDelta(mirror.raw, delta);
604
+ const out = {};
605
+ for (const key of ['rs', 'ph', 'cc', 'od']) if (Object.hasOwn(delta, key)) out[key] = delta[key];
606
+ for (const [field, setKey, appendKey] of [
607
+ ['text', 'ts', 'ta'],
608
+ ['reasoning', 'qs', 'qa'],
609
+ ]) {
610
+ const settled = redactSettledText(sessionId, mirror.raw[field]);
611
+ const written = mirror.written[field];
612
+ if (settled === written) continue;
613
+ if (settled.startsWith(written)) out[appendKey] = settled.slice(written.length);
614
+ else out[setKey] = settled;
615
+ }
616
+ if (Object.hasOwn(delta, 'tb')) out.tb = redactSensitiveContent(sessionId, mirror.raw.tomb);
617
+ if (delta.cs) out.cs = storedCallEntries(sessionId, delta.cs);
618
+ if (delta.os) out.os = storedResultEntries(sessionId, delta.os);
619
+ applyInterruptionDelta(mirror.written, out);
620
+ return out;
621
+ };
545
622
  return {
546
623
  /** Seed from the turn's opening state; returns the header's messages
547
624
  * and the journal's opening lines (head record, plus — defensively —
548
625
  * the delta that makes the journal authoritative if the tracker already
549
- * carried state at open time). */
550
- seed(turnOutgoing, currentUserContent, interruption, contextState = null) {
626
+ * carried state at open time). `snapshot` is the tracker snapshot the
627
+ * header stores; `epoch` names the journal a rewritten header starts. */
628
+ seed(turnOutgoing, currentUserContent, interruption, contextState = null, { snapshot = null, epoch = 0 } = {}) {
551
629
  const source = Array.isArray(turnOutgoing) ? turnOutgoing : [];
552
630
  const start = findTurnStart(source, currentUserContent);
553
631
  fullTranscript = start < 0;
554
632
  journaled = fullTranscript ? source.slice() : source.slice(start);
633
+ mirror = sensitiveValuesVersion(sessionId)
634
+ ? {
635
+ raw: interruptionStateFromSnapshot(snapshot),
636
+ written: interruptionStateFromSnapshot(redactInterruptionSnapshot(sessionId, snapshot)),
637
+ }
638
+ : null;
555
639
  const seeded = typeof interruption?.journalDelta === 'function' ? interruption.journalDelta(null) : null;
556
640
  cursor = seeded ? seeded.cursor : null;
557
641
  contextStateKey = contextState == null ? null : JSON.stringify(contextState);
558
642
  seq = 0;
559
- const openLines = [line({ t: 'h', turnToken, at: Date.now() })];
560
- if (seeded?.changed) openLines.push(line({ t: 'x', d: seeded.delta, at: Date.now() }));
643
+ const openLines = [line({ t: 'h', turnToken, ...(epoch ? { e: epoch } : {}), at: Date.now() })];
644
+ const seededDelta = seeded?.changed ? interruptionRecord(seeded.delta) : null;
645
+ if (seededDelta && Object.keys(seededDelta).length)
646
+ openLines.push(line({ t: 'x', d: seededDelta, at: Date.now() }));
561
647
  return {
562
648
  turnMessages: journaled.map(checkpointMessage),
563
649
  fullTranscript,
@@ -579,7 +665,7 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
579
665
  journaled = source.slice();
580
666
  records.push({
581
667
  t: 'full',
582
- ms: journaled.map(checkpointMessage),
668
+ ms: journaled.map(stored),
583
669
  });
584
670
  } else {
585
671
  const offset = fullTranscript ? 0 : start;
@@ -588,7 +674,7 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
588
674
  const raw = source[offset + index];
589
675
  if (journaled[index] === raw) continue;
590
676
  journaled[index] = raw;
591
- records.push({ t: 'm', i: index, m: checkpointMessage(raw) });
677
+ records.push({ t: 'm', i: index, m: stored(raw) });
592
678
  }
593
679
  if (journaled.length > length) {
594
680
  journaled.length = length;
@@ -598,7 +684,8 @@ export function createTurnJournalEncoder({ turnToken } = {}) {
598
684
  if (typeof interruption?.journalDelta === 'function') {
599
685
  const next = interruption.journalDelta(cursor);
600
686
  cursor = next.cursor;
601
- if (next.changed) records.push({ t: 'x', d: next.delta });
687
+ const delta = next.changed ? interruptionRecord(next.delta) : null;
688
+ if (delta && Object.keys(delta).length) records.push({ t: 'x', d: delta });
602
689
  }
603
690
  const nextContextStateKey = contextState == null ? null : JSON.stringify(contextState);
604
691
  if (nextContextStateKey !== contextStateKey) {
@@ -19,6 +19,7 @@ import {
19
19
  } from './turn-checkpoint-journal.mjs';
20
20
  import { captureTurnCheckpointContextState, restoreTurnCheckpointContextState } from './turn-checkpoint-context.mjs';
21
21
  import { stripProviderReplayMessages } from './provider-replay.mjs';
22
+ import { sensitiveValuesVersion } from '../sensitive-values.mjs';
22
23
 
23
24
  export { turnCheckpointPath, turnMessagesForCheckpoint };
24
25
  export { captureTurnCheckpointContextState, restoreTurnCheckpointContextState };
@@ -67,9 +68,14 @@ export function projectTurnCheckpointMessages(session, checkpoint) {
67
68
  // long tool-heavy turn no longer amplifies into the shared engine thread that
68
69
  // every fanned-out agent session shares.
69
70
  export function createTurnCheckpointRecorder({ sessionId, generation, turnToken, startedAt }) {
70
- const encoder = createTurnJournalEncoder({ turnToken });
71
+ const encoder = createTurnJournalEncoder({ turnToken, sessionId });
71
72
  let opened = false;
72
73
  let stopped = false;
74
+ // The sensitive-values version (sensitive-values.mjs) the header and
75
+ // journal were written under. A newly marked value rewrites both from the
76
+ // turn's current state with the value replaced, under a new journal epoch,
77
+ // so no recovery record keeps it.
78
+ let openedVersion = 0;
73
79
  return {
74
80
  get opened() {
75
81
  return opened;
@@ -77,8 +83,14 @@ export function createTurnCheckpointRecorder({ sessionId, generation, turnToken,
77
83
  /** True when this call wrote or queued durable state. */
78
84
  record({ currentUserContent, turnOutgoing, interruption, contextState = null }) {
79
85
  if (stopped || !sessionId || !turnToken) return false;
80
- if (!opened) {
81
- const seed = encoder.seed(turnOutgoing, currentUserContent, interruption, contextState);
86
+ const version = sensitiveValuesVersion(sessionId);
87
+ if (!opened || version !== openedVersion) {
88
+ const snapshot =
89
+ typeof interruption?.snapshot === 'function' ? interruption.snapshot() : emptyInterruptionSnapshot();
90
+ const seed = encoder.seed(turnOutgoing, currentUserContent, interruption, contextState, {
91
+ snapshot,
92
+ epoch: version,
93
+ });
82
94
  writeCheckpointHeader({
83
95
  sessionId,
84
96
  generation,
@@ -87,10 +99,11 @@ export function createTurnCheckpointRecorder({ sessionId, generation, turnToken,
87
99
  currentUserContent,
88
100
  turnMessages: seed.turnMessages,
89
101
  ...(seed.fullTranscript ? { fullTranscript: true } : {}),
90
- interruption:
91
- typeof interruption?.snapshot === 'function' ? interruption.snapshot() : emptyInterruptionSnapshot(),
102
+ ...(version ? { journalEpoch: version } : {}),
103
+ interruption: snapshot,
92
104
  contextState: seed.contextState,
93
105
  });
106
+ openedVersion = version;
94
107
  // Opening the journal is part of the anchor: a leftover journal
95
108
  // from an earlier turn of this session must never replay onto
96
109
  // the new header. It lands synchronously when the lane is idle
@@ -211,6 +224,9 @@ export function recoverTurnCheckpoint(session) {
211
224
  // mark it unanchored so the first resumed send cannot compact from a gross
212
225
  // whole-transcript estimate.
213
226
  const contextStateRestored = restoreTurnCheckpointContextState(session, checkpoint);
227
+ // Diagnostic only (anthropic-prefix-trace.mjs): the turn's last settled
228
+ // request, whatever the context reading's alignment.
229
+ if (checkpoint.contextState?.prefixBaseline) session.providerPrefixBaseline = checkpoint.contextState.prefixBaseline;
214
230
  delete session.activeTurnCheckpoint;
215
231
  delete session.providerState;
216
232
  delete session._providerPrefixGuardState;
@@ -110,6 +110,15 @@ function finalizeInterruptedTurn({
110
110
  };
111
111
  }
112
112
 
113
+ // Killed with a request on the wire (phase 'streaming' after a tool run):
114
+ // the provider received the transcript up to its last message, and may
115
+ // have cached it. Stamping that message keeps what this interruption
116
+ // appends (the marker, the next prompt) out of the tool_result it ends —
117
+ // see toAnthropicMessages. The stamp is meta only; no wire bytes change.
118
+ const sentTail = messages.length - 1;
119
+ if (phase === 'streaming' && (messages[sentTail]?.role === 'tool' || messages[sentTail]?.role === 'user')) {
120
+ messages[sentTail] = { ...messages[sentTail], meta: { ...messages[sentTail].meta, sentRequestTail: true } };
121
+ }
113
122
  appendObservedTurnOutput(messages, {
114
123
  partialAssistantContent,
115
124
  partialReasoningContent,
@@ -30,7 +30,7 @@ import { traceAgentCompact, messagePrefixHash } from '../agent-trace.mjs';
30
30
  import { invalidateProviderRequestToolsScope } from '../runtime-core/provider-request-tools.mjs';
31
31
  import { bumpUsageMetricsEpoch } from './manager/usage-metrics.mjs';
32
32
  import { resetReadStateAfterCompaction } from './read-dedup.mjs';
33
- import { scheduleCompactionArchive, stampCompactionMessageIds } from './compaction-archive-capture.mjs';
33
+ import { prepareCompactionArchive, stampCompactionMessageIds } from './compaction-archive-capture.mjs';
34
34
  import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
35
35
  import { isAgentModeSession } from './agent-mode.mjs';
36
36
  import { applyPendingWorkspaceReroot } from './manager/prompt-utils.mjs';
@@ -92,7 +92,11 @@ function preSendCompactDecision(state, compactPolicy) {
92
92
  messages,
93
93
  sessionRef,
94
94
  });
95
- const cacheExpired = shouldCompactForExpiredAgentCache(sessionRef, state.opts);
95
+ const cacheExpired = shouldCompactForExpiredAgentCache(sessionRef, state.opts, {
96
+ provider: sessionRef?.provider || state.provider?.name,
97
+ model: state.model,
98
+ messages,
99
+ });
96
100
  const mediaPressure = shouldCompactForRequestMedia(messages, sessionRef?.provider);
97
101
  const requireReduction = !reactivePending && !mediaPressure;
98
102
  let shouldCompact =
@@ -409,19 +413,31 @@ function adoptCompactedTranscript(ctx, run) {
409
413
  const { messages, sessionRef, sessionId, opts } = state;
410
414
  const replaced = messages.slice();
411
415
  if (sessionRef) backfillFallbackSessionTitle(sessionRef, replaced);
416
+ // Display-only archive of the dropped conversation, made durable (pending
417
+ // sidecar) BEFORE the shortened transcript is adopted and can be saved; a
418
+ // sidecar that cannot be written keeps the old transcript and fails this
419
+ // compaction. The publication runs off this synchronous adoption.
420
+ let prepared = null;
421
+ if (sessionRef) {
422
+ try {
423
+ prepared = prepareCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: run.compacted });
424
+ } catch (archiveError) {
425
+ throwCompactFailure(ctx, run, archiveError);
426
+ }
427
+ }
412
428
  messages.length = 0;
413
429
  messages.push(...run.compacted);
414
- // Display-only archive of the dropped conversation; the projection and the
415
- // write run off this synchronous adoption.
416
- const archived = sessionRef
417
- ? scheduleCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: messages })
418
- : null;
430
+ const archived = prepared ? { ...prepared, written: prepared.start() } : null;
419
431
  ctx.droppedMessages = archived?.dropped ?? 0;
420
432
  ctx.droppedIdentities = archived?.identities ?? null;
421
433
  acknowledgeGoalReminder(sessionRef, run);
422
434
  // The compacted transcript breaks the cache anyway: a staged working-folder
423
435
  // change re-roots the system prompt now and needs no notice any more.
424
436
  if (sessionRef) applyPendingWorkspaceReroot(sessionRef, messages);
437
+ // The system prompt is rebuilt from current sources outside a running turn
438
+ // (session-runtime agent-profile-fold.mjs): the next cold point folds it in
439
+ // instead of adding a break to the turn that is already warm again.
440
+ if (sessionRef) sessionRef.promptFoldPending = true;
425
441
  resetReadStateAfterCompaction(sessionId);
426
442
  // Cross-turn receipts reference tool results that compaction just removed
427
443
  // from context; a stub for them would point at nothing. Clear in place so
@@ -444,8 +460,7 @@ function adoptCompactedTranscript(ctx, run) {
444
460
  ctx.providerStateCleared = true;
445
461
  // Compaction starts a new loop epoch: the iteration counter restarts at 0
446
462
  // so iteration indices (traces, usage metrics, cross-turn dedup windows)
447
- // count from the compacted transcript. The counter bounds nothing — the
448
- // loop has no iteration cap.
463
+ // count from the compacted transcript.
449
464
  ctx.iterations = 0;
450
465
  // New loop epoch so persistIterationMetrics idempotency keys do not
451
466
  // collide when iteration indices restart at 1 (incl. iter 1 → iter 1).
@@ -0,0 +1,113 @@
1
+ // prompt-cache-idle-gaps.mjs — per session, the idle gaps the cache warmer
2
+ // learns from (cache-warm.mjs): from the send of the session's last real
3
+ // request (when it used the cache) to its next real request. Kept beside the
4
+ // prompt-cache touch store, never in the session record. A gap is kept as
5
+ // `{ at, ms }`, or `{ at, ms, censored: true }` when the session was still
6
+ // idle at the window it was cut at. The session's open gap (`open`: its
7
+ // start) survives a restart. Gaps older than the lookback are pruned on
8
+ // write; a deleted session's entry is dropped.
9
+ import { join } from 'node:path';
10
+ import { readJsonStateSync, writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
11
+ import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
12
+
13
+ const PROMPT_CACHE_IDLE_GAP_FILE = 'prompt-cache-idle-gaps.json';
14
+
15
+ const validGap = (gap) => gap && Number(gap.at) > 0 && Number(gap.ms) > 0;
16
+
17
+ /**
18
+ * An idle-gap store backed by `path` (default: the data dir's file).
19
+ * `lookbackMs`: how long gaps are kept; `windowMs`: a gap reaching it is
20
+ * recorded as censored there; `minMs`: shorter gaps are not kept (no
21
+ * refresh point lies inside them).
22
+ */
23
+ export function createIdleGapStore({
24
+ path = () => join(resolvePluginData(), PROMPT_CACHE_IDLE_GAP_FILE),
25
+ lookbackMs,
26
+ windowMs,
27
+ minMs,
28
+ now = () => Date.now(),
29
+ log = (line) => process.stderr.write(`[cache-warm] ${line}\n`),
30
+ } = {}) {
31
+ let sessions = null;
32
+ let writing = Promise.resolve();
33
+ const filePath = () => (typeof path === 'function' ? path() : path);
34
+ const load = () => {
35
+ if (sessions) return sessions;
36
+ sessions = new Map();
37
+ const stored = readJsonStateSync(filePath(), { fallback: null });
38
+ for (const [sessionId, entry] of Object.entries(stored?.sessions || {})) {
39
+ const gaps = Array.isArray(entry?.gaps)
40
+ ? entry.gaps.filter(validGap).map((gap) => {
41
+ const item = { at: Number(gap.at), ms: Number(gap.ms) };
42
+ if (gap.censored) item.censored = true;
43
+ return item;
44
+ })
45
+ : [];
46
+ const open = Number(entry?.open) > 0 ? Number(entry.open) : 0;
47
+ if (gaps.length || open) sessions.set(sessionId, { open, gaps });
48
+ }
49
+ return sessions;
50
+ };
51
+ const entryOf = (sessionId) => {
52
+ const id = String(sessionId || '');
53
+ if (!load().has(id)) sessions.set(id, { open: 0, gaps: [] });
54
+ return sessions.get(id);
55
+ };
56
+ const flush = () => {
57
+ const since = now() - lookbackMs;
58
+ const out = {};
59
+ for (const [sessionId, entry] of load()) {
60
+ entry.gaps = entry.gaps.filter((gap) => gap.at >= since);
61
+ if (entry.open && entry.open < since) entry.open = 0;
62
+ if (!entry.gaps.length && !entry.open) sessions.delete(sessionId);
63
+ else out[sessionId] = { ...(entry.open ? { open: entry.open } : {}), gaps: entry.gaps };
64
+ }
65
+ writing = writing
66
+ .catch(() => {})
67
+ .then(() => writeJsonAtomicAsync(filePath(), { version: 1, sessions: out }, { compact: true }));
68
+ return writing;
69
+ };
70
+ // A failed write is reported; the next flush recovers the queue and writes.
71
+ const scheduleFlush = () =>
72
+ void flush().catch((err) => log(`idle-gap store write failed (${filePath()}): ${err?.message || err}`));
73
+ return {
74
+ /** The session went idle: its last real request used the cache at `at`. */
75
+ open(sessionId, at) {
76
+ if (!sessionId || !(Number(at) > 0)) return;
77
+ entryOf(sessionId).open = Number(at);
78
+ scheduleFlush();
79
+ },
80
+ /** A real request arrived at `at`: close the open gap as a return, or as
81
+ * censored at the window when it outlasted it. Resolves the kept gap or
82
+ * null (none open, or too short to keep). */
83
+ close(sessionId, at) {
84
+ const entry = sessionId ? load().get(String(sessionId)) : null;
85
+ if (!entry?.open) return null;
86
+ const start = entry.open;
87
+ entry.open = 0;
88
+ const ms = Number(at) - start;
89
+ let gap = null;
90
+ if (ms >= windowMs) gap = { at: start, ms: windowMs, censored: true };
91
+ else if (ms > minMs) gap = { at: start, ms };
92
+ if (gap) entry.gaps.push(gap);
93
+ scheduleFlush();
94
+ return gap;
95
+ },
96
+ /** The session's kept gaps of the lookback (oldest first). */
97
+ gaps(sessionId) {
98
+ const since = now() - lookbackMs;
99
+ const entry = sessionId ? load().get(String(sessionId)) : null;
100
+ return (entry?.gaps || []).filter((gap) => gap.at >= since).map((gap) => ({ ...gap }));
101
+ },
102
+ /** When the session's open gap started, or 0. */
103
+ openAt(sessionId) {
104
+ return (sessionId && load().get(String(sessionId))?.open) || 0;
105
+ },
106
+ /** The session is gone (deleted): drop everything it learned. */
107
+ forget(sessionId) {
108
+ if (sessionId && load().delete(String(sessionId))) scheduleFlush();
109
+ },
110
+ /** Write pending changes now (tests, shutdown). */
111
+ flush,
112
+ };
113
+ }
@@ -0,0 +1,150 @@
1
+ // prompt-cache-touch-store.mjs — the durable record cache warms re-arm from
2
+ // after a restart (cache-warm.mjs): per session, the last confirmed touch of
3
+ // its cached prefix — when a call whose usage showed a read or write on it was
4
+ // sent, the tier the provider reported writing, the account that holds it and
5
+ // the prompt size. Kept beside the session records, never in them: a warm's
6
+ // touch must not move a session's own timestamps (activity, autoClear).
7
+ // Expired entries are dropped on write.
8
+ //
9
+ // Beside the touches: per session, when a warm last used the cache
10
+ // successfully (a read with no write). The idle auto-clear measures idleness
11
+ // from it (idle-auto-clear-gate.mjs) until a real request settles, so it
12
+ // outlives the touch: a later miss or an expiry drops the touch, not the use.
13
+ import { join } from 'node:path';
14
+ import { readJsonStateSync, writeJsonAtomicAsync } from '../../../shared/atomic-file.mjs';
15
+ import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
16
+
17
+ const PROMPT_CACHE_TOUCH_FILE = 'prompt-cache-touches.json';
18
+
19
+ function validTouch(value) {
20
+ return (
21
+ value &&
22
+ typeof value === 'object' &&
23
+ Number(value.touchAt) > 0 &&
24
+ Number(value.ttlMs) > 0 &&
25
+ Number(value.promptTokens) > 0
26
+ );
27
+ }
28
+
29
+ // A warm use as stored: `{ at, provider, model, accountId }` — the route and
30
+ // account whose cached prefix the warm read. A bare time (written before the
31
+ // identity was recorded) keeps its time and matches no route.
32
+ function warmUseRecord(value) {
33
+ const record = value && typeof value === 'object' ? value : { at: value };
34
+ if (!(Number(record.at) > 0)) return null;
35
+ return {
36
+ at: Number(record.at),
37
+ provider: String(record.provider || ''),
38
+ model: String(record.model || ''),
39
+ accountId: String(record.accountId || ''),
40
+ };
41
+ }
42
+
43
+ /** A touch store backed by `path` (default: the data dir's file). */
44
+ export function createPromptCacheTouchStore(
45
+ path = () => join(resolvePluginData(), PROMPT_CACHE_TOUCH_FILE),
46
+ { log = (line) => process.stderr.write(`[cache-warm] ${line}\n`) } = {}
47
+ ) {
48
+ let touches = null;
49
+ let warmUses = null;
50
+ let writing = Promise.resolve();
51
+ const filePath = () => (typeof path === 'function' ? path() : path);
52
+ const load = () => {
53
+ if (touches) return touches;
54
+ touches = new Map();
55
+ warmUses = new Map();
56
+ const stored = readJsonStateSync(filePath(), { fallback: null });
57
+ for (const [sessionId, touch] of Object.entries(stored?.touches || {})) {
58
+ if (validTouch(touch)) touches.set(sessionId, { ...touch });
59
+ }
60
+ for (const [sessionId, use] of Object.entries(stored?.warmUses || {})) {
61
+ const record = warmUseRecord(use);
62
+ if (record) warmUses.set(sessionId, record);
63
+ }
64
+ return touches;
65
+ };
66
+ const uses = () => {
67
+ load();
68
+ return warmUses;
69
+ };
70
+ // Touches change once per real request or warm: each change is written at
71
+ // once, the writes serialized.
72
+ const flush = () => {
73
+ const now = Date.now();
74
+ const out = {};
75
+ for (const [sessionId, touch] of load()) {
76
+ if (touch.touchAt + touch.ttlMs > now) out[sessionId] = touch;
77
+ else touches.delete(sessionId);
78
+ }
79
+ writing = writing
80
+ .catch(() => {})
81
+ .then(() =>
82
+ writeJsonAtomicAsync(
83
+ filePath(),
84
+ { version: 1, touches: out, warmUses: Object.fromEntries(uses()) },
85
+ { compact: true }
86
+ )
87
+ );
88
+ return writing;
89
+ };
90
+ // A failed write is reported; the next flush recovers the queue and writes.
91
+ const scheduleFlush = () =>
92
+ void flush().catch((err) => log(`touch store write failed (${filePath()}): ${err?.message || err}`));
93
+ return {
94
+ /** The session's last confirmed touch, or null. */
95
+ get(sessionId) {
96
+ const touch = load().get(String(sessionId || ''));
97
+ return touch ? { ...touch } : null;
98
+ },
99
+ /** The sessions holding a touch. */
100
+ ids() {
101
+ return [...load().keys()];
102
+ },
103
+ /** Record a confirmed touch `{ touchAt, ttlMs, accountId, promptTokens }`. */
104
+ record(sessionId, touch) {
105
+ if (!sessionId || !validTouch(touch)) return;
106
+ load().set(String(sessionId), {
107
+ touchAt: Number(touch.touchAt),
108
+ ttlMs: Number(touch.ttlMs),
109
+ promptTokens: Number(touch.promptTokens),
110
+ ...(touch.accountId ? { accountId: String(touch.accountId) } : {}),
111
+ });
112
+ scheduleFlush();
113
+ },
114
+ /** The session's cached prefix is gone (compaction, clear, close, miss). */
115
+ forget(sessionId) {
116
+ if (load().delete(String(sessionId || ''))) scheduleFlush();
117
+ },
118
+ /** When a warm last used the session's cache successfully, or 0. */
119
+ lastWarmUse(sessionId) {
120
+ return uses().get(String(sessionId || ''))?.at || 0;
121
+ },
122
+ /** The session's last successful warm use `{ at, provider, model,
123
+ * accountId }`, or null. */
124
+ warmUse(sessionId) {
125
+ const use = uses().get(String(sessionId || ''));
126
+ return use ? { ...use } : null;
127
+ },
128
+ /** A warm read the session's cache without writing at `at`, on the
129
+ * route and account `{ provider, model, accountId }`. */
130
+ recordWarmUse(sessionId, at, identity = {}) {
131
+ const record = warmUseRecord({ ...identity, at });
132
+ if (!sessionId || !record) return;
133
+ uses().set(String(sessionId), record);
134
+ scheduleFlush();
135
+ },
136
+ /** The session's own activity supersedes its warm use (a real request
137
+ * settled) or its cached conversation is gone (compaction, clear). */
138
+ forgetWarmUse(sessionId) {
139
+ if (uses().delete(String(sessionId || ''))) scheduleFlush();
140
+ },
141
+ /** Write pending changes now (tests, shutdown). */
142
+ flush,
143
+ };
144
+ }
145
+
146
+ let defaultStore = null;
147
+ export function promptCacheTouchStore() {
148
+ defaultStore ??= createPromptCacheTouchStore();
149
+ return defaultStore;
150
+ }