mixdog 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/README.md +8 -4
  2. package/package.json +2 -2
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/src/defaults/shipped-history.json +5 -1
  5. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  6. package/src/defaults/skills/setup/SKILL.md +23 -1
  7. package/src/defaults/skills/setup/references/actions.md +50 -3
  8. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  9. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  10. package/src/defaults/skills/visualize/SKILL.md +9 -6
  11. package/src/defaults/skills/visualize/references/craft.md +8 -1
  12. package/src/help.mjs +1 -1
  13. package/src/lib/keychain-cjs.cjs +4 -1
  14. package/src/lib/rules-builder.cjs +14 -7
  15. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  16. package/src/rules/agent-mode/MIXY.md +6 -8
  17. package/src/rules/lead/LEAD.md +2 -2
  18. package/src/rules/shared/10-tool-workflow.md +4 -0
  19. package/src/rules/shared/20-research.md +2 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  23. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  24. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  25. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  26. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  27. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  28. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  29. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  30. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  31. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  32. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  33. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  34. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  35. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  36. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  39. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  40. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  41. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  45. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  46. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  47. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  48. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  49. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  50. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  51. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  53. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  54. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  55. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  56. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  57. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  58. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  59. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  60. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  61. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  66. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  67. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  69. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -5
  70. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  71. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  73. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  75. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  76. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  77. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +17 -2
  78. package/src/runtime/agent/orchestrator/session/agent-room.mjs +133 -38
  79. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  80. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  82. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  83. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  84. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  85. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  87. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  88. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  91. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  92. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +136 -32
  93. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  94. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  95. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  96. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  97. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  98. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  99. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  100. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  101. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  102. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
  103. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  104. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  105. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  108. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  109. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  110. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  113. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  114. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  115. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  116. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  117. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  119. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  120. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  121. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  122. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  123. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +3 -8
  124. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  125. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  126. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  127. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  128. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  129. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  130. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  131. package/src/runtime/agent/orchestrator/session/routine-result.mjs +120 -9
  132. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  134. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  135. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  136. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  137. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  138. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  139. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  140. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  141. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  142. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  143. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  144. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  145. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  146. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  147. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  148. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  149. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  150. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  151. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  152. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  153. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  154. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  156. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  157. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  158. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  159. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  160. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  161. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  162. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  163. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  164. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  165. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  166. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  167. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  168. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  169. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  170. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  171. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  173. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  174. package/src/runtime/attachments/limits.mjs +5 -1
  175. package/src/runtime/attachments/office-extract.mjs +3 -6
  176. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  177. package/src/runtime/attachments/store.mjs +49 -30
  178. package/src/runtime/browser-bridge/client.mjs +73 -20
  179. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  180. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  181. package/src/runtime/channels/lib/state-file.mjs +15 -15
  182. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  183. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  184. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  185. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  186. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  187. package/src/runtime/effort-judge/judge-client.mjs +5 -2
  188. package/src/runtime/effort-judge/model-install.mjs +16 -8
  189. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  190. package/src/runtime/local-provider/catalog.mjs +5 -2
  191. package/src/runtime/local-provider/registered-models.mjs +64 -27
  192. package/src/runtime/local-provider/server.mjs +2 -3
  193. package/src/runtime/media/adapters/image-api.mjs +89 -0
  194. package/src/runtime/media/auth.mjs +64 -1
  195. package/src/runtime/media/catalog.mjs +66 -4
  196. package/src/runtime/media/jobs.mjs +4 -0
  197. package/src/runtime/media/lanes.mjs +46 -5
  198. package/src/runtime/media/media-usage.mjs +17 -0
  199. package/src/runtime/media/renditions.mjs +15 -15
  200. package/src/runtime/media/store.mjs +36 -35
  201. package/src/runtime/media/tool.mjs +8 -1
  202. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  203. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  204. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  205. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  206. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  207. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  208. package/src/runtime/office/core/journal.mjs +7 -14
  209. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  210. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  211. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  212. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  213. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  214. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  215. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  216. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  217. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  218. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  219. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  220. package/src/runtime/shared/agent-store.mjs +24 -7
  221. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  222. package/src/runtime/shared/atomic-file.d.mts +50 -0
  223. package/src/runtime/shared/atomic-file.mjs +394 -132
  224. package/src/runtime/shared/background-tasks.mjs +7 -4
  225. package/src/runtime/shared/bot-hops.mjs +30 -0
  226. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  227. package/src/runtime/shared/bounded-download.mjs +4 -2
  228. package/src/runtime/shared/config.mjs +39 -9
  229. package/src/runtime/shared/defaults-separation.mjs +6 -2
  230. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  231. package/src/runtime/shared/err-text.mjs +4 -0
  232. package/src/runtime/shared/error-presentation.mjs +22 -4
  233. package/src/runtime/shared/file-has-data.mjs +28 -0
  234. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  235. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  236. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  237. package/src/runtime/shared/projects.mjs +50 -41
  238. package/src/runtime/shared/provider-accounts.mjs +17 -12
  239. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  240. package/src/runtime/shared/routine-trigger.mjs +14 -6
  241. package/src/runtime/shared/run-child.d.mts +3 -0
  242. package/src/runtime/shared/run-child.mjs +24 -3
  243. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  244. package/src/runtime/shared/singleton-owner.mjs +7 -6
  245. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  246. package/src/runtime/shared/staged-update.mjs +4 -2
  247. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  248. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  249. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  250. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  251. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  252. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  253. package/src/runtime/shared/user-cwd.mjs +9 -4
  254. package/src/runtime/shared/user-data-guard.mjs +41 -42
  255. package/src/runtime/shared/utf8-cut.mjs +10 -0
  256. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  257. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  258. package/src/runtime/tidy/core-install.mjs +26 -4
  259. package/src/runtime/tidy/install.mjs +91 -23
  260. package/src/runtime/web-search/lib/config.mjs +4 -16
  261. package/src/session-runtime/agent-home-delivery.mjs +555 -199
  262. package/src/session-runtime/agent-room-delivery.mjs +143 -36
  263. package/src/session-runtime/agent-task-sessions.mjs +667 -165
  264. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  265. package/src/session-runtime/boot/apis.mjs +45 -24
  266. package/src/session-runtime/boot/config.mjs +1 -0
  267. package/src/session-runtime/boot/facade.mjs +11 -1
  268. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  269. package/src/session-runtime/boot/providers.mjs +2 -2
  270. package/src/session-runtime/boot/shared.mjs +12 -6
  271. package/src/session-runtime/boot/tools.mjs +2 -2
  272. package/src/session-runtime/bot-status.mjs +432 -0
  273. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  274. package/src/session-runtime/config-lifecycle.mjs +60 -55
  275. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  276. package/src/session-runtime/held-requests.mjs +36 -33
  277. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  278. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  279. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +19 -4
  280. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  281. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  282. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  283. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  284. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  285. package/src/session-runtime/media-api.mjs +5 -2
  286. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  287. package/src/session-runtime/model-route/set-route.mjs +61 -1
  288. package/src/session-runtime/owned-automation-run.mjs +8 -1
  289. package/src/session-runtime/room-member-gate.mjs +16 -3
  290. package/src/session-runtime/route-resolution.mjs +16 -8
  291. package/src/session-runtime/route-state.mjs +1 -59
  292. package/src/session-runtime/routine-home-turn.mjs +5 -3
  293. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  294. package/src/session-runtime/runtime-paths.mjs +11 -5
  295. package/src/session-runtime/runtime-tunables.mjs +6 -0
  296. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  297. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  298. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  299. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  300. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  301. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  302. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  303. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  304. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  305. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  306. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  307. package/src/session-runtime/services/agent-tool.mjs +6 -0
  308. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  309. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  310. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  311. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  312. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  313. package/src/session-runtime/services/provider-admin.mjs +14 -4
  314. package/src/session-runtime/services/schedule-access.mjs +33 -4
  315. package/src/session-runtime/services/seeds.mjs +4 -3
  316. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  317. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  318. package/src/session-runtime/session-create/materialize.mjs +2 -3
  319. package/src/session-runtime/session-create/session-options.mjs +15 -2
  320. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  321. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  322. package/src/session-runtime/setup-tool/executor.mjs +8 -2
  323. package/src/session-runtime/setup-tool/settings-contract.mjs +31 -1
  324. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  325. package/src/session-runtime/skills-api.mjs +12 -5
  326. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  327. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  328. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  329. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  330. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  331. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  332. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  333. package/src/session-runtime/tool-policy-refresh.mjs +47 -369
  334. package/src/session-runtime/tool-surface.mjs +5 -3
  335. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  336. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  337. package/src/session-runtime/turn/turn-run.mjs +2 -1
  338. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  339. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +519 -333
  340. package/src/session-runtime/workflow-agents-api/agent-route.mjs +46 -46
  341. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  342. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  343. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  344. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  345. package/src/standalone/daemon-agent-control.mjs +4 -1
  346. package/src/standalone/daemon-session-control.mjs +51 -10
  347. package/src/standalone/daemon.mjs +28 -10
  348. package/src/standalone/local-session-runtime.mjs +22 -14
  349. package/src/standalone/session-client.mjs +7 -2
  350. package/src/standalone/session-protocol.mjs +7 -0
  351. package/src/standalone/session-service.mjs +11 -0
  352. package/src/standalone/session-state-projection.mjs +19 -9
  353. package/src/standalone/session-tool-output.mjs +35 -9
  354. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  355. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  356. package/src/tui/app/slash-commands.mjs +1 -1
  357. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  358. package/src/tui/dist/index.mjs +529 -333
  359. package/src/tui/prompt-history-store.mjs +16 -21
  360. package/src/tui/session/context-state.mjs +4 -0
  361. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  362. package/src/tui/session/session-api/media.mjs +2 -1
  363. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  364. package/src/tui/session/session-api/routes-write.mjs +203 -0
  365. package/src/tui/session/session-api/routes.mjs +23 -247
  366. package/src/tui/session/session-api/settings.mjs +14 -1
  367. package/src/tui/session/session-flow/drain.mjs +28 -3
  368. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  369. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  370. package/src/tui/session/session-flow/steering.mjs +8 -3
  371. package/src/tui/session/tool-approval.mjs +185 -20
  372. package/src/tui/session/transcript-intake.mjs +2 -0
  373. package/src/tui/session/turn.mjs +23 -14
  374. package/src/tui/transcript-tool-failures.mjs +8 -2
  375. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  376. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  377. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -5,26 +5,65 @@
