mixdog 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/daemon-stub.mjs +1 -1
  5. package/scripts/gate-local.mjs +4 -2
  6. package/scripts/native-release-status.mjs +2 -2
  7. package/scripts/recall-history-cases.json +1 -1
  8. package/scripts/suite-health.mjs +10 -1
  9. package/scripts/test.mjs +7 -11
  10. package/src/agents/reviewer/AGENT.md +7 -11
  11. package/src/agents/reviewer/agent.json +1 -1
  12. package/src/defaults/shipped-history.json +17 -1
  13. package/src/defaults/skills/browser-use/SKILL.md +59 -11
  14. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  15. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  16. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  17. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  18. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  19. package/src/defaults/skills/setup/SKILL.md +22 -0
  20. package/src/defaults/skills/setup/references/actions.md +39 -5
  21. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  22. package/src/defaults/skills/visualize/SKILL.md +2 -1
  23. package/src/lib/rules-builder.cjs +10 -8
  24. package/src/rules/agent/AGENT.md +6 -6
  25. package/src/rules/lead/LEAD.md +18 -20
  26. package/src/rules/shared/05-tool-calls.md +5 -6
  27. package/src/rules/shared/10-tool-workflow.md +2 -0
  28. package/src/rules/shared/60-verification.md +7 -5
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  30. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  31. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  32. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  33. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  35. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  36. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  39. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
  40. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  41. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  42. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  45. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +145 -66
  46. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  47. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  48. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  49. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  50. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  52. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
  53. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  54. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  55. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  56. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  57. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  58. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  64. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  65. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +26 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  68. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  69. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  71. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  72. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  73. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  76. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  77. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
  78. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  79. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  80. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  82. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  83. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  84. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  86. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  87. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  88. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  90. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -2
  91. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +3 -2
  93. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  94. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  97. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  98. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  99. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  100. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  101. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  102. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  103. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  104. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  105. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  106. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  107. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  108. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  109. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  110. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  112. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  113. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  114. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  115. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  116. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  117. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  118. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  120. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  121. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  122. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  123. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  124. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  125. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  126. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  127. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  128. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  129. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  130. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  131. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  132. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  133. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  135. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +18 -1
  136. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  137. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  138. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  141. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  143. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  144. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  145. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  146. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  147. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  148. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  149. package/src/runtime/browser-bridge/client.mjs +73 -8
  150. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  151. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  152. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  153. package/src/runtime/channels/lib/config.mjs +6 -61
  154. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  155. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  156. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  157. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  158. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  159. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  160. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  161. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  162. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  163. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  164. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  165. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  166. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  167. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  168. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  169. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  170. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  171. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  172. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  173. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  174. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  175. package/src/runtime/computer-bridge/actions.mjs +9 -1
  176. package/src/runtime/computer-bridge/client.mjs +180 -8
  177. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  178. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  179. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  180. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  181. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  182. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  183. package/src/runtime/connectors/google-mcp.mjs +10 -12
  184. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  185. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  186. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  187. package/src/runtime/session-search/session-search-db.mjs +73 -9
  188. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  189. package/src/runtime/shared/abort-race.mjs +89 -0
  190. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  191. package/src/runtime/shared/agent-kind.mjs +4 -3
  192. package/src/runtime/shared/bounded-download.mjs +8 -3
  193. package/src/runtime/shared/config.mjs +2 -13
  194. package/src/runtime/shared/connector-accounts.mjs +20 -10
  195. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  196. package/src/runtime/shared/connector-catalog.mjs +150 -0
  197. package/src/runtime/shared/connector-triggers.mjs +22 -19
  198. package/src/runtime/shared/defaults-separation.mjs +2 -14
  199. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  200. package/src/runtime/shared/err-text.mjs +24 -15
  201. package/src/runtime/shared/file-permissions.d.mts +0 -8
  202. package/src/runtime/shared/file-permissions.mjs +0 -11
  203. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  204. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  205. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  206. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  207. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  208. package/src/runtime/shared/mixy-presence.mjs +22 -0
  209. package/src/runtime/shared/orchestration.d.mts +2 -0
  210. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  211. package/src/runtime/shared/profile-config.cjs +1 -1
  212. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  213. package/src/runtime/shared/prompt-origin.mjs +21 -0
  214. package/src/runtime/shared/provider-accounts.mjs +7 -1
  215. package/src/runtime/shared/provider-display-name.mjs +29 -0
  216. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  217. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  218. package/src/runtime/shared/routine-trigger.mjs +10 -0
  219. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  220. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  221. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  222. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  223. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  224. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  225. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  226. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  227. package/src/runtime/terminal-bridge/client.mjs +10 -1
  228. package/src/runtime/visualize-bridge/client.mjs +27 -8
  229. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  230. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  231. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  232. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  233. package/src/session-runtime/agent-home-delivery.mjs +448 -185
  234. package/src/session-runtime/agent-task-sessions.mjs +329 -180
  235. package/src/session-runtime/boot/apis.mjs +9 -0
  236. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  237. package/src/session-runtime/boot/providers.mjs +3 -0
  238. package/src/session-runtime/boot/tools.mjs +6 -1
  239. package/src/session-runtime/config-lifecycle.mjs +6 -0
  240. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  241. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  242. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  243. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  244. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  245. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  246. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  247. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  248. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  249. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  250. package/src/session-runtime/notification-bus.mjs +31 -117
  251. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  252. package/src/session-runtime/notification-listeners.mjs +76 -0
  253. package/src/session-runtime/owned-automation-run.mjs +2 -1
  254. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  255. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  256. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  257. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  258. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  259. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  260. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  261. package/src/session-runtime/routine-owner.mjs +40 -9
  262. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  263. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  264. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  265. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  266. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  267. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  268. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  269. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  270. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  271. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  272. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  273. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  274. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  275. package/src/session-runtime/services/channel-admin.mjs +7 -13
  276. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  277. package/src/session-runtime/services/seeds.mjs +0 -28
  278. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  279. package/src/session-runtime/session-create/session-options.mjs +1 -0
  280. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  281. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  282. package/src/session-runtime/settings-system-api.mjs +13 -1
  283. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  284. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  285. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  286. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  287. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  288. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  289. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  290. package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
  291. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  292. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  293. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  294. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  295. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  296. package/src/standalone/channel-transport.mjs +1 -1
  297. package/src/standalone/daemon-channels-loader.mjs +10 -8
  298. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  299. package/src/standalone/daemon-session-control/request.mjs +51 -0
  300. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  301. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  302. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  303. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  304. package/src/standalone/daemon-session-control.mjs +124 -188
  305. package/src/standalone/daemon.mjs +26 -3
  306. package/src/standalone/session-client.mjs +7 -3
  307. package/src/standalone/session-protocol.mjs +5 -0
  308. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  309. package/src/standalone/session-service/quota-resume.mjs +3 -0
  310. package/src/standalone/session-service/retention/busy.mjs +65 -1
  311. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  312. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  313. package/src/standalone/session-tool-output.mjs +15 -7
  314. package/src/tui/app/model-options.mjs +3 -28
  315. package/src/tui/dist/index.mjs +58 -64
  316. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  317. package/src/tui/session/initial-state.mjs +3 -0
  318. package/src/tui/session/notice-surface.mjs +7 -1
  319. package/src/tui/session/queue-helpers.mjs +3 -42
  320. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  321. package/src/tui/session/session-api/routes-write.mjs +5 -1
  322. package/src/tui/session/session-api/routes.mjs +1 -0
  323. package/src/tui/session/session-api/settings.mjs +17 -1
  324. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  325. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  326. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  327. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  328. package/src/tui/session/session-flow/drain.mjs +118 -149
  329. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  330. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  331. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  332. package/src/tui/session/tool-approval-request.mjs +39 -0
  333. package/src/tui/session/tool-approval-settle.mjs +62 -0
  334. package/src/tui/session/tool-approval.mjs +69 -115
  335. package/src/tui/session/tui-steering-persist.mjs +8 -1
  336. package/src/tui/session/turn-stream.mjs +19 -2
  337. package/src/tui/session/turn.mjs +1 -0
  338. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  339. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  340. package/scripts/code-graph-description-contract.mjs +0 -188
  341. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -4,6 +4,7 @@ import { isDeliveredCompletion, logDuplicateSkip } from './delivered-completions
