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
@@ -12,10 +12,7 @@ import {
12
12
  loadSkillResource,
13
13
  buildSkillToolEnvelope,
14
14
  filterSkillsExcludingDisabled,
15
- isSkillDisabled,
16
- skillMissingFeature,
17
- skillOutsideScope,
18
- skillOutsideScopeError,
15
+ skillLoadRefusal,
19
16
  } from '../../context/collect.mjs';
20
17
 
21
18
  // Eager-dispatch: tools with readOnlyHint:true in their declaration are safe
@@ -95,12 +92,8 @@ export function buildSkillsListResponse(cwd) {
95
92
  export function viewSkill(cwd, name, session = null) {
96
93
  const skillName = String(name || '').trim();
97
94
  if (!skillName) return 'Error: skill name is required';
98
- const missingFeature = skillMissingFeature(skillName);
99
- if (missingFeature) {
100
- return `Error: skill "${skillName}" needs the ${missingFeature} built-in feature, which is not installed or is switched off in Extensions → Plugin → Built-in`;
101
- }
102
- if (isSkillDisabled(skillName)) return `Error: skill "${skillName}" is disabled`;
103
- if (skillOutsideScope(skillName, cwd)) return skillOutsideScopeError(skillName);
95
+ const refusal = skillLoadRefusal(skillName, cwd);
96
+ if (refusal) return refusal;
104
97
  const res = loadSkillResource(skillName, cwd);
105
98
  if (!res) return `Error: skill "${skillName}" not found`;
106
99
  // Return the general tool envelope: the model-visible tool_result is the
@@ -9,6 +9,7 @@ import { processToolBatch } from '../tool-batch.mjs';
9
9
  import { CROSS_TURN_CAP, REPEAT_FAIL_LIMIT } from './loop-state.mjs';
10
10
  import { markStepEffort } from './step-auto-effort.mjs';
11
11
  import { resolveExecutionToolCalls } from './tool-call-resolution.mjs';
12
+ import { rememberSensitiveValues } from '../sensitive-values.mjs';
12
13
  import { callsAllowed, settleTurnLimits, turnLimitsOf } from './turn-limits.mjs';
13
14
 
14
15
  function isSleepLikeToolCall(call) {
@@ -41,13 +42,19 @@ export async function runToolPhase(state, round, sent, onToolCall) {
41
42
  // derive the multi-tool adoption ratio without scanning message bodies.
42
43
  // Audit records what runs: a bridged call_tool is recorded as its target.
43
44
  // The wire calls stay the transcript identity (assistant turn below).
44
- const toolBatchId = recordToolBatch(sessionId, resolveExecutionToolCalls(calls, sessionRef), state.iterations);
45
+ const executionCalls = resolveExecutionToolCalls(calls, sessionRef);
46
+ const toolBatchId = recordToolBatch(sessionId, executionCalls, state.iterations);
47
+ // A sensitive fill (a verification code): every stored copy of the
48
+ // transcript keeps a marker instead of the value; the call runs as is.
49
+ rememberSensitiveValues(sessionId, executionCalls);
45
50
  await Promise.resolve(onToolCall?.(state.iterations, calls));
46
51
  const assistantTurnMsg = buildToolCallAssistantMessage(response, { calls, opts });
47
52
  commitAssistantMessage(messages, assistantTurnMsg, opts);
48
53
  try {
49
54
  opts.onToolPhaseStarted?.();
50
- } catch {}
55
+ } catch {
56
+ // the tool-phase-started observer is advisory
57
+ }
51
58
  const toolsT0 = Date.now();
52
59
  const resultsFrom = messages.length;
53
60
  ({ dedupStubTotal: state.dedupStubTotal, editCount: state.editCount } = await processToolBatch({
@@ -90,7 +97,9 @@ export async function runToolPhase(state, round, sent, onToolCall) {
90
97
  calls: calls.length,
91
98
  elapsedMs: toolsEndedAt - toolsT0,
92
99
  });
93
- } catch {}
100
+ } catch {
101
+ // the tool-phase-completed observer is advisory
102
+ }
94
103
  traceLoopPhaseTiming({
95
104
  iteration: round.nextIteration,
96
105
  preSendMs: sent.preSendMs,
@@ -9,6 +9,7 @@ import {
9
9
  drainPendingMessages,
10
10
  releasePendingMessages,
11
11
  } from './pending-messages.mjs';
12
+ import { promptOriginOf } from '../../../../shared/prompt-origin.mjs';
12
13
 
13
14
  const SYNTHETIC_PROMPT_SOURCES = ['goal-continuation', 'goal-closeout'];
14
15
 
@@ -35,6 +36,7 @@ const tailTurnFor = (group) => ({
35
36
  content: group.content,
36
37
  entries: group.entries,
37
38
  ...(group.hop ? { hop: group.hop } : {}),
39
+ ...(group.origin ? { origin: group.origin } : {}),
38
40
  ...(group.mode === PENDING_MODE_TASK_NOTIFICATION
39
41
  ? promptSourceFields(PENDING_MODE_TASK_NOTIFICATION, group.execution)
40
42
  : {}),
@@ -49,13 +51,19 @@ const queuedTurnInput = (tailTurn) => ({
49
51
  pendingEntries: tailTurn.entries,
50
52
  ...(tailTurn.hop ? { hop: tailTurn.hop } : {}),
51
53
  promptSource: promptSourceFields(tailTurn.source, tailTurn.execution),
54
+ ...(tailTurn.origin ? { promptOrigin: tailTurn.origin } : {}),
52
55
  });
53
56
 
54
- export function createAskPromptQueue({ sessionId, promptSource }) {
57
+ // The caller's provenance of its initial prompt, apart from its presentation
58
+ // source: an inbound channel message or an agent report (shared/prompt-origin.mjs).
59
+ const initialPromptOriginFor = (promptOrigin) => promptOriginOf(promptOrigin);
60
+
61
+ export function createAskPromptQueue({ sessionId, promptSource, promptOrigin }) {
55
62
  // Local FIFO of follow-up prompts drained from the pending-message queue
56
63
  // after each turn — keeps queued `agent type=send` messages in order.
57
64
  const tail = [];
58
65
  let initialPromptSource = initialPromptSourceFor(promptSource);
66
+ let initialPromptOrigin = initialPromptOriginFor(promptOrigin);
59
67
  // Queue entries of the input nextTurn handed out that no turn owns yet.
60
68
  // Once a turn starts, its failure path acks or releases them instead.
61
69
  let unstartedEntries = [];
@@ -67,13 +75,15 @@ export function createAskPromptQueue({ sessionId, promptSource }) {
67
75
  * and provenance — or null when nothing model-visible is left to run. */
68
76
  const nextTurn = (original) => {
69
77
  const source = initialPromptSource;
78
+ const origin = initialPromptOrigin;
70
79
  initialPromptSource = null;
80
+ initialPromptOrigin = null;
71
81
  let input;
72
82
  if (tail.length > 0) {
73
83
  // After the first turn, the next prompt comes from the drained queue.
74
84
  input = queuedTurnInput(tail.shift());
75
85
  } else if (hasModelVisiblePromptContent(original.prompt)) {
76
- input = { ...original, pendingEntries: [], promptSource: source };
86
+ input = { ...original, pendingEntries: [], promptSource: source, ...(origin ? { promptOrigin: origin } : {}) };
77
87
  } else {
78
88
  // Idle resume: TUI kicks an empty ask() after execution completions
79
89
  // mirror model-visible bodies into session pending. Drain that queue
@@ -19,6 +19,7 @@ import { armTurnDurability, claimTurnRuntime } from './ask-turn-open.mjs';
19
19
  import { prepareAskTurn, runAskAgentLoop } from './ask-turn-execute.mjs';
20
20
  import { commitAskTurn, publishAskTurn } from './ask-turn-settle.mjs';
21
21
  import { finalizeAskTurnFailure } from './ask-turn-failure.mjs';
22
+ import { cacheWarmTurnKind, endCacheWarmTurn, failedTurnProvenance, noteCacheWarmTurn } from '../cache-warm.mjs';
22
23
 
23
24
  export { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
24
25
  export {
@@ -168,7 +169,14 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
168
169
  // return value (the queued tail turns supersede the original prompt's
169
170
  // result, mirroring how a live chat returns the latest turn).
170
171
  let result;
171
- const queue = createAskPromptQueue({ sessionId, promptSource: askOpts.promptSource });
172
+ const queue = createAskPromptQueue({
173
+ sessionId,
174
+ promptSource: askOpts.promptSource,
175
+ promptOrigin: askOpts.promptOrigin,
176
+ });
177
+ // The turn the prompt-cache warmer is told about last; it forgets it when
178
+ // the turn ends (endCacheWarmTurn), whatever way the ask unwinds.
179
+ let warmTurn = null;
172
180
  // Hoisted so the outer finally (which runs once after the whole turn loop)
173
181
  // can compare against the last turn's generation.
174
182
  let askGeneration = 0;
@@ -213,6 +221,12 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
213
221
  recoverCheckpoint: !crashRecoveryChecked,
214
222
  emitStart: !sessionStartEmitted,
215
223
  });
224
+ // The prompt-cache warmer learns idle gaps from real turns only; a
225
+ // retry is classified by the failed turn's stored provenance.
226
+ // The rebuilt retry message keeps that provenance (ask-turn-execute.mjs).
227
+ const retryProvenance = failedTurnProvenance(claimed.preSession?.messages, input);
228
+ if (retryProvenance) input.retryProvenance = retryProvenance;
229
+ warmTurn = noteCacheWarmTurn(sessionId, cacheWarmTurnKind(input, claimed.preSession?.messages));
216
230
  crashRecoveryChecked = true;
217
231
  sessionStartEmitted = true;
218
232
  askGeneration = claimed.askGeneration;
@@ -234,6 +248,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
234
248
  askStartedAt,
235
249
  });
236
250
  result = turn.result;
251
+ endCacheWarmTurn(sessionId, warmTurn);
237
252
  // Turn complete. Drain the pending-message queue: any `agent type=send`
238
253
  // that arrived while this turn was in flight runs next, in order, as a
239
254
  // follow-up user turn. The mutex is still held, so a send racing this
@@ -264,6 +279,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
264
279
  return result;
265
280
  }
266
281
  } finally {
282
+ // A failed/cancelled turn ends here too: its warmer turn entry goes (a
283
+ // later ask's own turn is never removed by this one).
284
+ endCacheWarmTurn(sessionId, warmTurn);
267
285
  // A failed/cancelled turn ends the ask: the follow-up turns drained
268
286
  // behind it never ran, so their entries go back to the pending queue.
269
287
  queue.releaseUnstarted();
@@ -7,6 +7,7 @@
7
7
  import { getProvider } from '../../providers/registry.mjs';
8
8
  import { pinSessionWireRoute } from '../../providers/wire-route.mjs';
9
9
  import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
10
+ import { appendSkillUpdateNotices } from '../skill-update-notice.mjs';
10
11
  import { prepareTurnEffortConfiguration } from '../../providers/effort-configuration.mjs';
11
12
  import { resolveTurnAutoEffort, turnStepAutoEffort } from './ask-turn-auto-effort.mjs';
12
13
  import { saveSessionAsync } from '../store.mjs';
@@ -22,6 +23,128 @@ import { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
22
23
  import { backfillFallbackSessionTitle } from './session-fallback-title.mjs';
23
24
  import { stampTurnHop } from '../../../../shared/bot-hops.mjs';
24
25
 
26
+ function turnPromptSourceFor(input) {
27
+ if (!(input.promptOrigin || input.retryProvenance)) return input.promptSource;
28
+ return {
29
+ ...(input.promptSource || {}),
30
+ ...(input.promptOrigin ? { origin: input.promptOrigin } : {}),
31
+ ...(input.retryProvenance ? { provenance: input.retryProvenance } : {}),
32
+ };
33
+ }
34
+
35
+ // A retried failed turn resubmits its retained user message over the history
36
+ // before it: the message keeps its input and records the failed completion.
37
+ function retainedUserTurn(session, retained, transcriptMeta, currentAgents) {
38
+ const historyMessages = filterModelVisibleSessionMessages(session.messages.slice(0, retained.index));
39
+ const { completion, ...previousTranscript } = retained.message.meta.transcript;
40
+ // The retained message keeps its input; agents enabled or switched
41
+ // off since it was built ride its runtime context.
42
+ const userTurnMessage = withAgentListingNotice(
43
+ session,
44
+ retained.index,
45
+ {
46
+ ...retained.message,
47
+ meta: {
48
+ ...retained.message.meta,
49
+ transcript: {
50
+ ...previousTranscript,
51
+ ...transcriptMeta,
52
+ messageId: previousTranscript.messageId,
53
+ previousCompletions: [
54
+ ...(previousTranscript.previousCompletions || []),
55
+ { ...completion, at: completion.at ?? previousTranscript.at },
56
+ ],
57
+ },
58
+ },
59
+ },
60
+ currentAgents
61
+ );
62
+ return { historyMessages, userTurnMessage, outgoing: [...historyMessages, userTurnMessage] };
63
+ }
64
+
65
+ // The turn's user message: the retained one of a retried failed turn, or a
66
+ // newly built one (a promise).
67
+ function pendingUserTurn({ session, input, retained, turnSignal, transcriptMeta, currentAgents }) {
68
+ if (retained) return retainedUserTurn(session, retained, transcriptMeta, currentAgents);
69
+ return buildUserTurn({
70
+ session,
71
+ prompt: input.prompt,
72
+ context: input.context,
73
+ explicitPrefetch: input.explicitPrefetch,
74
+ turnSignal,
75
+ transcriptMeta,
76
+ // The stored message keeps an inbound channel's provenance and, for
77
+ // a rebuilt retry (the resubmitted prompt or a continuation prompt),
78
+ // the failed turn's, which a later retry reads back
79
+ // (cache-warm.mjs failedTurnProvenance).
80
+ turnPromptSource: turnPromptSourceFor(input),
81
+ effortConfiguration: null,
82
+ currentAgents,
83
+ });
84
+ }
85
+
86
+ // Stamp the turn's effort on the user message (same meta order as when it
87
+ // was built with it: before the runtime-context provenance).
88
+ function stampTurnEffort(userTurnMessage, effortConfiguration) {
89
+ const { runtimeUserContext, ...meta } = userTurnMessage.meta || {};
90
+ userTurnMessage.meta = {
91
+ ...meta,
92
+ effortConfiguration,
93
+ ...(runtimeUserContext ? { runtimeUserContext } : {}),
94
+ };
95
+ }
96
+
97
+ // Expose the in-flight working transcript so contextStatus() can
98
+ // estimate the LIVE context footprint mid-turn. agentLoop mutates
99
+ // `outgoing` in place (user turn + tool calls/results + compaction),
100
+ // so the statusline context gauge climbs as the turn accumulates
101
+ // tool output instead of freezing at the pre-turn snapshot. Cleared
102
+ // on turn commit and in the ask finally.
103
+ //
104
+ // Also commit the user turn to the live session BEFORE the provider
105
+ // call. Previously the prompt only reached session.messages after
106
+ // agentLoop returned. If a worker/lead session was closed or aborted
107
+ // before first response, closeSession() wrote a tombstone from the
108
+ // still-system-only session and the handoff brief vanished forever
109
+ // (agent row showed messages=2). Pre-committing makes cancellation,
110
+ // close, and post-mortem files retain the exact user task; completion
111
+ // overwrites this provisional transcript with the fully mutated
112
+ // outgoing history and appends the assistant result, so no duplicate
113
+ // user turn is introduced.
114
+ function precommitUserTurn(session, outgoing, historyMessages, input) {
115
+ session.messages = filterModelVisibleSessionMessages(outgoing);
116
+ session.liveTurnMessages = outgoing;
117
+ // Name a titleless session from its first human prompt before the first
118
+ // save, so the title outlives any later compaction of the transcript. The
119
+ // earliest human turn already in the history wins; this turn's prompt is the
120
+ // fallback (runtime-injected turns carrying a promptSource never name it).
121
+ backfillFallbackSessionTitle(session, [
122
+ ...historyMessages,
123
+ ...(input.promptSource ? [] : [{ role: 'user', content: input.prompt }]),
124
+ ]);
125
+ }
126
+
127
+ // Mark the turn active and persist the pre-committed session before the
128
+ // provider runs.
129
+ function persistTurnPreflight(session, { turnToken, startedAt, checkpoint, askGeneration }) {
130
+ session.activeTurnCheckpoint = {
131
+ version: 1,
132
+ turnToken,
133
+ startedAt,
134
+ };
135
+ // The sidecar lands synchronously before provider execution. Even
136
+ // if the async canonical-session preflight save has not reached its
137
+ // worker when the process is killed, recovery still has the prompt.
138
+ checkpoint.schedule(true);
139
+ saveSessionAsync(session, { expectedGeneration: askGeneration }).catch((err) => {
140
+ try {
141
+ process.stderr.write(`[session] preflight user-turn save failed: ${err?.message || err}\n`);
142
+ } catch {
143
+ // stderr may be closed; the save failure is only reported
144
+ }
145
+ });
146
+ }
147
+
25
148
  /** Resolves the provider, builds the user turn and pre-commits it; returns
26
149
  * what the loop and the commit need from this preparation. */
27
150
  export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, transcriptMeta, currentAgents }) {
@@ -68,45 +191,7 @@ export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, tr
68
191
  });
69
192
  const [autoEffort, userTurn] = await Promise.all([
70
193
  autoEffortPending,
71
- retained
72
- ? (() => {
73
- const historyMessages = filterModelVisibleSessionMessages(session.messages.slice(0, retained.index));
74
- const { completion, ...previousTranscript } = retained.message.meta.transcript;
75
- // The retained message keeps its input; agents enabled or switched
76
- // off since it was built ride its runtime context.
77
- const userTurnMessage = withAgentListingNotice(
78
- session,
79
- retained.index,
80
- {
81
- ...retained.message,
82
- meta: {
83
- ...retained.message.meta,
84
- transcript: {
85
- ...previousTranscript,
86
- ...transcriptMeta,
87
- messageId: previousTranscript.messageId,
88
- previousCompletions: [
89
- ...(previousTranscript.previousCompletions || []),
90
- { ...completion, at: completion.at ?? previousTranscript.at },
91
- ],
92
- },
93
- },
94
- },
95
- currentAgents
96
- );
97
- return { historyMessages, userTurnMessage, outgoing: [...historyMessages, userTurnMessage] };
98
- })()
99
- : buildUserTurn({
100
- session,
101
- prompt: input.prompt,
102
- context: input.context,
103
- explicitPrefetch: input.explicitPrefetch,
104
- turnSignal,
105
- transcriptMeta,
106
- turnPromptSource: input.promptSource,
107
- effortConfiguration: null,
108
- currentAgents,
109
- }),
194
+ pendingUserTurn({ session, input, retained, turnSignal, transcriptMeta, currentAgents }),
110
195
  wireRoutePending,
111
196
  ]);
112
197
  const turnEffort = autoEffort?.applied ? autoEffort.effort : session.effort || null;
@@ -118,60 +203,12 @@ export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, tr
118
203
  // Auto effort also judges every tool-result step of the turn when the
119
204
  // installed judge was trained on steps (session/loop/step-auto-effort.mjs).
120
205
  const stepAutoEffort = turnStepAutoEffort(autoEffort, effortConfiguration);
121
- if (effortConfiguration) {
122
- // Stamp the turn's effort on the user message (same meta order as when it
123
- // was built with it: before the runtime-context provenance).
124
- const { runtimeUserContext, ...meta } = userTurn.userTurnMessage.meta || {};
125
- userTurn.userTurnMessage.meta = {
126
- ...meta,
127
- effortConfiguration,
128
- ...(runtimeUserContext ? { runtimeUserContext } : {}),
129
- };
130
- }
206
+ if (effortConfiguration) stampTurnEffort(userTurn.userTurnMessage, effortConfiguration);
131
207
  const { historyMessages, outgoing } = userTurn;
132
208
  turn.userTurnContent = userTurn.userTurnMessage.content;
133
209
  turn.outgoing = outgoing;
134
- // Expose the in-flight working transcript so contextStatus() can
135
- // estimate the LIVE context footprint mid-turn. agentLoop mutates
136
- // `outgoing` in place (user turn + tool calls/results + compaction),
137
- // so the statusline context gauge climbs as the turn accumulates
138
- // tool output instead of freezing at the pre-turn snapshot. Cleared
139
- // on turn commit and in the ask finally.
140
- //
141
- // Also commit the user turn to the live session BEFORE the provider
142
- // call. Previously the prompt only reached session.messages after
143
- // agentLoop returned. If a worker/lead session was closed or aborted
144
- // before first response, closeSession() wrote a tombstone from the
145
- // still-system-only session and the handoff brief vanished forever
146
- // (agent row showed messages=2). Pre-committing makes cancellation,
147
- // close, and post-mortem files retain the exact user task; completion
148
- // overwrites this provisional transcript with the fully mutated
149
- // outgoing history and appends the assistant result, so no duplicate
150
- // user turn is introduced.
151
- session.messages = filterModelVisibleSessionMessages(outgoing);
152
- session.liveTurnMessages = outgoing;
153
- // Name a titleless session from its first human prompt before the first
154
- // save, so the title outlives any later compaction of the transcript. The
155
- // earliest human turn already in the history wins; this turn's prompt is the
156
- // fallback (runtime-injected turns carrying a promptSource never name it).
157
- backfillFallbackSessionTitle(session, [
158
- ...historyMessages,
159
- ...(input.promptSource ? [] : [{ role: 'user', content: input.prompt }]),
160
- ]);
161
- session.activeTurnCheckpoint = {
162
- version: 1,
163
- turnToken,
164
- startedAt,
165
- };
166
- // The sidecar lands synchronously before provider execution. Even
167
- // if the async canonical-session preflight save has not reached its
168
- // worker when the process is killed, recovery still has the prompt.
169
- checkpoint.schedule(true);
170
- saveSessionAsync(session, { expectedGeneration: askGeneration }).catch((err) => {
171
- try {
172
- process.stderr.write(`[session] preflight user-turn save failed: ${err?.message || err}\n`);
173
- } catch {}
174
- });
210
+ precommitUserTurn(session, outgoing, historyMessages, input);
211
+ persistTurnPreflight(session, { turnToken, startedAt, checkpoint, askGeneration });
175
212
  traceTurnContext({
176
213
  sessionId,
177
214
  session,
@@ -237,6 +274,12 @@ export async function runAskAgentLoop({
237
274
  checkpoint.schedule(true);
238
275
  notifySkillsApplied(askOpts?.onSkillsApplied, applied, userMessage);
239
276
  }
277
+ // Every turn (any session, any prompt source): a loaded skill that changed
278
+ // since it was loaded is announced once, after the history.
279
+ if (appendSkillUpdateNotices(outgoing, effectiveCwd).length) {
280
+ session.messages = filterModelVisibleSessionMessages(outgoing);
281
+ checkpoint.schedule(true);
282
+ }
240
283
  return await _api_call_with_interrupt(sessionId, (signal) =>
241
284
  agentLoop(
242
285
  provider,
@@ -4,6 +4,9 @@
4
4
  // canonicalizes the transcript when the turn is interrupted.
5
5
  import { captureTurnCheckpointContextState, createTurnCheckpointRecorder } from './turn-checkpoint.mjs';
6
6
  import { finalizeTurnInterruptionSnapshot } from './turn-interruption.mjs';
7
+ import { onSensitiveValuesMarked } from '../sensitive-values.mjs';
8
+ import { saveSessionAsync } from '../store.mjs';
9
+ import { ingestSessionTurn } from '../../../../session-search/session-search-ingest.mjs';
7
10
 
8
11
  // Coalescing window for streaming-delta flushes. Each flush journals only the
9
12
  // delta since the previous one, so the window just bounds syscalls.
@@ -50,11 +53,36 @@ export function createTurnCheckpointScheduler({ sessionId, generation, turnToken
50
53
  warned = true;
51
54
  try {
52
55
  process.stderr.write(`[turn-checkpoint] write failed session=${sessionId}: ${error?.message || error}\n`);
53
- } catch {}
56
+ } catch {
57
+ // stderr may be closed; the checkpoint failure is only reported
58
+ }
54
59
  }
55
60
  return false;
56
61
  }
57
62
  };
63
+ const flushNow = () => {
64
+ if (timer) clearTimeout(timer);
65
+ timer = null;
66
+ flush();
67
+ };
68
+ // A value marked sensitive while the turn runs (sensitive-values.mjs) —
69
+ // before the call that carries it executes: every stored copy that may hold
70
+ // it is written again with it replaced. The checkpoint is rewritten in full
71
+ // now (the recorder sees the new version); the session file gets a full
72
+ // save (store/save-delta.mjs resyncs on the version) and the search rows a
73
+ // transactional replacement (session-search-ingest.mjs).
74
+ const stopMarks = onSensitiveValuesMarked(sessionId, () => {
75
+ if (stopped) return;
76
+ if (turn.outgoing) flushNow();
77
+ saveSessionAsync(turn.session, { expectedGeneration: generation }).catch((err) => {
78
+ try {
79
+ process.stderr.write(`[session] sensitive-value save failed session=${sessionId}: ${err?.message || err}\n`);
80
+ } catch {
81
+ // stderr may be closed; the save failure is only reported
82
+ }
83
+ });
84
+ ingestSessionTurn(turn.session);
85
+ });
58
86
  return {
59
87
  refreshContextState() {
60
88
  contextState = captureTurnCheckpointContextState(turn.session, turn.outgoing, turn.userTurnContent);
@@ -62,9 +90,7 @@ export function createTurnCheckpointScheduler({ sessionId, generation, turnToken
62
90
  schedule(immediate = false) {
63
91
  if (stopped || !turn.outgoing) return;
64
92
  if (immediate || Date.now() - lastAt >= TURN_CHECKPOINT_THROTTLE_MS) {
65
- if (timer) clearTimeout(timer);
66
- timer = null;
67
- flush();
93
+ flushNow();
68
94
  return;
69
95
  }
70
96
  if (timer) return;
@@ -77,6 +103,7 @@ export function createTurnCheckpointScheduler({ sessionId, generation, turnToken
77
103
  },
78
104
  stop() {
79
105
  stopped = true;
106
+ stopMarks();
80
107
  if (timer) clearTimeout(timer);
81
108
  timer = null;
82
109
  recorder.stop();
@@ -15,6 +15,7 @@ import {
15
15
  } from '../context-utils.mjs';
16
16
  import { previewFreshContextCompaction, runFreshContextCompact } from '../loop/fresh-context.mjs';
17
17
  import { positiveInt } from '../../../../shared/numbers.mjs';
18
+ import { withLiveCompactionConfig } from '../../runtime-core/compaction-config-keys.mjs';
18
19
  import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
19
20
  import { traceAgentCompact, messagePrefixHash } from '../../agent-trace.mjs';
20
21
  import { uncachedInputTokensForProvider } from './usage-metrics.mjs';
@@ -36,7 +37,7 @@ import {
36
37
  shouldCompactForRequestMedia,
37
38
  } from '../loop/compact-policy.mjs';
38
39
  import { snapshotProviderRequestTools } from '../../runtime-core/tool-catalog.mjs';
39
- import { scheduleCompactionArchive, stampCompactionMessageIds } from '../compaction-archive-capture.mjs';
40
+ import { prepareCompactionArchive, stampCompactionMessageIds } from '../compaction-archive-capture.mjs';
40
41
 
41
42
  // 'compacting' is a transient in-flight stage written just before Compact
42
43
  // runs. If the process crashes or only partially
@@ -263,11 +264,12 @@ function compactionResult(plan, outcome, after, tail) {
263
264
  };
264
265
  }
265
266
 
267
+ // The commit writes only its own outcome telemetry; config-owned fields stay
268
+ // the live ones (withLiveCompactionConfig).
266
269
  function compactionRecord(plan, { stage, after, now, reserve, outcome }) {
267
- const { session, mode } = plan;
268
- return {
269
- ...withoutLegacyCompactFields(session.compaction),
270
- auto: mode === 'auto' ? true : session.compaction?.auto !== false,
270
+ const { session } = plan;
271
+ const live = withoutLegacyCompactFields(session.compaction);
272
+ return withLiveCompactionConfig(live, {
271
273
  boundaryTokens: plan.boundary,
272
274
  triggerTokens: plan.triggerTokens,
273
275
  bufferTokens: plan.bufferTokens,
@@ -282,7 +284,7 @@ function compactionRecord(plan, { stage, after, now, reserve, outcome }) {
282
284
  currentEstimatedTokens: after.tokens,
283
285
  lastCheckedAt: now,
284
286
  ...outcome,
285
- };
287
+ });
286
288
  }
287
289
 
288
290
  // compact_meta parity with the loop's pre-send pass: the out-of-loop
@@ -659,6 +661,26 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
659
661
  // Older titleless sessions: keep a name before the transcript that carries
660
662
  // the first prompt is replaced.
661
663
  if (changed) backfillFallbackSessionTitle(session, messages);
664
+ // Write-ahead: what this compaction drops is durable (pending sidecar)
665
+ // before the shortened transcript is adopted, since any later save persists
666
+ // it. A sidecar that cannot be written keeps the old transcript.
667
+ let archive = null;
668
+ if (changed && plan.archive) {
669
+ try {
670
+ archive = prepareCompactionArchive({
671
+ session,
672
+ sessionId: resolvedSessionId,
673
+ before: messages,
674
+ after: transcript,
675
+ at: now,
676
+ });
677
+ } catch (archiveError) {
678
+ writeStderrBestEffort(
679
+ `[session] compaction not applied (sess=${resolvedSessionId || 'unknown'}): ${archiveError?.message || archiveError}\n`
680
+ );
681
+ return recordFailedCompaction(plan, { compactError: archiveError, freshContextError: null }, compactStartedAt);
682
+ }
683
+ }
662
684
  session.messages = transcript;
663
685
  // The compacted transcript breaks the cache anyway: a staged working-folder
664
686
  // change re-roots the system prompt now and needs no notice any more.
@@ -677,18 +699,8 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
677
699
  outcome: compactionOutcome(session, freshContextResult, changed, now),
678
700
  });
679
701
  // Display-only: the dropped conversation stays readable above the divider.
680
- // Only the selection and the segment number happen here; the projection and
681
- // the write run later, off this commit.
682
- const archived =
683
- changed && plan.archive
684
- ? scheduleCompactionArchive({
685
- session,
686
- sessionId: resolvedSessionId,
687
- before: messages,
688
- after: transcript,
689
- at: now,
690
- })
691
- : null;
702
+ // The publication runs later, off this commit.
703
+ const archived = archive ? { ...archive, written: archive.start() } : null;
692
704
  if (changed) {
693
705
  invalidateProviderContextBaseline(session);
694
706
  if (postCompactPolicy) {
@@ -8,7 +8,8 @@
8
8
  // The chain applies only while the session runs on the owner's configured
9
9
  // primary model. A session the user put on another model (composer picker,
10
10
  // New Task) is a deliberate choice and gets none. Route changes recompute it
11
- // (updateSessionRoute `routeFallbacks`); a fallback hop does not.
11
+ // (updateSessionRoute `routeFallbacks`); a fallback hop reads it from the
12
+ // owner's route at that moment (route-fallback-chain.mjs).
12
13
  import { loadConfig } from '../../config.mjs';
13
14
  import { findPreset } from '../../runtime-core/config-helpers.mjs';
14
15
  import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
@@ -17,26 +18,35 @@ import { storedBotRoute } from '../../../../shared/agent-store.mjs';
17
18
  import { configuredAgentRouteCandidates } from '../../../../shared/agent-route-config.mjs';
18
19
  import { CODING_MODE } from '../agent-mode.mjs';
19
20
 
20
- export function ownerRouteFallbacks({ sessionMode, agentId, provider, model, config = null }) {
21
- if (!agentId) return [];
21
+ /** Whether two routes name the same model (provider aliases folded). */
22
+ export const sameRouteModel = (a, b) =>
23
+ Boolean(a?.provider && a?.model) &&
24
+ canonicalRouteProvider(a.provider) === canonicalRouteProvider(b?.provider) &&
25
+ String(a.model) === String(b?.model);
26
+
27
+ /** The owner's stored route (primary plus `fallbacks`), normalized; null when
28
+ * the owner has none or it cannot be read. */
29
+ export function ownerConfiguredRoute({ sessionMode, agentId, config = null }) {
30
+ if (!agentId) return null;
22
31
  const cfg = () => config || loadConfig({ secrets: false });
23
32
  let stored;
24
33
  if (sessionMode === CODING_MODE) {
25
- stored = findPreset(cfg(), cfg()?.default);
34
+ const loaded = cfg();
35
+ stored = findPreset(loaded, loaded?.default);
26
36
  } else {
27
37
  // Before the bot split a bot's route is still in the config (Mixy's never).
28
38
  stored = storedBotRoute(agentId) || (agentId === 'lead' ? null : configuredAgentRouteCandidates(cfg(), agentId)[0]);
29
39
  }
30
- if (!stored?.fallbacks?.length) return [];
31
- let route;
40
+ if (!stored) return null;
32
41
  try {
33
- route = normalizeWorkflowRoute(stored);
42
+ return normalizeWorkflowRoute(stored) || null;
34
43
  } catch {
35
- return [];
44
+ return null;
36
45
  }
37
- const onPrimary =
38
- route &&
39
- canonicalRouteProvider(route.provider) === canonicalRouteProvider(provider) &&
40
- String(route.model) === String(model);
41
- return onPrimary ? route.fallbacks || [] : [];
46
+ }
47
+
48
+ export function ownerRouteFallbacks({ sessionMode, agentId, provider, model, config = null }) {
49
+ const route = ownerConfiguredRoute({ sessionMode, agentId, config });
50
+ if (!route?.fallbacks?.length) return [];
51
+ return sameRouteModel(route, { provider, model }) ? route.fallbacks : [];
42
52
  }