mixdog 1.3.5 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -453,8 +453,9 @@ function callArgs(call) {
453
453
  return args && typeof args === 'object' ? args : {};
454
454
  }
455
455
 
456
- /** The paths one tool call wrote or produced. */
457
- function toolCallOutputs(call) {
456
+ /** The paths one tool call wrote or produced; `written`: only those it
457
+ * actually created or modified (opening a file in visualize does neither). */
458
+ function toolCallOutputs(call, written) {
458
459
  const name = String(call?.function?.name || call?.name || '');
459
460
  const args = callArgs(call);
460
461
  const text = (value) => (typeof value === 'string' && value.trim() ? [value.trim()] : []);
@@ -464,7 +465,7 @@ function toolCallOutputs(call) {
464
465
  if (name === 'office') return OFFICE_WRITE_ACTIONS.has(args.action) ? [...text(args.output), ...text(args.path)] : [];
465
466
  if (name === 'visualize') {
466
467
  const input = args.input && typeof args.input === 'object' ? args.input : {};
467
- const targets = args.action === 'open' && Array.isArray(input.targets) ? input.targets : [];
468
+ const targets = !written && args.action === 'open' && Array.isArray(input.targets) ? input.targets : [];
468
469
  return [
469
470
  ...text(input.output),
470
471
  ...targets.filter((target) => typeof target === 'string' && !/^[a-z][a-z0-9+.-]*:\/\//i.test(target)),
@@ -520,8 +521,9 @@ export function turnSlice(messages, { runId = '', since = 0 } = {}) {
520
521
 
521
522
  /** The candidate artifact paths of a turn's messages, most relevant first:
522
523
  * what its final reply names, then media/office/visualize outputs, then the
523
- * files it edited. */
524
- export function turnArtifactCandidates(messages) {
524
+ * files it edited. `written`: only the files it created or modified (no
525
+ * reply paths, no visualize open targets). */
526
+ export function turnArtifactCandidates(messages, { written = false } = {}) {
525
527
  const list = Array.isArray(messages) ? messages : [];
526
528
  const reply = list.findLast((message) => message?.role === 'assistant' && textOf(message.content).trim());
527
529
  const produced = [];
@@ -535,10 +537,10 @@ export function turnArtifactCandidates(messages) {
535
537
  : [];
536
538
  for (const call of calls) {
537
539
  const name = String(call?.function?.name || call?.name || '');
538
- (name === 'edit' || name === 'apply_patch' ? edited : produced).push(...toolCallOutputs(call));
540
+ (name === 'edit' || name === 'apply_patch' ? edited : produced).push(...toolCallOutputs(call, written));
539
541
  }
540
542
  }
541
- return [...(reply ? replyPaths(textOf(reply.content)) : []), ...produced, ...edited];
543
+ return [...(!written && reply ? replyPaths(textOf(reply.content)) : []), ...produced, ...edited];
542
544
  }
543
545
 
544
546
  /**
@@ -549,38 +551,16 @@ export function turnArtifactCandidates(messages) {
549
551
  * the roots are left out silently; at most HANDOFF_LIMITS.count are kept.
550
552
  */
551
553
  export function collectTurnArtifacts(messages, { cwd = '', roots = [] } = {}) {
552
- const seen = new Set();
553
554
  const files = [];
554
555
  let omitted = 0;
555
556
  const budget = createBudget();
556
- for (const candidate of turnArtifactCandidates(messages)) {
557
- // A candidate is a lexical path; its canonical form dedupes after the read.
558
- const lexical = samePlatformPath(
559
- isAbsolute(candidate) ? resolve(candidate) : resolve(cwd || process.cwd(), candidate)
560
- );
561
- if (seen.has(lexical)) continue;
562
- seen.add(lexical);
563
- let file;
564
- try {
565
- file = readContained(candidate, { cwd, roots, surface: 'artifact' });
566
- } catch {
567
- continue;
568
- }
569
- const key = samePlatformPath(file.path);
570
- if (key !== lexical && seen.has(key)) continue;
571
- seen.add(key);
557
+ for (const { file } of readableCandidates(turnArtifactCandidates(messages), { cwd, roots })) {
572
558
  if (files.length >= HANDOFF_LIMITS.count) {
573
559
  omitted += 1;
574
560
  continue;
575
561
  }
576
562
  const { type, bytes } = file;
577
- const entry = {
578
- path: file.path,
579
- name: file.name,
580
- kind: type?.kind || 'file',
581
- mimeType: type?.mimeType || 'application/octet-stream',
582
- size: file.size,
583
- };
563
+ const entry = pathEntry(file);
584
564
  const storable = bytes && (type.kind !== 'image' || SUPPORTED_IMAGE_MIME_TYPES.has(type.mimeType));
585
565
  if (storable && budget.take(type.kind, bytes.length)) {
586
566
  try {
@@ -591,3 +571,74 @@ export function collectTurnArtifacts(messages, { cwd = '', roots = [] } = {}) {
591
571
  }
592
572
  return { files, omitted };
593
573
  }
574
+
575
+ /**
576
+ * The files `messages` (one turn) created or modified, inside `roots`, that
577
+ * `report` (the text handed to the Lead) does not name: `{ files, omitted }`
578
+ * as path-only entries, which the Lead forwards by path. Nothing is stored.
579
+ */
580
+ export function collectUnreportedWrites(messages, { cwd = '', roots = [], report }) {
581
+ const found = [...readableCandidates(turnArtifactCandidates(messages, { written: true }), { cwd, roots })];
582
+ const unnamed = found.filter((item) => !reportNames(report, item, found, cwd));
583
+ return {
584
+ files: unnamed.slice(0, HANDOFF_LIMITS.count).map(({ file }) => pathEntry(file)),
585
+ omitted: Math.max(0, unnamed.length - HANDOFF_LIMITS.count),
586
+ };
587
+ }
588
+
589
+ const lexicalKey = (path, cwd) => samePlatformPath(resolve(cwd || process.cwd(), path));
590
+
591
+ const pathEntry = (file) => ({
592
+ path: file.path,
593
+ name: file.name,
594
+ kind: file.type?.kind || 'file',
595
+ mimeType: file.type?.mimeType || 'application/octet-stream',
596
+ size: file.size,
597
+ });
598
+
599
+ /** The distinct files among `candidates` that can be read inside `roots`, with
600
+ * their lexical and canonical keys. A candidate is a lexical path; its
601
+ * canonical form dedupes after the read. */
602
+ function* readableCandidates(candidates, { cwd, roots }) {
603
+ const seen = new Set();
604
+ for (const candidate of candidates) {
605
+ const lexical = lexicalKey(candidate, cwd);
606
+ if (seen.has(lexical)) continue;
607
+ seen.add(lexical);
608
+ let file;
609
+ try {
610
+ file = readContained(candidate, { cwd, roots, surface: 'artifact' });
611
+ } catch {
612
+ continue;
613
+ }
614
+ const key = samePlatformPath(file.path);
615
+ if (key !== lexical && seen.has(key)) continue;
616
+ seen.add(key);
617
+ yield { file, lexical, key };
618
+ }
619
+ }
620
+
621
+ /** Whether `report` names the produced file `item` among `found`: by its full
622
+ * path or a cwd-relative path resolving to it exactly, or by its bare file name
623
+ * when no other produced file shares that name. */
624
+ function reportNames(report, { file, lexical, key }, found, cwd) {
625
+ const flat = (value) => String(value).replaceAll('\\', '/');
626
+ const filePath = flat(file.path);
627
+ if (flat(report).includes(filePath)) return true;
628
+ const plain = report
629
+ .split(/[\s`"'<>()[\],;|]+/)
630
+ .map((token) => token.replace(/:\d+(?::\d+)?$/, '').replace(/[.:!?]+$/, ''))
631
+ .filter((token) => /\.[A-Za-z0-9]{1,8}$/.test(token) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(token));
632
+ const refs = new Set([...replyPaths(report), ...plain]);
633
+ const unique = found.filter((other) => other.file.name === file.name).length === 1;
634
+ for (const ref of refs) {
635
+ const normalized = flat(ref).replace(/^(?:\.\/)+/, '');
636
+ if (!normalized.includes('/')) {
637
+ if (unique && normalized === file.name) return true;
638
+ continue;
639
+ }
640
+ const refKey = lexicalKey(ref, cwd);
641
+ if (refKey === lexical || refKey === key) return true;
642
+ }
643
+ return false;
644
+ }
@@ -1,40 +1,85 @@
1
1
  // ask_user: put 1-4 multiple-choice questions to the user through the session's
2
2
  // tool-approval hook (kind: 'question') and report the answers to the model.
3
+ // handoff_user (the secret / takeover handoff) shares this module's human-gate code.
3
4
  import { randomUUID } from 'node:crypto';
4
5
  import {
5
- validateAskUserHandoff,
6
+ validateUserHandoff,
6
7
  validateAskUserQuestions,
7
8
  ASK_USER_MAX_TEXT,
8
9
  } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
9
10
  import { createCallerContextResolvers } from './caller-context.mjs';
10
11
  import { runAbortable } from '../../runtime/shared/abort-race.mjs';
11
- import { SECRET_REQUEST_TIMEOUT_MS } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
12
+ import {
13
+ HANDOFF_OUTCOMES as AGENT_OUTCOMES,
14
+ SECRET_REQUEST_TIMEOUT_MS,
15
+ } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
12
16
  import {
13
17
  agentIdForSession,
14
18
  recordHumanDecision,
15
19
  unattendedApprovalPort,
16
20
  } from '../../runtime/agent/orchestrator/session/human-gate/gate.mjs';
17
- import { browserSecretInput } from '../../runtime/browser-bridge/client.mjs';
18
- import { computerSecretInput } from '../../runtime/computer-bridge/client.mjs';
21
+ import { browserHandoffPage, browserSecretInput } from '../../runtime/browser-bridge/client.mjs';
22
+ import { computerSecretBind, computerSecretInput } from '../../runtime/computer-bridge/client.mjs';
23
+
24
+ /** The settle reason of a takeover that expired (desktop takeover-gate.ts
25
+ * TAKEOVER_EXPIRED_REASON). */
26
+ const TAKEOVER_EXPIRED_REASON = 'expired';
27
+
28
+ /**
29
+ * The agent's result of a secret request, from the card's answer and the
30
+ * desktop's fill: one of requests.mjs HANDOFF_OUTCOMES. `fill` runs only for a provided
31
+ * value and answers 'entered' / 'no_field' / 'page_changed' / 'blocked' /
32
+ * 'expired'.
33
+ */
34
+ export async function secretOutcome(result, fill) {
35
+ if (result?.timedOut === true || result?.reason === TAKEOVER_EXPIRED_REASON) return 'expired';
36
+ if (result?.approved !== true || result.response !== 'provide-secret') return 'user_skipped';
37
+ const filled = await fill();
38
+ return AGENT_OUTCOMES.includes(filled) && filled !== 'user_skipped' ? filled : 'blocked';
39
+ }
40
+
41
+ /** A page address as the agent may see it: origin and path, no query or
42
+ * fragment (a sign-in redirect can carry a code or token there). */
43
+ function factUrl(value) {
44
+ try {
45
+ const url = new URL(String(value || ''));
46
+ return url.protocol === 'http:' || url.protocol === 'https:' ? `${url.origin}${url.pathname}` : '';
47
+ } catch {
48
+ return '';
49
+ }
50
+ }
51
+
52
+ /** The agent's result of a takeover: what the user did and where the page is
53
+ * now, never "signed in" (the agent checks the page). */
54
+ export function takeoverOutcome(result, pageUrl = '') {
55
+ if (result?.timedOut === true || result?.reason === TAKEOVER_EXPIRED_REASON) return 'expired';
56
+ if (result?.approved !== true) return 'user_skipped';
57
+ const url = factUrl(pageUrl);
58
+ const did = result.reason === 'saved_login' ? 'User chose a saved login; it was filled in' : 'User pressed Done';
59
+ return url ? `${did}; page now at ${url}` : did;
60
+ }
19
61
 
20
62
  /**
21
- * A secret or takeover handoff. Secret: the card collects the value on the
22
- * desktop, which holds it for `requestId` and fills it into the target field
23
- * (isolated-world fill for the browser, one-shot typed input for computer
24
- * use); here only the verdict travels, and the result is exactly 'provided',
25
- * 'skipped' or 'timeout'. Takeover: the run waits until the user says done
26
- * (computer and browser actions stay blocked meanwhile, desktop
27
- * main/takeover-gate.ts); the result is 'done' or 'denied'.
63
+ * A secret or takeover handoff. Secret: at request time the desktop binds the
64
+ * request to its page ({pageId, origin}) or, for computer use, it is bound to
65
+ * {window_id, ref}; the card collects the value on the desktop, which holds it
66
+ * for `requestId` and fills only that page, only while its origin is
67
+ * unchanged. Here only the outcome travels (secretOutcome). Takeover: the run
68
+ * waits until the user says done (that session's actions stay blocked
69
+ * meanwhile, desktop main/takeover-gate.ts); the result is takeoverOutcome's.
28
70
  */
29
71
  export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId, signal, inputs = {} }) {
30
72
  const secretInput = {
31
73
  browser: inputs.browser || browserSecretInput,
32
74
  computer: inputs.computer || computerSecretInput,
33
75
  };
76
+ const page = inputs.page || browserHandoffPage;
77
+ const bindComputer = inputs.computerBind || computerSecretBind;
78
+ const identity = handoff.identity || {};
34
79
  const gate = {
35
80
  agentId: agentIdForSession(sessionId),
36
81
  kind: handoff.kind,
37
- tool: `ask_user:${handoff.surface}`,
82
+ tool: `handoff_user:${handoff.surface}`,
38
83
  target: handoff.target || handoff.field || '',
39
84
  };
40
85
  // A schedule or webhook run has nobody to take over: the step ends it now,
@@ -47,43 +92,96 @@ export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId,
47
92
  return UNATTENDED_TAKEOVER;
48
93
  }
49
94
  if (typeof hook !== 'function') {
50
- // Nobody can take over here: blocked, never "proceed".
95
+ // Nobody can take over or type here: blocked, never "proceed".
51
96
  if (handoff.kind === 'takeover') return NO_TAKEOVER;
52
- await recordHumanDecision(gate, { response: 'skipped' });
53
- return 'skipped';
97
+ await recordHumanDecision(gate, { response: 'blocked' });
98
+ return 'blocked';
54
99
  }
55
100
  if (handoff.kind === 'secret') {
56
101
  const requestId = `secret_${randomUUID()}`;
102
+ // Bound now: the page (and its origin) the value may go to, and what its
103
+ // field is. A browser page that cannot be bound, or has no such field,
104
+ // never asks the user for a value it could not place.
105
+ let bound = {};
106
+ if (handoff.surface === 'browser') {
107
+ const facts = await page(
108
+ sessionId,
109
+ 'secret_bind',
110
+ { request_id: requestId, ...(identity.tab ? { tab: identity.tab } : {}), field: handoff.field },
111
+ { signal }
112
+ );
113
+ let outcome = '';
114
+ if (!facts?.origin) outcome = 'blocked';
115
+ else if (!facts.form) outcome = 'no_field';
116
+ if (outcome) {
117
+ await recordHumanDecision(gate, { response: outcome });
118
+ return outcome;
119
+ }
120
+ bound = { origin: facts.origin, form: facts.form, ...(facts.length ? { length: facts.length } : {}) };
121
+ } else {
122
+ // Computer Use: bound to the exact window and field ref the agent named
123
+ // (from snapshot/find). Without both there is no field to confirm, and
124
+ // none is made up (never the focused field or the front window).
125
+ const outcome =
126
+ identity.windowId && identity.ref
127
+ ? await bindComputer(sessionId, requestId, { windowId: identity.windowId, ref: identity.ref, signal })
128
+ : 'no_field';
129
+ if (outcome !== 'bound') {
130
+ const refused = outcome === 'no_field' ? 'no_field' : 'blocked';
131
+ await recordHumanDecision(gate, { response: refused });
132
+ return refused;
133
+ }
134
+ }
57
135
  const result = await runAbortable(signal, () =>
58
136
  hook({
59
- name: 'ask_user',
137
+ name: 'handoff_user',
60
138
  kind: 'secret',
61
- secret: { requestId, surface: handoff.surface, target: handoff.target, field: handoff.field },
139
+ secret: { requestId, surface: handoff.surface, target: handoff.target, field: handoff.field, ...bound },
62
140
  reason: '',
63
141
  timeoutMs: SECRET_REQUEST_TIMEOUT_MS,
64
142
  sessionId,
65
143
  toolCallId,
66
144
  })
67
145
  );
68
- let verdict = 'skipped';
69
- if (result?.timedOut === true) verdict = 'timeout';
70
- else if (result?.approved === true && result.response === 'provide-secret') {
71
- verdict = await secretInput[handoff.surface](sessionId, requestId, { signal });
72
- }
73
- await recordHumanDecision(gate, { response: verdict, device: result?.device });
74
- return verdict;
146
+ const computerTarget = {
147
+ ...(identity.ref ? { ref: identity.ref } : {}),
148
+ ...(identity.windowId ? { windowId: identity.windowId } : {}),
149
+ };
150
+ const outcome = await secretOutcome(result, () =>
151
+ secretInput[handoff.surface](sessionId, requestId, {
152
+ signal,
153
+ ...(handoff.surface === 'computer' ? computerTarget : {}),
154
+ })
155
+ );
156
+ await recordHumanDecision(gate, { response: outcome, device: result?.device });
157
+ return outcome;
75
158
  }
159
+ // A sign-in page's saved logins (masked) let the card fill one for the
160
+ // user: the desktop offers them only for a page on that origin, bound now
161
+ // under `fillId` so the fill reaches that page alone (host-agent.ts fillBoundLogin).
162
+ const fillId = handoff.login ? `secret_${randomUUID()}` : '';
163
+ const accounts = handoff.login
164
+ ? (
165
+ await page(
166
+ sessionId,
167
+ 'login_accounts',
168
+ { url: handoff.login.url, request_id: fillId, ...(identity.tab ? { tab: identity.tab } : {}) },
169
+ { signal }
170
+ )
171
+ )?.accounts
172
+ : null;
76
173
  const result = await runAbortable(signal, () =>
77
174
  hook({
78
- name: 'ask_user',
175
+ name: 'handoff_user',
79
176
  kind: 'takeover',
80
177
  takeover: {
81
178
  surface: handoff.surface,
82
179
  instructions: handoff.instructions,
83
180
  target: handoff.target,
84
- ...handoff.identity,
181
+ ...identity,
85
182
  // A login takeover: the desktop opens its sign-in window on this page.
86
183
  ...(handoff.login ? { purpose: 'login', url: handoff.login.url } : {}),
184
+ ...(Array.isArray(accounts) && accounts.length ? { accounts, fillId } : {}),
87
185
  },
88
186
  args: { surface: handoff.surface, instructions: handoff.instructions },
89
187
  reason: '',
@@ -92,8 +190,15 @@ export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId,
92
190
  toolCallId,
93
191
  })
94
192
  );
95
- const answer = result?.approved === true ? 'done' : 'denied';
96
- await recordHumanDecision(gate, { response: answer, device: result?.device });
193
+ const pageUrl =
194
+ result?.approved === true && handoff.surface === 'browser'
195
+ ? (await page(sessionId, 'page_url', identity.tab ? { tab: identity.tab } : {}, { signal }))?.url
196
+ : '';
197
+ const answer = takeoverOutcome(result, pageUrl);
198
+ await recordHumanDecision(gate, {
199
+ response: answer.startsWith('User ') ? 'done' : answer,
200
+ device: result?.device,
201
+ });
97
202
  return answer;
98
203
  }
99
204
 
@@ -125,7 +230,7 @@ const DISMISSED =
125
230
  'User closed the questions without answering. Proceed with your best judgment; ask again only if something new depends on it.';
126
231
 
127
232
  /** Wire answers -> array aligned with questions; invalid entries become null. */
128
- export function normalizeAskUserAnswers(answers, questions) {
233
+ function normalizeAskUserAnswers(answers, questions) {
129
234
  const list = Array.isArray(answers) ? answers.slice(0, 4) : [];
130
235
  return questions.map((q, i) => {
131
236
  const entry = list[i];
@@ -143,7 +248,7 @@ export function normalizeAskUserAnswers(answers, questions) {
143
248
 
144
249
  // Strings are JSON-quoted so a quote inside one cannot end it early; typed
145
250
  // text stays apart from the picked labels (`"picked" + "typed" (typed)`).
146
- export function formatAskUserAnswers(questions, answers) {
251
+ function formatAskUserAnswers(questions, answers) {
147
252
  const quote = (text) => JSON.stringify(text);
148
253
  const parts = questions.map((q, i) => {
149
254
  const a = answers[i];
@@ -157,14 +262,14 @@ export function formatAskUserAnswers(questions, answers) {
157
262
 
158
263
  /** A composer message sent instead of (or with) picking: kept when it is a
159
264
  * non-empty string within the typed-answer limit, else dropped. */
160
- export function normalizeAskUserReply(reply) {
265
+ function normalizeAskUserReply(reply) {
161
266
  if (typeof reply !== 'string' || reply.length > ASK_USER_MAX_TEXT) return '';
162
267
  return reply.trim();
163
268
  }
164
269
 
165
270
  // `User replied in chat: "<reply>". ` leads, then the usual picks sentence
166
271
  // when any question was answered, else just the closing sentence.
167
- export function formatAskUserResult(questions, answers, reply) {
272
+ function formatAskUserResult(questions, answers, reply) {
168
273
  if (!reply) return formatAskUserAnswers(questions, answers);
169
274
  const lead = `User replied in chat: ${JSON.stringify(reply)}. `;
170
275
  if (answers.some(Boolean)) return `${lead}${formatAskUserAnswers(questions, answers)}`;
@@ -174,16 +279,14 @@ export function formatAskUserResult(questions, answers, reply) {
174
279
  export function createAskUserToolHandlers({ rt }) {
175
280
  const { sessionIdFor, signalFor } = createCallerContextResolvers(rt);
176
281
  return {
282
+ handoff_user: async (args, { callerCtx }) =>
283
+ runAskUserHandoff(validateUserHandoff(args), {
284
+ hook: callerCtx?.toolApprovalHook,
285
+ sessionId: sessionIdFor(callerCtx),
286
+ toolCallId: callerCtx?.toolCallId || null,
287
+ signal: signalFor(callerCtx),
288
+ }),
177
289
  ask_user: async (args, { callerCtx }) => {
178
- const handoff = validateAskUserHandoff(args);
179
- if (handoff) {
180
- return runAskUserHandoff(handoff, {
181
- hook: callerCtx?.toolApprovalHook,
182
- sessionId: sessionIdFor(callerCtx),
183
- toolCallId: callerCtx?.toolCallId || null,
184
- signal: signalFor(callerCtx),
185
- });
186
- }
187
290
  const questions = validateAskUserQuestions(args);
188
291
  const hook = callerCtx?.toolApprovalHook;
189
292
  if (typeof hook !== 'function') return NO_USER;
@@ -45,6 +45,8 @@ export function createBridgeToolHandlers({ rt }) {
45
45
  cwd: callerCwd,
46
46
  requestApproval: callerCtx?.toolApprovalHook,
47
47
  toolCallId: callerCtx?.toolCallId || null,
48
+ // The agent turn, as for the browser: it scopes a saved-login fill's protection.
49
+ turnId: callerCtx?.turnId || rt.session?.usageMetricsTurnId,
48
50
  signal: signalFor(callerCtx),
49
51
  });
50
52
  };
@@ -81,6 +83,7 @@ export function createBridgeToolHandlers({ rt }) {
81
83
  }
82
84
  return await executeVisualizeTool(args, {
83
85
  sessionId: sessionIdFor(callerCtx),
86
+ callId: callerCtx?.toolCallId,
84
87
  cwd: callerCwd,
85
88
  signal: signalFor(callerCtx),
86
89
  });
@@ -4,7 +4,10 @@
4
4
  // the message is the user's row in the room's log. Never a coding session, a
5
5
  // delegated worker, or a run nobody attends.
6
6
  import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
7
- import { noteReactionTarget, validateReactionArgs } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
7
+ import {
8
+ noteReactionTarget,
9
+ validateReactionArgs,
10
+ } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
8
11
  import { agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
9
12
  import { roomMemberOfSession, roomSessionId } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
10
13
  import { reactInSession } from '../../runtime/agent/orchestrator/session/message-reactions.mjs';
@@ -17,6 +17,7 @@ import { scheduleCallerScope } from '../services/schedule-access.mjs';
17
17
  import { worktreeCallerScope } from '../services/worktree-access.mjs';
18
18
  import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
19
19
  import { delegationChainOf } from '../bot-chain.mjs';
20
+ import { BOT_TARGET_ACTIONS, resolveBotTarget } from '../bot-target.mjs';
20
21
 
21
22
  const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
22
23
 
@@ -50,6 +51,7 @@ export function createWorkspaceToolHandlers({
50
51
  getSessionManager = () => null,
51
52
  getLifecycleApi = () => null,
52
53
  executeSessionControl = null,
54
+ listBots,
53
55
  }) {
54
56
  const assertLead = (action, callerCtx) => {
55
57
  const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
@@ -110,12 +112,16 @@ export function createWorkspaceToolHandlers({
110
112
  'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
111
113
  );
112
114
  }
115
+ // A bot id names that bot's home chat; every check below and in the
116
+ // control runs on the chat it resolves to.
117
+ const sessionTarget =
118
+ BOT_TARGET_ACTIONS.has(action) && target ? await resolveBotTarget(target, listBots) : target;
113
119
  // A task session reaches its agent only through its reported result.
114
120
  const origin = rt.session?.taskOrigin;
115
121
  if (
116
122
  action === 'send' &&
117
123
  origin &&
118
- (target === origin.sessionId || agentIdOfHomeSession(target) === origin.agentId)
124
+ (sessionTarget === origin.sessionId || agentIdOfHomeSession(sessionTarget) === origin.agentId)
119
125
  ) {
120
126
  throw new Error('session: a task session reports to its agent only through its result');
121
127
  }
@@ -129,7 +135,7 @@ export function createWorkspaceToolHandlers({
129
135
  const attachmentParts = callerAttachmentParts(rt, args, callerCwd, 'session');
130
136
  return JSON.stringify(
131
137
  await executeSessionControl(
132
- { ...rest, action },
138
+ { ...rest, action, ...(sessionTarget !== target ? { session_id: sessionTarget } : {}) },
133
139
  {
134
140
  callerSessionId,
135
141
  callerAgentId,
@@ -150,14 +150,19 @@ export function buildColdHeir(source, now = Date.now()) {
150
150
  // earlier folder) is the source's own: the heir's prompt is built for the
151
151
  // folder it actually starts in. Project memory follows the Project, which a
152
152
  // worktree shares, so the source's current memory slice carries over.
153
+ // In the same folder only the environment facts are rebuilt: they name the
154
+ // heir's own scratch directory, never the source's, while the inherited
155
+ // instruction files and memory stay as the source presented them.
153
156
  const pending = source.workspaceReroot;
154
157
  delete heir.workspaceReroot;
155
- if ((projectFolder || pending) && heir.cwd) {
156
- refreshSessionWorkspaceContext(heir, {
157
- cwd: heir.cwd,
158
- ...buildWorkspacePromptContext(heir, heir.cwd),
159
- coreMemoryContext: pending?.target?.coreMemoryContext,
160
- });
158
+ if (heir.cwd) {
159
+ const context = buildWorkspacePromptContext(heir, heir.cwd);
160
+ refreshSessionWorkspaceContext(
161
+ heir,
162
+ projectFolder || pending
163
+ ? { cwd: heir.cwd, ...context, coreMemoryContext: pending?.target?.coreMemoryContext }
164
+ : { cwd: heir.cwd, environmentContext: context.environmentContext }
165
+ );
161
166
  }
162
167
  invalidateContextUsageSnapshot(heir);
163
168
  invalidateProviderContextBaseline(heir);
@@ -15,6 +15,10 @@ import {
15
15
  inheritanceRouteTarget,
16
16
  } from '../inheritance-fit.mjs';
17
17
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
18
+ import {
19
+ rebindToChangedPrefix,
20
+ requestPrefixIdentity,
21
+ } from '../../runtime/agent/orchestrator/session/thinking-binding.mjs';
18
22
  import { buildColdHeir, committedSource, persistColdHeir } from './cold-heir.mjs';
19
23
 
20
24
  export function createInheritance(deps, { compactConversation, saveSession }) {
@@ -155,8 +159,17 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
155
159
  async function carryConversation(source, target, { compact = false } = {}) {
156
160
  // System blocks belong to the session that BUILT them: the target's own
157
161
  // prompt was composed for the current model, tool surface, and workflow.
158
- // Only the conversation itself travels.
159
- let carried = inheritableMessages(source.messages);
162
+ // Only the conversation itself travels — without its thinking replay when
163
+ // the heir's prompt or tools differ from the source's: that replay is
164
+ // bound to the source's prefix, so the provider would drop it (and every
165
+ // thinking block after it) or refuse it, while the heir's context estimate
166
+ // kept counting it (thinking-binding.mjs); an open tool round the source
167
+ // stopped in is ended for the heir. An identical prefix keeps it all.
168
+ const carrySource =
169
+ requestPrefixIdentity(source) === requestPrefixIdentity(target)
170
+ ? source
171
+ : { ...source, messages: rebindToChangedPrefix(source.messages) };
172
+ let carried = inheritableMessages(carrySource.messages);
160
173
  if (!hasUserConversationMessage(carried)) {
161
174
  throw new Error('inheritFrom: the source session has no conversation to carry');
162
175
  }
@@ -169,7 +182,7 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
169
182
  // compact:true summarizes even a conversation that fits; otherwise only
170
183
  // one that cannot fit is compacted.
171
184
  if (compact || (fit.known && !fit.fits)) {
172
- const next = await fitConversationForHeir(source, target, carried, fit, { required: compact });
185
+ const next = await fitConversationForHeir(carrySource, target, carried, fit, { required: compact });
173
186
  compacted = next !== carried;
174
187
  carried = next;
175
188
  }
@@ -21,7 +21,11 @@ import {
21
21
  wantsIncomingTail,
22
22
  } from '../../runtime/agent/orchestrator/session/store-summary-last-message.mjs';
23
23
  import { isRealTurnMessage } from '../../runtime/agent/orchestrator/session/compact/user-message-kind.mjs';
24
- import { realTurnCount as realTurnCountOf } from '../../runtime/agent/orchestrator/session/store-summary-fields.mjs';
24
+ import {
25
+ compactCount as compactCountOf,
26
+ compactCountField,
27
+ realTurnCount as realTurnCountOf,
28
+ } from '../../runtime/agent/orchestrator/session/store-summary-fields.mjs';
25
29
 
26
30
  const VISIBLE_OWNERS = ['cli', 'user', 'mixdog'];
27
31
 
@@ -77,7 +81,9 @@ export function leadSessionRow(s, working = false) {
77
81
  if (sourceType === 'exec') return null;
78
82
  if (!agentSession && !isLeadShaped(s, owner, sourceType)) return null;
79
83
  const { preview, messageCount, realTurnCount, incomingTail } = previewOf(s);
80
- if (!preview && messageCount === 0) return null;
84
+ // A titled one is not scratch: its prompt was rewound away (Esc-Esc message
85
+ // selector), so it stays listed rather than vanishing with its open tab.
86
+ if (!preview && messageCount === 0 && !cleanSessionPreview(s.title || '', 100)) return null;
81
87
  const lastMessageRoutine = normalizeRoutineProvenance(s.lastMessageRoutine);
82
88
  return {
83
89
  id: s.id,
@@ -105,6 +111,10 @@ export function leadSessionRow(s, working = false) {
105
111
  // sit among the counted ones: a bot row's unread pill counts those past
106
112
  // the read cursor. Absent when unknown; clients then show one.
107
113
  ...(incomingTail ? { incomingTail } : {}),
114
+ // The numbering epoch of messageCount: each compaction restarts it. A
115
+ // summary row carries it at the top; a stored session in its compaction
116
+ // record. Unknown stays absent.
117
+ ...compactCountField(compactCountOf(s) === null ? s.compaction : s),
108
118
  title: cleanSessionPreview(s.title || '', 100),
109
119
  preview,
110
120
  // The latest user or assistant line (an agent contact's row shows it).