4
4
  import { promptContentText } from './prompt-utils.mjs';
5
5
  import { parseTaskNotification, taskNotificationId } from '../../../../shared/task-notification-envelope.mjs';
6
6
  import { hopOf } from '../../../../shared/bot-hops.mjs';
7
+ import { PROMPT_ORIGIN_CHANNEL } from '../../../../shared/prompt-origin.mjs';
7
8
 
8
9
  const STALE_USER_INJECTION_TTL_MS = 30 * 60 * 1000;
9
10
  const PENDING_PROCESS_START_MS = Date.now();
@@ -12,6 +13,8 @@ const LIFECYCLE_TOKEN = Symbol('pendingLifecycleToken');
12
13
  export const COMPLETION_NOTIFICATION_KIND = 'completion_notification';
13
14
  export const PENDING_MODE_PROMPT = 'prompt';
14
15
  export const PENDING_MODE_TASK_NOTIFICATION = 'task-notification';
16
+ // Prompt provenance apart from the presentation mode (shared/prompt-origin.mjs).
17
+ export { PROMPT_ORIGIN_AGENT_REPORT, PROMPT_ORIGIN_CHANNEL } from '../../../../shared/prompt-origin.mjs';
15
18
 
16
19
  export function stampLifecycleToken(entry, token) {
17
20
  if (!entry || typeof entry !== 'object' || !token) return entry;
@@ -169,9 +172,14 @@ export function lateDeliveryText(text, entry, now = Date.now()) {
169
172
  // A non-completion entry may be queued explicitly as a task notification (a
170
173
  // runtime/channel notice, never the user typing): the mode survives
171
174
  // normalization and the durable spool so the drain stores it as one and it
172
- // never drives explicit skill selection.
175
+ // never drives explicit skill selection. A channel's inbound message keeps
176
+ // its provenance beside the mode (PROMPT_ORIGIN_CHANNEL).
173
177
  function explicitModeField(entry) {
174
- return entry?.mode === PENDING_MODE_TASK_NOTIFICATION ? { mode: PENDING_MODE_TASK_NOTIFICATION } : {};
178
+ if (entry?.mode !== PENDING_MODE_TASK_NOTIFICATION) return {};
179
+ return {
180
+ mode: PENDING_MODE_TASK_NOTIFICATION,
181
+ ...(entry.origin === PROMPT_ORIGIN_CHANNEL ? { origin: PROMPT_ORIGIN_CHANNEL } : {}),
182
+ };
175
183
  }
176
184
 
177
185
  function normalizePendingMessageEntry(entry) {
@@ -343,6 +351,7 @@ export function _groupPendingMessageEntries(entries) {
343
351
  mode: PENDING_MODE_TASK_NOTIFICATION,
344
352
  ...(normalized.execution ? { execution: normalized.execution } : {}),
345
353
  ...(normalized.hop ? { hop: normalized.hop } : {}),
354
+ ...(normalized.origin ? { origin: normalized.origin } : {}),
346
355
  ids: idsOf([entry]),
347
356
  entries: [entry],
348
357
  });
@@ -33,6 +33,19 @@ function plainToolCalls(toolCalls) {
33
33
  });
34
34
  }