5
5
  * active one into store state (state.toolApproval). They need live session
6
6
  * state, so the closure identifiers (state/set/nextId/disposed/timeout) are
7
7
  * threaded through the factory argument via getters — never stale snapshots.
8
+ *
9
+ * Delegation (`surfaceOf`, human-gate/gate.mjs approvalSurface): a sub-agent's
10
+ * approval is also offered to the top-level conversation the user watches
11
+ * (runtime/shared/delegated-approval-host.mjs), labelled with the agent chain.
12
+ * That session's queue holds a copy (`mirror`): it never times out, survives
13
+ * that session's own turn ending, and its answer is forwarded to the owning
14
+ * queue, so either place settles the one request once.
8
15
  */
9
16
  import {
10
17
  humanRequestFields,
11
18
  normalizeHumanResponse,
12
19
  } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
20
+ import {
21
+ delegateApproval,
22
+ delegatedApprovalsFor,
23
+ registerApprovalQueue,
24
+ withdrawDelegatedApproval,
25
+ } from '../../runtime/shared/delegated-approval-host.mjs';
26
+
27
+ /** Approvals a sub-agent shows in the top-level chat too; a question, a secret
28
+ * or a takeover stays in its own session (its answer or window is there). */
29
+ const DELEGATED_KINDS = new Set(['', 'approval', 'outbound', 'permission']);
13
30
 