35
35
 
36
+ /** The provider-native replay state `message` carries — its replay fields'
37
+ * values and its tool calls' signatures — or null when it carries none. */
38
+ export function providerReplayState(message) {
39
+ if (!message || typeof message !== 'object') return null;
40
+ const state = PROVIDER_REPLAY_FIELDS.filter((field) => field in message).map((field) => message[field]);
41
+ for (const call of Array.isArray(message.toolCalls) ? message.toolCalls : []) {
42
+ if (call && typeof call === 'object') {
43
+ for (const field of TOOL_CALL_SIGNATURE_FIELDS) if (field in call) state.push(call[field]);
44
+ }
45
+ }
46
+ return state.length ? state : null;
47
+ }
48
+
36
49
  /** `message` without provider-native replay state; the same object when it carries none. */
37
50
  export function stripProviderReplayMessage(message) {
38
51
  if (!message || typeof message !== 'object') return message;
@@ -0,0 +1,129 @@
1
+ // The fallback chain a running session follows, read from its settings when
2
+ // it is needed (a hop, a status read) instead of the copy taken at creation:
3
+ // an edit made while the session runs applies from its next model request,
4
+ // and a chain cleared to [] stops further hops at once.
5
+ // - A Lead-class session (coding Lead, a bot's chat, Mixy) follows its
6
+ // owner's stored route (owner-route-fallbacks.mjs);
7
+ // - a delegated agent follows the route it was spawned from
8
+ // (`routeFallbackSource`: its agent's route, else Main's, or a named preset).
9
+ // The stored chain applies only while the session's primary — the route it
10
+ // ran on before its first hop — is that stored route's model. A session
11
+ // whose source cannot be read (an older record, a direct route) keeps its own.
12
+ import { findPreset } from '../../runtime-core/config-helpers.mjs';
13
+ import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
14
+ import { configuredAgentRouteCandidates } from '../../../../shared/agent-route-config.mjs';
15
+ import { CODING_MODE, resolveSessionMode } from '../agent-mode.mjs';
16
+ import { _liveSessions } from '../store/live-state.mjs';
17
+ import { ownerConfiguredRoute, sameRouteModel } from './owner-route-fallbacks.mjs';
18
+
19
+ const LEAF_FIELDS = ['effort', 'fast', 'autoEffort', 'modelParameters', 'contextPercent', 'contextTokens'];
20
+
21
+ /** The switchable fields of a route or session record (no chain). */
22
+ export function routeLeaf(route) {
23
+ const leaf = { provider: route?.provider || '', model: route?.model || '' };
24
+ for (const key of LEAF_FIELDS) if (route?.[key] !== undefined && route[key] !== null) leaf[key] = route[key];
25
+ return leaf;
26
+ }
27
+
28
+ /** The route the session ran on before its first fallback hop; its own route
29
+ * while it has not hopped. */
30
+ export function sessionRoutePrimary(session) {
31
+ if (session?.routeFallbackPrimary?.provider) return session.routeFallbackPrimary;
32
+ const first = session?.routeFallbackFailures?.[0];
33
+ if (first?.provider && first?.model) return { provider: first.provider, model: first.model };
34
+ return routeLeaf(session);
35
+ }
36
+
37
+ function normalizedOrNull(stored) {
38
+ if (!stored) return null;
39
+ try {
40
+ return normalizeWorkflowRoute(stored) || null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ /** The stored route the session's chain comes from, read now; null when the
47
+ * session has no readable source (it then keeps its own chain). */
48
+ export function sessionChainSource(session, config) {
49
+ const { mode, agentId } = resolveSessionMode({ session });
50
+ if (agentId) return ownerConfiguredRoute({ sessionMode: mode, agentId, config });
51
+ const source = session?.routeFallbackSource;
52
+ if (source?.agent) {
53
+ // As the spawn resolved it: the agent's own route, else Main's.
54
+ const own = configuredAgentRouteCandidates(config, source.agent).map(normalizedOrNull).find(Boolean);
55
+ return own || normalizedOrNull(findPreset(config, config?.default));
56
+ }
57
+ if (source?.preset) return normalizedOrNull(findPreset(config, source.preset));
58
+ return null;
59
+ }
60
+
61
+ /** `{ chain, fromSettings }`: the fallback routes the session follows now. */
62
+ export function liveRouteFallbacks(session, config) {
63
+ const source = sessionChainSource(session, config);
64
+ if (!source)
65
+ return { chain: Array.isArray(session?.routeFallbacks) ? session.routeFallbacks : [], fromSettings: false };
66
+ const onSourcePrimary = sameRouteModel(source, sessionRoutePrimary(session));
67
+ return { chain: onSourcePrimary ? source.fallbacks || [] : [], fromSettings: true };
68
+ }
69
+
70
+ /** Index in `chain` of the next route to try: the first after the session's
71
+ * current model that this session has not already run (its primary and the
72
+ * models it already left); `chain.length` when none is left. */
73
+ export function nextRouteFallbackIndex(session, chain) {
74
+ const tried = [sessionRoutePrimary(session), ...(session?.routeFallbackFailures || []), session];
75
+ const at = chain.findIndex((entry) => sameRouteModel(entry, session));
76
+ for (let index = at + 1; index < chain.length; index += 1) {
77
+ if (!tried.some((route) => sameRouteModel(chain[index], route))) return index;
78
+ }
79
+ return chain.length;
80
+ }
81
+
82
+ const modelOf = (route) => ({ provider: route?.provider || '', model: route?.model || '' });
83
+
84
+ /** The public fallback position of one session: the route it is on against
85
+ * its primary, the hop index, the chain it follows now and why it left each model. */
86
+ export function routeFallbackPosition(session, config) {
87
+ const { chain, fromSettings } = liveRouteFallbacks(session, config);
88
+ const next = chain[nextRouteFallbackIndex(session, chain)] || null;
89
+ const primary = sessionRoutePrimary(session);
90
+ const { mode, agentId } = resolveSessionMode({ session });
91
+ let kind = 'agent';
92
+ if (agentId) kind = mode === CODING_MODE ? 'lead' : 'bot';
93
+ return {
94
+ sessionId: session.id,
95
+ kind,
96
+ agent: agentId || session.agent || null,
97
+ current: modelOf(session),
98
+ primary: modelOf(primary),
99
+ onPrimary: sameRouteModel(primary, session),
100
+ fallbackIndex: Number(session.routeFallbackIndex) || 0,
101
+ fallbacks: chain.map(modelOf),
102
+ chainFrom: fromSettings ? 'settings' : 'session',
103
+ next: next ? modelOf(next) : null,
104
+ failures: (session.routeFallbackFailures || []).map(({ provider, model, kind: errorKind, error }) => ({
105
+ provider,
106
+ model,
107
+ kind: errorKind,
108
+ error,
109
+ })),
110
+ };
111
+ }
112
+
113
+ /** Whether the session has left its primary for a fallback model. */
114
+ export const routeFallbackHopped = (session) =>
115
+ Number(session?.routeFallbackIndex) > 0 || Boolean(session?.routeFallbackFailures?.length);
116
+
117
+ /** Positions of the sessions held in this process that follow a chain or have
118
+ * hopped; `keep(position)` narrows them. */
119
+ export function liveRouteFallbackPositions(config, keep = () => true) {
120
+ const rows = [];
121
+ for (const session of _liveSessions.values()) {
122
+ // Only a session with an agent hops (advanceAgentRoute).
123
+ if (!session?.id || session.closed === true || !session.agent) continue;
124
+ const position = routeFallbackPosition(session, config);
125
+ if (!position.fallbacks.length && !routeFallbackHopped(session)) continue;
126
+ if (keep(position)) rows.push(position);
127
+ }
128
+ return rows;
129
+ }
@@ -23,10 +23,12 @@
23
23
  // }
24
24
  import { createAbortController } from '../../../../shared/abort-controller.mjs';
25
25
  import { emitSessionApprovalWaitChange } from '../../../../shared/session-approval-wait.mjs';
26
- import { emitSessionActivityChange } from '../../../../shared/session-activity-signal.mjs';
26
+ import { emitSessionActivityChange, emitSessionTurnStart } from '../../../../shared/session-activity-signal.mjs';
27
27
  import { DEFAULT_ACTIVITY_HEARTBEAT_MS } from '../../stall-policy.mjs';
28
28
  import { MODEL_THINKING_STAGE, awaitingModelResponse, lastProgressAt } from '../../../../shared/quiet-progress.mjs';
29
- import { finishBrowserTurn } from '../../../../browser-bridge/client.mjs';
29
+ import { beginBrowserTurn, finishBrowserTurn, forgetBrowserSession } from '../../../../browser-bridge/client.mjs';
30
+ import { registerSessionPurgeHook } from '../store.mjs';
31
+ import { finishComputerTurn } from '../../../../computer-bridge/client.mjs';
30
32
  import { configureUsageMetricsRuntime, dropMetricSeenState, bumpUsageMetricsTurnId } from './usage-metrics.mjs';
31
33
  import { runtimeState as _runtimeState, isTurnInFlight } from './runtime-state.mjs';
32
34
 
@@ -48,11 +50,25 @@ const VALID_STAGES = new Set([
48
50
  ]);
49
51
  const TERMINAL_STAGES = new Set(['done', 'error']);
50
52
 
53
+ function beginBrowserWork(id, entry) {
54
+ void beginBrowserTurn(id, entry.browserTurnId).catch((error) => {
55
+ process.stderr.write(`[browser] Task start failed: ${error?.message || error}\n`);
56
+ });
57
+ }
58
+
59
+ // A session deleted for good no longer has browser turns to report.
60
+ registerSessionPurgeHook(forgetBrowserSession);
61
+
51
62
  function finishBrowserWork(id, entry, aborted = false) {
52
63
  const turnId = Number(entry.browserTurnId ?? entry.session?.usageMetricsTurnId) || 0;
53
64
  void finishBrowserTurn(id, turnId, { aborted }).catch((error) => {
54
65
  process.stderr.write(`[browser] Task cleanup failed: ${error?.message || error}\n`);
55
66
  });
67
+ // The same terminal state settles the turn's Computer Use saved-login fills
68
+ // (lead and sub-agent sessions alike).
69
+ void finishComputerTurn(id, turnId).catch((error) => {
70
+ process.stderr.write(`[computer] Turn settlement failed: ${error?.message || error}\n`);
71
+ });
56
72
  }
57
73
 
58
74
  // Injected deps that would otherwise pull manager.mjs's store/provider surface
@@ -149,6 +165,7 @@ export function markSessionAskStart(id) {
149
165
  const sessionForTurn = entry.session ?? _deps.loadSession(id);
150
166
  if (sessionForTurn) bumpUsageMetricsTurnId(sessionForTurn);
151
167
  entry.browserTurnId = Number(sessionForTurn?.usageMetricsTurnId) || 0;
168
+ beginBrowserWork(id, entry);
152
169
  entry.stage = 'connecting';
153
170
  entry.lastStreamDeltaAt = null;
154
171
  entry.lastTransportAt = null;
@@ -179,6 +196,7 @@ export function markSessionAskStart(id) {
179
196
  entry.lastProgressAt = now;
180
197
  entry.updatedAt = now;
181
198
  entry.taskWaits = 0;
199
+ emitSessionTurnStart(id);
182
200
  emitSessionActivityChange(id);
183
201
  }
184
202
  /** The provider put the request on the wire; its response headers are
@@ -24,6 +24,7 @@ import {
24
24
  } from './runtime-liveness.mjs';
25
25
  import { clearTurnCheckpoint } from './turn-checkpoint.mjs';
26
26
  import { forgetSessionSaveBaseline } from '../store/save-worker.mjs';
27
+ import { publishSessionPersistence } from '../store/session-persistence-events.mjs';
27
28
  import { forgetSessionLoadCache } from '../store/load-cache.mjs';
28
29
  import { releaseReadSnapshotScope } from '../../tools/builtin/snapshot-store.mjs';
29
30
  import { cancelOwnedShellTrees } from '../../tools/lib/owned-shell-trees.mjs';
@@ -142,6 +143,7 @@ function finishClose({ id, reason, tombstone, entry, closeSnapshot, newGen, outc
142
143
  // above; committing it stops the turn's checkpoint writer. Only now is it
143
144
  // safe to remove the crash fallback.
144
145
  closeSnapshot?.commit();
146
+ publishSessionPersistence(id, 'closed');
145
147
  _stopToolActivityHeartbeat(id);
146
148
  cancelOwnedShellProcesses({ id, reason, tombstone });
147
149
  clearTurnCheckpoint(id);
@@ -288,6 +288,13 @@ export function createSession(createOpts) {
288
288
  if (rulesRevision) session.rulesRevision = rulesRevision;
289
289
  }
290
290
  if (routeFallbacks?.length) session.routeFallbacks = routeFallbacks;
291
+ // Where an agent's chain is read from at each hop (route-fallback-chain.mjs).
292
+ const fallbackSource = Object.hasOwn(opts, 'routeFallbackSource')
293
+ ? opts.routeFallbackSource
294
+ : presetObj?.fallbackSource;
295
+ if (!actingAgent && fallbackSource && typeof fallbackSource === 'object') {
296
+ session.routeFallbackSource = { ...fallbackSource };
297
+ }
291
298
  // A non-native agent sends core + bridge, built from its filtered tools.
292
299
  applyAgentToolExposure(session);
293
300
  refreshSessionBp3Environment(session, opts.cwd);
@@ -364,6 +371,7 @@ function applyRouteFields(session, route) {
364
371
  else delete session.routeFallbacks;
365
372
  delete session.routeFallbackIndex;
366
373
  delete session.routeFallbackFailures;
374
+ delete session.routeFallbackPrimary;
367
375
  }
368
376
  ensureCodexWireSessionId(session);
369
377
  if (Object.hasOwn(route, 'modelParameters')) {
@@ -36,7 +36,7 @@ export function normalizeDesktopSessionMetadata(value, cwd = null) {
36
36
  return null;
37
37
  }
38
38
 
39
- function initialCompactionConfig(compaction = {}, contextMeta = {}) {
39
+ export function initialCompactionConfig(compaction = {}, contextMeta = {}) {
40
40
  return {
41
41
  auto: compaction?.auto !== false,
42
42
  model: compaction?.model || null,
@@ -3,7 +3,8 @@
3
3
  import { compareCodePoints } from '../../../../shared/code-point-order.mjs';
4
4
  import { getMcpTools } from '../../mcp/client.mjs';
5
5
  import { getInternalTools } from '../../internal-tools.mjs';
6
- import { BUILTIN_TOOLS } from '../../tools/builtin/builtin-tools.mjs';
6
+ import { BUILTIN_TOOLS, bindShellKind } from '../../tools/builtin/builtin-tools.mjs';
7
+ import { resolveShellFor } from '../../tools/builtin/shell-runtime.mjs';
7
8
  import { PATCH_TOOL_DEFS } from '../../tools/patch-tool-defs.mjs';
8
9
  import { CODE_GRAPH_TOOL_DEFS } from '../../tools/code-graph-tool-defs.mjs';
9
10
  import { buildSkillToolDefs } from '../../context/collect.mjs';
@@ -101,7 +102,11 @@ export function resolveSessionTools(
101
102
  // time (loop.mjs), so the schema bytes stay bit-identical across roles /
102
103
  // cwds and the provider cache shard does not fragment.
103
104
  const skillTools = buildSkillToolDefs(skills, { ownerIsAgentSession });
104
- return filterModelEditTools(_computeBaseTools(toolSpec, mcp, skillTools), modelName);
105
+ // The shell text follows the system shell the runtime actually resolves
106
+ // (Git Bash/WSL on Windows, pwsh on POSIX), before the edit-dialect rewrite.
107
+ const shellKind = resolveShellFor('default')?.shellType;
108
+ const base = _computeBaseTools(toolSpec, mcp, skillTools).map((tool) => bindShellKind(tool, shellKind));
109
+ return filterModelEditTools(base, modelName);
105
110
  }
106
111
 
107
112
  export function previewSessionTools(toolSpec, skills = [], options = {}) {
@@ -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
@@ -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';
@@ -409,19 +409,31 @@ function adoptCompactedTranscript(ctx, run) {
409
409
  const { messages, sessionRef, sessionId, opts } = state;
410
410
  const replaced = messages.slice();
411
411
  if (sessionRef) backfillFallbackSessionTitle(sessionRef, replaced);
412
+ // Display-only archive of the dropped conversation, made durable (pending
413
+ // sidecar) BEFORE the shortened transcript is adopted and can be saved; a
414
+ // sidecar that cannot be written keeps the old transcript and fails this
415
+ // compaction. The publication runs off this synchronous adoption.
416
+ let prepared = null;
417
+ if (sessionRef) {
418
+ try {
419
+ prepared = prepareCompactionArchive({ session: sessionRef, sessionId, before: replaced, after: run.compacted });
420
+ } catch (archiveError) {
421
+ throwCompactFailure(ctx, run, archiveError);
422
+ }
423
+ }
412
424
  messages.length = 0;
413
425
  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;
426
+ const archived = prepared ? { ...prepared, written: prepared.start() } : null;
419
427
  ctx.droppedMessages = archived?.dropped ?? 0;
420
428
  ctx.droppedIdentities = archived?.identities ?? null;
421
429
  acknowledgeGoalReminder(sessionRef, run);
422
430
  // The compacted transcript breaks the cache anyway: a staged working-folder
423
431
  // change re-roots the system prompt now and needs no notice any more.
424
432
  if (sessionRef) applyPendingWorkspaceReroot(sessionRef, messages);
433
+ // The system prompt is rebuilt from current sources outside a running turn
434
+ // (session-runtime agent-profile-fold.mjs): the next cold point folds it in
435
+ // instead of adding a break to the turn that is already warm again.
436
+ if (sessionRef) sessionRef.promptFoldPending = true;
425
437
  resetReadStateAfterCompaction(sessionId);
426
438
  // Cross-turn receipts reference tool results that compaction just removed
427
439
  // from context; a stub for them would point at nothing. Clear in place so
@@ -444,8 +456,7 @@ function adoptCompactedTranscript(ctx, run) {
444
456
  ctx.providerStateCleared = true;
445
457
  // Compaction starts a new loop epoch: the iteration counter restarts at 0
446
458
  // 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.
459
+ // count from the compacted transcript.
449
460
  ctx.iterations = 0;
450
461
  // New loop epoch so persistIterationMetrics idempotency keys do not
451
462
  // collide when iteration indices restart at 1 (incl. iter 1 → iter 1).