14
- export function createToolApproval({ getState, set, nextId, getDisposed, timeoutMs }) {
31
+ /** The wait limit an approval has when its request names none, from
32
+ * MIXDOG_TOOL_APPROVAL_TIMEOUT_MS (milliseconds, one second at least): null —
33
+ * no limit — unless the variable sets one, which tests and ops use. An
34
+ * approval waits until the user answers, Stop/turn abort denies it, or its
35
+ * runtime closes. */
36
+ export function resolveToolApprovalTimeoutMs(env = process.env) {
37
+ const value = Number(env.MIXDOG_TOOL_APPROVAL_TIMEOUT_MS);
38
+ return Number.isFinite(value) && value > 0 ? Math.max(1000, Math.round(value)) : null;
39
+ }
40
+
41
+ export function createToolApproval({ getState, set, nextId, getDisposed, timeoutMs = null, surfaceOf = null }) {
42
+ const defaultTimeoutMs = Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0 ? Number(timeoutMs) : null;
15
43
  const toolApprovalQueue = [];
16
44
  let activeToolApproval = null;
45
+ const ownSessionId = () => String(getState()?.sessionId || '');
46
+ const unregister =
47
+ typeof surfaceOf === 'function'
48
+ ? registerApprovalQueue({
49
+ sessionId: ownSessionId,
50
+ disposed: () => getDisposed(),
51
+ offer: offerDelegatedApproval,
52
+ withdraw: withdrawDelegatedCopy,
53
+ })
54
+ : () => {};
17
55
 
18
56
  function normalizeToolApprovalRequest(input = {}, id = nextId()) {
19
57
  const now = Date.now();
20
58
  // An explicit `timeoutMs: null` means no wait limit (no timer, no expiry);
21
- // any other missing/invalid value falls back to the session default.
59
+ // any other missing/invalid value falls back to the session default,
60
+ // which is itself no limit unless the runtime configures one.
22
61
  const requestTimeoutMs =
23
62
  input?.timeoutMs === null
24
63
  ? null
25
64
  : Number.isFinite(Number(input?.timeoutMs)) && Number(input.timeoutMs) > 0
26
65
  ? Number(input.timeoutMs)
27
- : timeoutMs;
66
+ : defaultTimeoutMs;
28
67
  return {
29
68
  id,
30
69
  name: String(input?.name || input?.tool_name || 'tool'),
@@ -41,12 +80,14 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
41
80
  ...humanRequestFields(input),
42
81
  };
43
82
  }
44
- // Every request expires from ITS OWN requestedAt, queued or active. Arming
45
- // the timer only on presentation let a queued request sit past expiresAt
46
- // (retaining its promise and surfacing an obsolete approval later).
83
+ // A request that has a limit expires from ITS OWN requestedAt, queued or
84
+ // active. Arming the timer only on presentation let a queued request sit past
85
+ // expiresAt (retaining its promise and surfacing an obsolete approval later).
86
+ // One without a limit (expiresAt null — the default) arms nothing.
47
87
  function armToolApprovalTimeout(entry) {
48
- if (!entry || entry.timer || entry.request?.expiresAt === null) return;
49
- const remainingMs = Math.max(0, Number(entry.request?.expiresAt || 0) - Date.now());
88
+ // A copy expires with its owner, which withdraws it.
89
+ if (!entry || entry.mirror || entry.timer || !Number.isFinite(entry.request?.expiresAt)) return;
90
+ const remainingMs = Math.max(0, entry.request.expiresAt - Date.now());
50
91
  entry.timer = setTimeout(() => {
51
92
  finishToolApproval(entry.id, false, 'approval timed out', '', undefined, undefined, { timedOut: true });
52
93
  }, remainingMs);
@@ -55,21 +96,75 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
55
96
  // Settle one entry: drop its timeout, then hand the waiter its answer. The
56
97
  // resolve stays guarded — a waiter whose caller already went away must never
57
98
  // take the rest of the queue down with it.
58
- function settleToolApproval(entry, approved, reason, answers, reply, extra, device = '') {
99
+ function settleToolApproval(entry, approved, reason, answers, reply, extra, device = '', forwarded = false) {
59
100
  if (entry?.timer) clearTimeout(entry.timer);
60
101
  const result = { approved, reason };
61
102
  if (answers !== undefined) result.answers = answers;
62
103
  if (reply !== undefined) result.reply = reply;
63
- // A human-kind request answers with its canonical response (never a value).
64
- if (entry?.request?.remember) {
65
- Object.assign(result, normalizeHumanResponse(entry.request, approved, extra));
104
+ // A human-kind request answers with its canonical response (never a value),
105
+ // judged against what the answering place offered: this queue's own view,
106
+ // or — answered on its copy — the full request the copy already narrowed.
107
+ const answered = forwarded ? entry?.full || entry?.request : entry?.request;
108
+ if (answered?.remember) {
109
+ Object.assign(result, normalizeHumanResponse(answered, approved, extra));
66
110
  if (device) result.device = device;
67
111
  }
68
112
  if (extra?.timedOut === true) result.timedOut = true;
113
+ // Settled here: its copy in the top-level chat goes away.
114
+ if (entry?.delegated) withdrawDelegatedApproval(entry.id, { approved, device });
69
115
  try {
70
116
  entry.resolve(result);
71
117
  } catch {}
72
118
  }
119
+ /** Hand a copy's answer to the queue that owns the request; false when the
120
+ * owner had already settled it. */
121
+ function forwardToOwner(entry, approved, reason, device, answers, reply, extra) {
122
+ try {
123
+ return entry.mirror.answer(approved, reason, device, answers, reply, onlyOffered(entry.request, extra)) === true;
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ /** `request` as this session shows it: a remembering scope is offered only
129
+ * in the one session the request names for it (`rememberAt`, the bot's own
130
+ * chat); everywhere else — and when it names none — it asks once. */
131
+ function viewFor(request) {
132
+ if (!Array.isArray(request?.remember) || request.remember.length <= 1) return request;
133
+ const here = ownSessionId();
134
+ return here && request.rememberAt === here ? request : { ...request, remember: ['once'] };
135
+ }
136
+ /** `extra` without a remember scope `view` did not offer. */
137
+ function onlyOffered(view, extra) {
138
+ if (!extra || extra.remember === undefined) return extra;
139
+ return (view?.remember || ['once']).includes(extra.remember) ? extra : { ...extra, remember: 'once' };
140
+ }
141
+ function offerDelegatedApproval(delegated) {
142
+ if (getDisposed() || delegated.sessionId !== ownSessionId()) return;
143
+ if (activeToolApproval?.id === delegated.id || toolApprovalQueue.some((entry) => entry.id === delegated.id)) return;
144
+ toolApprovalQueue.push({ id: delegated.id, request: viewFor(delegated.request), mirror: delegated, timer: null });
145
+ presentNextToolApproval();
146
+ }
147
+ function withdrawDelegatedCopy(id, result) {
148
+ if (activeToolApproval?.mirror && activeToolApproval.id === id) {
149
+ activeToolApproval = null;
150
+ const device = String(result?.device || '');
151
+ set({
152
+ toolApproval: null,
153
+ toolApprovalResult: device ? { id, approved: result.approved === true, device, at: Date.now() } : null,
154
+ });
155
+ presentNextToolApproval();
156
+ return;
157
+ }
158
+ const index = toolApprovalQueue.findIndex((entry) => entry.mirror && entry.id === id);
159
+ if (index >= 0) toolApprovalQueue.splice(index, 1);
160
+ }
161
+ /** Show the delegated approvals waiting for this session (it loaded, or its
162
+ * session id became the top-level one after the request was made). */
163
+ function adoptDelegatedApprovals() {
164
+ const sessionId = ownSessionId();
165
+ if (!sessionId || typeof surfaceOf !== 'function') return;
166
+ for (const delegated of delegatedApprovalsFor(sessionId)) offerDelegatedApproval(delegated);
167
+ }
73
168
  function presentNextToolApproval() {
74
169
  if (activeToolApproval || getDisposed()) return;
75
170
  const entry = toolApprovalQueue.shift();
@@ -88,7 +183,8 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
88
183
  device = '',
89
184
  answers = undefined,
90
185
  reply = undefined,
91
- extra = undefined
186
+ extra = undefined,
187
+ { forwarded = false } = {}
92
188
  ) {
93
189
  const targetId = String(id || '');
94
190
  if (activeToolApproval && activeToolApproval.id === targetId) {
@@ -100,44 +196,113 @@ export function createToolApproval({ getState, set, nextId, getDisposed, timeout
100
196
  ? { id: targetId, approved: approved === true, device: String(device), at: Date.now() }
101
197
  : null;
102
198
  set({ toolApproval: null, toolApprovalResult });
103
- settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
199
+ let accepted = true;
200
+ if (entry.mirror) {
201
+ accepted = forwardToOwner(entry, approved, reason, device, answers, reply, extra);
202
+ } else {
203
+ settleToolApproval(
204
+ entry,
205
+ approved === true,
206
+ String(reason || ''),
207
+ answers,
208
+ reply,
209
+ extra,
210
+ String(device || ''),
211
+ forwarded
212
+ );
213
+ }
104
214
  presentNextToolApproval();
105
- return true;
215
+ return accepted;
106
216
  }
107
217
  const index = toolApprovalQueue.findIndex((entry) => entry.id === targetId);
108
218
  if (index >= 0) {
109
219
  const [entry] = toolApprovalQueue.splice(index, 1);
110
- settleToolApproval(entry, approved === true, String(reason || ''), answers, reply, extra, String(device || ''));
220
+ if (entry.mirror) return forwardToOwner(entry, approved, reason, device, answers, reply, extra);
221
+ settleToolApproval(
222
+ entry,
223
+ approved === true,
224
+ String(reason || ''),
225
+ answers,
226
+ reply,
227
+ extra,
228
+ String(device || ''),
229
+ forwarded
230
+ );
111
231
  return true;
112
232
  }
113
233
  return false;
114
234
  }
235
+ /** Deny this session's own requests. A copy of a sub-agent's request stays
236
+ * (that agent still waits) until this runtime closes, which drops it
237
+ * unanswered: the next runtime of this session adopts it again. */
115
238
  function denyAllToolApprovals(reason = 'approval cancelled') {
116
- if (activeToolApproval) {
239
+ const closing = getDisposed();
240
+ if (closing) unregister();
241
+ if (activeToolApproval && !(activeToolApproval.mirror && !closing)) {
117
242
  const entry = activeToolApproval;
118
243
  activeToolApproval = null;
119
- settleToolApproval(entry, false, reason);
244
+ if (!entry.mirror) settleToolApproval(entry, false, reason);
120
245
  }
246
+ const kept = [];
121
247
  while (toolApprovalQueue.length > 0) {
122
- settleToolApproval(toolApprovalQueue.shift(), false, reason);
248
+ const entry = toolApprovalQueue.shift();
249
+ if (!entry.mirror) settleToolApproval(entry, false, reason);
250
+ else if (!closing) kept.push(entry);
123
251
  }
252
+ toolApprovalQueue.push(...kept);
253
+ if (activeToolApproval) return;
124
254
  if (getState().toolApproval) set({ toolApproval: null });
255
+ presentNextToolApproval();
125
256
  }
126
257
  function requestToolApproval(input = {}) {
127
258
  if (getDisposed()) return Promise.resolve({ approved: false, reason: 'runtime disposed' });
128
259
  return new Promise((resolve) => {
129
260
  const id = nextId();
130
- const entry = { id, request: normalizeToolApprovalRequest(input, id), resolve, timer: null };
261
+ const full = normalizeToolApprovalRequest(input, id);
262
+ const surface = delegationSurface(full);
263
+ if (surface) {
264
+ // Who asks, from the top-level chat down ("사무관 › Worker"), and where the copy is.
265
+ full.chain = surface.chain;
266
+ full.surfaceSessionId = surface.sessionId;
267
+ }
268
+ // `request` is what this session shows (and accepts answers against);
269
+ // `full` what the copy in the top-level chat is made from.
270
+ const entry = { id, request: viewFor(full), full, resolve, timer: null, delegated: Boolean(surface) };
131
271
  toolApprovalQueue.push(entry);
132
272
  armToolApprovalTimeout(entry);
133
273
  presentNextToolApproval();
274
+ if (surface) {
275
+ delegateApproval({
276
+ id,
277
+ sessionId: surface.sessionId,
278
+ request: full,
279
+ answer: (approved, reason, device, answers, reply, extra) =>
280
+ finishToolApproval(id, approved, reason, device, answers, reply, extra, { forwarded: true }),
281
+ });
282
+ }
134
283
  });
135
284
  }
285
+ /** `{ sessionId, chain }` of the top-level chat a sub-agent's request also
286
+ * shows in, or null (no delegation, another kind, or this is the top). */
287
+ function delegationSurface(request) {
288
+ if (typeof surfaceOf !== 'function' || !DELEGATED_KINDS.has(request.kind)) return null;
289
+ const own = String(request.sessionId || ownSessionId());
290
+ let surface = null;
291
+ try {
292
+ surface = surfaceOf(own);
293
+ } catch {
294
+ return null;
295
+ }
296
+ const sessionId = String(surface?.sessionId || '');
297
+ if (!sessionId || sessionId === own || sessionId === ownSessionId()) return null;
298
+ return { sessionId, chain: Array.isArray(surface.chain) ? surface.chain.map(String) : [] };
299
+ }
136
300
 
137
301
  return {
138
302
  presentNextToolApproval,
139
303
  finishToolApproval,
140
304
  denyAllToolApprovals,
141
305
  requestToolApproval,
306
+ adoptDelegatedApprovals,
142
307
  };
143
308
  }
@@ -123,6 +123,8 @@ export function createTranscriptIntake({ getState, flags, pushItem, patchItem, t
123
123
  ...routineTriggerField(extra),
124
124
  ...(extra && typeof extra.sender === 'string' && extra.sender ? { sender: extra.sender } : {}),
125
125
  ...(extra && typeof extra.device === 'string' && extra.device ? { device: extra.device } : {}),
126
+ // The bot that sent this message (a bot's `session send`).
127
+ ...(extra && typeof extra.fromAgent === 'string' && extra.fromAgent ? { fromAgent: extra.fromAgent } : {}),
126
128
  // Delivered into the running turn (steering): the desktop row's read mark.
127
129
  ...(extra?.steered === true ? { steered: true } : {}),
128
130
  // Byte-free attachment metadata (name/mime/size) from the queue entry —
@@ -10,8 +10,9 @@ import { applyUsageDelta } from './session-stats.mjs';
10
10
  import {
11
11
  createTranscriptRouteMetadata,
12
12
  transcriptRouteMetadataFields,
13
+ transcriptSourceFields,
13
14
  } from '../../runtime/shared/transcript-metadata.mjs';
14
- import { publishModelSwitch } from './session-api/routes.mjs';
15
+ import { publishModelSwitch, publishSessionSwitches } from './session-api/routes.mjs';
15
16
  import { compactEventLabel } from '../../runtime/shared/compact-event-label.mjs';
16
17
  import { pickVerb, pickDoneVerb, compactEventDetail } from './labels.mjs';
17
18
  import { isCancelLikeError } from '../../runtime/shared/err-text.mjs';
@@ -172,6 +173,7 @@ function promptRestoreFor(displayText, options, submittedIds, content = null) {
172
173
  pastedImages: options.pastedImages && typeof options.pastedImages === 'object' ? options.pastedImages : null,
173
174
  pastedTexts: options.pastedTexts && typeof options.pastedTexts === 'object' ? options.pastedTexts : null,
174
175
  onCommitted: typeof options.onCommitted === 'function' ? options.onCommitted : null,
176
+ onAborted: typeof options.onAborted === 'function' ? options.onAborted : null,
175
177
  restorable: options.restorable !== false,
176
178
  submittedIds,
177
179
  // Which device submitted the prompt: only that device may get it back.
@@ -202,7 +204,7 @@ export function commitPromptRestore(flags, turn) {
202
204
  restore.content = null;
203
205
  }
204
206
 
205
- function steeringItemExtras(steeringMeta) {
207
+ export function steeringItemExtras(steeringMeta) {
206
208
  return {
207
209
  // The steered message's stable id, so a compaction that drops it greys
208
210
  // this row.
@@ -217,12 +219,7 @@ function steeringItemExtras(steeringMeta) {
217
219
  ? { execution: steeringMeta.execution }
218
220
  : {}),
219
221
  ...(Array.isArray(steeringMeta?.images) && steeringMeta.images.length ? { images: steeringMeta.images } : {}),
220
- ...(typeof steeringMeta?.transcriptMeta?.sender === 'string' && steeringMeta.transcriptMeta.sender
221
- ? { sender: steeringMeta.transcriptMeta.sender }
222
- : {}),
223
- ...(typeof steeringMeta?.transcriptMeta?.device === 'string' && steeringMeta.transcriptMeta.device
224
- ? { device: steeringMeta.transcriptMeta.device }
225
- : {}),
222
+ ...transcriptSourceFields(steeringMeta?.transcriptMeta),
226
223
  };
227
224
  }
228
225
 
@@ -231,7 +228,7 @@ function steeringItemExtras(steeringMeta) {
231
228
  // first (including a streamed tail that never got a trailing '\n') so the
232
229
  // steered user turn and the next assistant response do not merge into one
233
230
  // bubble and the tail is not dropped when the injection races finalization.
234
- function injectSteerMessage({ stream, cards, pushUserOrSyntheticItem }, text, steeringMeta = {}) {
231
+ export function injectSteerMessage({ stream, cards, pushUserOrSyntheticItem }, text, steeringMeta = {}) {
235
232
  // A suppressed live-completion twin is model-visible only; its Response
236
233
  // card was already pushed at delivery time.
237
234
  if (text === STEERING_SUPPRESSED_DISPLAY) return;
@@ -294,7 +291,7 @@ async function approveToolRequest(ctx, request) {
294
291
  // contextStatus path instead of competing with its provider-aligned value.
295
292
  function publishContextPressure({ syncContextStats, getState, set }, info) {
296
293
  if (info?.willCompact !== true) return;
297
- const used = Math.max(0, Number(info?.usedTokens) || 0);
294
+ const used = Math.max(0, Number(info.usedTokens) || 0);
298
295
  if (!used) return;
299
296
  syncContextStats({ allowEstimated: true });
300
297
  set({ stats: { ...getState().stats } });
@@ -424,10 +421,18 @@ export function toolHandlers(ctx) {
424
421
  // state parked on screen until the next model event arrives.
425
422
  set({ spinner: { ...spinner, mode: 'requesting' } });
426
423
  },
427
- onToolApproval: async (request) => {
428
- if (!isCurrentTurn()) return { approved: false, reason: 'turn no longer active' };
429
- return approveToolRequest(ctx, request);
430
- },
424
+ onToolApproval: Object.assign(
425
+ async (request) => {
426
+ if (!isCurrentTurn()) return { approved: false, reason: 'turn no longer active' };
427
+ return approveToolRequest(ctx, request);
428
+ },
429
+ {
430
+ // A word to the person after their answer (e.g. an always-allow that could not be saved).
431
+ // A transcript notice of THIS session: the desktop's toast region draws only the
432
+ // host-level snapshot's toasts, while the pane that asked reads its own session lane.
433
+ notice: (text, tone = 'warn') => ctx.pushNotice?.(text, tone, { transcript: true }),
434
+ }
435
+ ),
431
436
  };
432
437
  }
433
438
 
@@ -768,6 +773,10 @@ export function createRunTurn(bag) {
768
773
  if (!isCurrentTurn()) return;
769
774
  publishModelSwitch(bag, boundary);
770
775
  },
776
+ onSessionSwitches: (switches) => {
777
+ if (!isCurrentTurn()) return;
778
+ publishSessionSwitches(bag, switches);
779
+ },
771
780
  });
772
781
  if (!isCurrentTurn()) turn.cancelled = true;
773
782
  else completeTurn(ctx, result, session);
@@ -8,11 +8,14 @@ import { parseToolArgs } from '../runtime/shared/tool-surface.mjs';
8
8
  // Anchor to the orchestrator's actual denial envelope:
9
9
  // `Error: tool "<name>" denied by hook[: ...]`
10
10
  // `Error: tool "<name>" denied by hook: approval required but no approval UI is available`
11
+ // `Error: tool "<name>" was not approved: <reason>. Do not retry it …` (a person said no)
11
12
  // Matching the bare phrase anywhere in the body produced FALSE POSITIVES when a
12
13
  // normal (successful) tool result merely CONTAINED the text — e.g. a read/grep
13
14
  // over this very file or the hook source. The denial is always an Error
14
15
  // envelope emitted at the START of the result, so anchor on that prefix.
15
- const HOOK_DENIAL_RE = /^Error:\s*tool\s*"[^"]*"\s*denied by hook\b/im;
16
+ const HOOK_DENIAL_RE = /^Error:\s*tool\s*"[^"]*"\s*(?:denied by hook|was not approved)\b/im;
17
+ // The not-approved denial's instruction to the model, not shown to the user.
18
+ const NOT_APPROVED_INSTRUCTION_RE = /\s*Do not retry it or reach the same result another way; report it\.\s*$/;
16
19
  const APPROVAL_UNAVAILABLE_RE = /denied by hook:\s*approval required but no approval UI is available/i;
17
20
 
18
21
  export function toolItemResultText(item) {
@@ -44,7 +47,10 @@ export function isHookApprovalDenialToolItem(item) {
44
47
  export function formatHookDenialDetail(text) {
45
48
  const raw = String(text ?? '').trim();
46
49
  if (!raw) return '';
47
- const stripped = raw.replace(/^Error:\s*tool\s*"[^"]*"\s*/i, '').trim();
50
+ const stripped = raw
51
+ .replace(/^Error:\s*tool\s*"[^"]*"\s*/i, '')
52
+ .replace(NOT_APPROVED_INSTRUCTION_RE, '')
53
+ .trim();
48
54
  return stripped || raw;
49
55
  }
50
56
 
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync,
3
- renameSync, statSync, fstatSync, writeFileSync,
3
+ renameSync, statSync, fstatSync,
4
4
  } from 'fs';
5
- import { appendFile, mkdir, readFile, rename, writeFile } from 'fs/promises';
5
+ import { appendFile, mkdir, readFile } from 'fs/promises';
6
+ import { writeFileAtomicAsync, writeFileAtomicSync } from '../../../../runtime/shared/atomic-file.mjs';
6
7
  import { dirname, join } from 'path';
7
8
  import { resolvePluginData } from '../../../../runtime/shared/plugin-paths.mjs';
8
9
  import { readSection } from '../../../../runtime/shared/config.mjs';
@@ -586,9 +587,9 @@ function ensureUsageStoreMigrated() {
586
587
  if (!existsSync(store)) {
587
588
  const raw = readJsonFile(legacy);
588
589
  const events = (Array.isArray(raw?.events) ? raw.events : []).slice(-MAX_USAGE_EVENTS);
589
- const tmp = `${store}.migrating.${process.pid}`;
590
- writeFileSync(tmp, events.map(e => JSON.stringify(e) + '\n').join(''));
591
- renameSync(tmp, store);
590
+ // Flushed before it is published: the store would otherwise be NUL
591
+ // after a crash, losing the migrated history.
592
+ writeFileAtomicSync(store, events.map(e => JSON.stringify(e) + '\n').join(''));
592
593
  }
593
594
  let backup = legacy + LEGACY_BACKUP_SUFFIX;
594
595
  if (existsSync(backup)) backup = `${legacy}.${Date.now()}${LEGACY_BACKUP_SUFFIX}`;
@@ -645,9 +646,7 @@ async function compactUsageStore(store) {
645
646
  await withFileLock(usageLockPath(store), async () => {
646
647
  const lines = (await readFile(store, 'utf8')).split('\n').filter(l => l.trim());
647
648
  if (lines.length <= MAX_USAGE_EVENTS * 1.5) return;
648
- const tmp = `${store}.compact.${process.pid}`;
649
- await writeFile(tmp, lines.slice(-MAX_USAGE_EVENTS).join('\n') + '\n');
650
- await rename(tmp, store);
649
+ await writeFileAtomicAsync(store, lines.slice(-MAX_USAGE_EVENTS).join('\n') + '\n');
651
650
  });
652
651
  } catch {
653
652
  // Compaction is best effort; the next flush retries.
@@ -1,7 +1,6 @@
1
- import { existsSync, readFileSync } from 'fs';
2
1
  import { join } from 'path';
3
2
  import { resolvePluginData } from '../../../../runtime/shared/plugin-paths.mjs';
4
- import { updateJsonAtomicSync } from '../../../../runtime/shared/atomic-file.mjs';
3
+ import { readJsonStateSync, updateJsonAtomicSync } from '../../../../runtime/shared/atomic-file.mjs';
5
4
  import { CLAUDE_CURRENT_MODE } from './claude-current.mjs';
6
5
 
7
6
  const STORE_FILE = 'gateway-session-routes.json';
@@ -68,9 +67,9 @@ export function gatewaySessionRoutesPath() {
68
67
 
69
68
  function readStore() {
70
69
  try {
71
- const file = gatewaySessionRoutesPath();
72
- if (!existsSync(file)) return {};
73
- const parsed = JSON.parse(readFileSync(file, 'utf8'));
70
+ // A damaged store (NUL, truncated) is kept aside as *.corrupt-<ts> and its
71
+ // last good copy restored, so a session never loses its route pin to it.
72
+ const parsed = readJsonStateSync(gatewaySessionRoutesPath(), { fallback: {} });
74
73
  return parsed && typeof parsed === 'object' ? parsed : {};
75
74
  } catch {
76
75
  return {};
@@ -187,7 +186,7 @@ export function writeGatewaySessionRoutes(entries = []) {
187
186
  sessions: pruneRouteMap(sessions, updatedAt, MAX_SESSION_ROUTES),
188
187
  sessionHosts: pruneRouteMap(sessionHosts, updatedAt, MAX_HOST_ROUTES),
189
188
  };
190
- }, { compact: true, fsync: false, fsyncDir: false });
189
+ }, { compact: true, backup: true });
191
190
  return true;
192
191
  } catch {
193
192
  return false;
@@ -216,7 +215,7 @@ export function clearGatewaySessionRoute(sessionId, options = {}) {
216
215
  delete sessions[sid];
217
216
  }
218
217
  return { version: 2, updatedAt: Date.now(), sessions, sessionHosts };
219
- }, { compact: true, fsync: false, fsyncDir: false });
218
+ }, { compact: true, backup: true });
220
219
  return true;
221
220
  } catch {
222
221
  return false;
@@ -1,6 +0,0 @@
1
- - No user is present in this run. Do not wait for clarification; when
2
- approval is required, follow the approval-hold path. Hand the result to the
3
- configured delivery and do not send a duplicate message.
4
- - Report the outcome in plain words for the user; never name internal tools,
5
- actions or parameters. If a sign-in, code or CAPTCHA blocks the task, stop
6
- and say which site needs the user to sign in.