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
@@ -100,7 +100,11 @@ export function priceUsage(args) {
100
100
  // A provider-billed figure for a media request (e.g. xAI cost_in_usd_ticks)
101
101
  // is the price, with or without a catalog row.
102
102
  const media = args.media || null;
103
- if (typeof media?.reportedCostUsd === 'number' && Number.isFinite(media.reportedCostUsd) && media.reportedCostUsd >= 0)
103
+ if (
104
+ typeof media?.reportedCostUsd === 'number' &&
105
+ Number.isFinite(media.reportedCostUsd) &&
106
+ media.reportedCostUsd >= 0
107
+ )
104
108
  return {
105
109
  input,
106
110
  costUsd: Number(media.reportedCostUsd.toFixed(6)),
@@ -22,7 +22,7 @@ export function usageRollupDayKey(ts) {
22
22
  // background runners. An unknown source counts as conversation on purpose: a
23
23
  // new runner showing up in the user's totals is visible and correctable, while
24
24
  // silently dropping the user's own turns is neither.
25
- const BACKGROUND_SOURCES = new Set(['memory-cycle', 'schedule', 'webhook', 'native-web-search']);
25
+ const BACKGROUND_SOURCES = new Set(['memory-cycle', 'schedule', 'webhook', 'native-web-search', 'cache-warm']);
26
26
 
27
27
  export function isConversationUsageSource(sourceType) {
28
28
  const id = typeof sourceType === 'string' ? sourceType.trim().toLowerCase() : '';
@@ -1,3 +1,5 @@
1
+ import { FETCH_BLOCKED_PORTS, FETCH_SAFE_ATTEMPTS } from './fetch-safe-listen.mjs';
2
+
1
3
  // One loopback listener owns each bind attempt, its event handlers, and stop.
2
4
  export function createLoopbackListener({
3
5
  server,
@@ -24,7 +26,15 @@ export function createLoopbackListener({
24
26
 
25
27
  const { promise, resolve, reject } = Promise.withResolvers();
26
28
  const controller = new AbortController();
27
- const attempt = { promise, reject, controller, port: basePort, bound: false, detach };
29
+ const attempt = {
30
+ promise,
31
+ reject,
32
+ controller,
33
+ port: basePort,
34
+ bound: false,
35
+ blockedRetries: FETCH_SAFE_ATTEMPTS,
36
+ detach,
37
+ };
28
38
  current = attempt;
29
39
 
30
40
  function detach() {
@@ -35,6 +45,19 @@ export function createLoopbackListener({
35
45
  function onListening() {
36
46
  if (current !== attempt) return;
37
47
  const boundPort = server.address().port;
48
+ // An OS-assigned port can be one fetch clients refuse ("bad port"):
49
+ // close it and take another, a bounded number of times.
50
+ if (attempt.port === 0 && FETCH_BLOCKED_PORTS.has(boundPort)) {
51
+ const exhausted = attempt.blockedRetries <= 0;
52
+ attempt.blockedRetries -= 1;
53
+ server.close(() => {
54
+ if (current !== attempt) return;
55
+ if (exhausted) onError(new Error('no fetch-safe loopback port was assigned'));
56
+ else listen();
57
+ });
58
+ return;
59
+ }
60
+ server.off('listening', onListening);
38
61
  attempt.bound = true;
39
62
  reportListening(boundPort);
40
63
  resolve(boundPort);
@@ -67,7 +90,7 @@ export function createLoopbackListener({
67
90
  }
68
91
 
69
92
  server.on('error', onError);
70
- server.once('listening', onListening);
93
+ server.on('listening', onListening);
71
94
  listen();
72
95
  return promise;
73
96
  }
@@ -0,0 +1,22 @@
1
+ // Whether the representative bot (Mixy) exists: the one rule. The desktop
2
+ // answers from it (getLeadStatus `exists`, main/mixy-first.ts), and so do
3
+ // Mixy's routines (routine-owner.mjs `no-mixy`), her heartbeat and the
4
+ // heartbeat reports queued for her chat (mixy-heartbeat.mjs,
5
+ // agent-home-delivery.mjs), all through routine-owner.mjs readMixyExists.
6
+ //
7
+ // `created: false` (Mixy's agent.json) is no. Otherwise she exists on
8
+ // evidence that the user made or uses her:
9
+ // - a name (the 대표 에이전트 dialog always sets one; her profile can);
10
+ // - a message the user wrote in her thread (`homeChatted`,
11
+ // agent-kind.mjs homeThreadEvidence);
12
+ // - a schedule or webhook she owns (`ownsRoutines`): the user set it up,
13
+ // and since v1.3.0 it ran for her whether or not she was named.
14
+ // Without any of them she does not exist: v1.3.0's untouched automatic
15
+ // `{"created": true}`, and an undecided profile (`created` null). Her own
16
+ // posts in her thread, heartbeat reports and routine results, are no
17
+ // evidence. `ownsRoutines` is read only when nothing else decides.
18
+ export async function mixyExists(status, ownsRoutines) {
19
+ if (status?.created === false) return false;
20
+ if (String(status?.name ?? '').trim() || status?.homeChatted === true) return true;
21
+ return (await ownsRoutines()) === true;
22
+ }
@@ -1,8 +1,13 @@
1
1
  export function canonicalRouteProvider(value: unknown): string;
2
2
  export function routeChain(route: Record<string, unknown>): Record<string, unknown>[];
3
- export function validateRouteChainProviders(routes: Record<string, unknown>[]): void;
3
+ export function executionFallbacks(route: Record<string, unknown> | null | undefined): Record<string, unknown>[];
4
+ export function validateRouteChainProviders(
5
+ routes: Record<string, unknown>[],
6
+ options?: { allowDuplicates?: boolean }
7
+ ): void;
4
8
  export function normalizeRouteChain(
5
9
  route: Record<string, unknown>,
6
10
  normalizeLeaf: (route: Record<string, unknown>, fallback: Record<string, unknown>) => Record<string, unknown> | null,
7
- fallback?: Record<string, unknown>
11
+ fallback?: Record<string, unknown>,
12
+ options?: { strict?: boolean }
8
13
  ): Record<string, unknown> | null;
@@ -22,18 +22,36 @@ export function routeChain(route) {
22
22
  return [primary, ...fallbacks];
23
23
  }
24
24
 
25
- export function validateRouteChainProviders(routes) {
25
+ /** The fallbacks a chain runs: those on a provider not already used by the
26
+ * primary or an earlier fallback. The stored list is never changed; a fallback
27
+ * on the main model's own provider (left there by a later main-model change)
28
+ * is simply inactive. */
29
+ export function executionFallbacks(route) {
30
+ const [primary, ...fallbacks] = routeChain(route);
31
+ if (!primary) return [];
32
+ const seen = new Set([canonicalRouteProvider(primary.provider)]);
33
+ return fallbacks.filter((entry) => {
34
+ const provider = canonicalRouteProvider(entry.provider);
35
+ if (seen.has(provider)) return false;
36
+ seen.add(provider);
37
+ return true;
38
+ });
39
+ }
40
+
41
+ /** `allowDuplicates` accepts a stored chain whose fallbacks repeat a provider
42
+ * (see executionFallbacks); an explicit save validates without it. */
43
+ export function validateRouteChainProviders(routes, { allowDuplicates = false } = {}) {
26
44
  const providers = new Set();
27
45
  for (const route of routes) {
28
46
  const provider = canonicalRouteProvider(route.provider);
29
47
  if (!provider || !String(route.model || '').trim()) throw new Error('route requires provider and model');
30
- if (providers.has(provider)) throw new Error(`duplicate route provider "${provider}"`);
48
+ if (!allowDuplicates && providers.has(provider)) throw new Error(`duplicate route provider "${provider}"`);
31
49
  if (providers.size && provider === 'default') throw new Error('follow Main is only allowed as the primary route');
32
50
  providers.add(provider);
33
51
  }
34
52
  }
35
53
 
36
- export function normalizeRouteChain(route, normalizeLeaf, fallback = {}) {
54
+ export function normalizeRouteChain(route, normalizeLeaf, fallback = {}, { strict = true } = {}) {
37
55
  const raw = routeChain({ ...fallback, ...route });
38
56
  const primary = normalizeLeaf(route, fallback);
39
57
  if (!primary) return null;
@@ -42,7 +60,7 @@ export function normalizeRouteChain(route, normalizeLeaf, fallback = {}) {
42
60
  if (!normalized) throw new Error('fallback route requires provider and model');
43
61
  return { ...normalized, provider: canonicalRouteProvider(normalized.provider) };
44
62
  });
45
- validateRouteChainProviders(routes);
63
+ validateRouteChainProviders(routes, { allowDuplicates: !strict });
46
64
  const [first, ...rest] = routes;
47
65
  return rest.length ? { ...first, fallbacks: rest } : first;
48
66
  }
@@ -2,6 +2,16 @@ import { spawn } from 'node:child_process';
2
2
  import { isWSL } from './wsl.mjs';
3
3
  import { detachedSpawnOpts } from './spawn-flags.mjs';
4
4
 
5
+ /**
6
+ * Whether a browser can plausibly open on this machine: not inside an SSH
7
+ * session, and on non-WSL Linux a DISPLAY or WAYLAND_DISPLAY exists.
8
+ */
9
+ export function canOpenBrowser(env = process.env, platform = process.platform, wsl = isWSL()) {
10
+ if (env.SSH_TTY || env.SSH_CONNECTION) return false;
11
+ if (platform !== 'linux' || wsl) return true;
12
+ return Boolean(env.DISPLAY || env.WAYLAND_DISPLAY);
13
+ }
14
+
5
15
  /**
6
16
  * Open a URL in the user's default browser. Best-effort and non-blocking.
7
17
  * Callers also print the URL, so opener failure should never block OAuth.
@@ -0,0 +1,2 @@
1
+ export type OrchestrationMode = 'none' | 'focused' | 'balanced' | 'swarm';
2
+ export function configuredOrchestrationMode(config: unknown): OrchestrationMode;
@@ -1,21 +1,32 @@
1
- /** Lazy per-dataDir PG handle: one in-flight init, bootstrap lock, retry after failure. */
1
+ import { createSharedAbortable } from './abort-race.mjs';
2
+
3
+ /** Lazy per-dataDir PG handle: one in-flight init, bootstrap lock, retry after
4
+ * failure. `signal` withdraws a caller; the init (a PG runtime install
5
+ * included) is cancelled once every caller has (createSharedAbortable). */
2
6
  export function createPgSchemaDb({ schema, ddl, defaultDataDir, ensurePg, withLock }) {
3
7
  const ready = new Map();
4
- return async function getDb(dataDir = defaultDataDir()) {
5
- if (ready.has(dataDir)) return ready.get(dataDir);
6
- const p = (async () => {
7
- const { db, pool } = await ensurePg(dataDir, { schema });
8
- // Serialize CREATE TABLE across concurrent first-boot processes on the
9
- // same cluster-global advisory lock the adapter uses for schema bootstrap.
10
- await withLock(pool, () => db.exec(ddl));
11
- return db;
12
- })();
13
- ready.set(dataDir, p);
14
- try {
15
- return await p;
16
- } catch (err) {
17
- ready.delete(dataDir); // let the next call retry after a transient failure
18
- throw err;
8
+ return function getDb(dataDir = defaultDataDir(), { signal = null } = {}) {
9
+ let shared = ready.get(dataDir);
10
+ if (!shared) {
11
+ const entry = createSharedAbortable(
12
+ async (initSignal) => {
13
+ const { db, pool } = await ensurePg(dataDir, { schema, signal: initSignal });
14
+ initSignal.throwIfAborted();
15
+ // Serialize CREATE TABLE across concurrent first-boot processes on the
16
+ // same cluster-global advisory lock the adapter uses for schema bootstrap.
17
+ await withLock(pool, () => db.exec(ddl));
18
+ return db;
19
+ },
20
+ {
21
+ // Let the next call retry after a transient failure or a cancellation.
22
+ onSettled: (failed) => {
23
+ if (failed && ready.get(dataDir) === entry) ready.delete(dataDir);
24
+ },
25
+ }
26
+ );
27
+ shared = entry;
28
+ ready.set(dataDir, entry);
19
29
  }
30
+ return shared.join(signal);
20
31
  };
21
32
  }
@@ -5,7 +5,7 @@
5
5
  const PROFILE_LANGUAGES = Object.freeze([
6
6
  { id: 'system', label: 'System (locale)', prompt: null },
7
7
  { id: 'en', label: 'English', prompt: 'English' },
8
- { id: 'ko', label: 'Korean', prompt: 'Korean' },
8
+ { id: 'ko', label: '한국어', prompt: 'Korean' },
9
9
  { id: 'ja', label: '日本語', prompt: 'Japanese (日本語)' },
10
10
  { id: 'zh-Hans', label: '中文(简体)', prompt: 'Simplified Chinese (简体中文)' },
11
11
  { id: 'zh-Hant', label: '中文(繁體)', prompt: 'Traditional Chinese (繁體中文)' },
@@ -0,0 +1,4 @@
1
+ export declare const PROMPT_ORIGIN_CHANNEL: 'channel';
2
+ export declare const PROMPT_ORIGIN_AGENT_REPORT: 'agent-report';
3
+ export type PromptOrigin = typeof PROMPT_ORIGIN_CHANNEL | typeof PROMPT_ORIGIN_AGENT_REPORT;
4
+ export declare function promptOriginOf(value: unknown): PromptOrigin | null;
@@ -0,0 +1,21 @@
1
+ // A submitted prompt's provenance, carried apart from its presentation mode
2
+ // (`origin` on the submit options, the queue entry, the turn input and the
3
+ // stored user message's meta). Dependency-free: the desktop main process
4
+ // stamps it too (agent-room-turn.ts).
5
+
6
+ /** Real external activity presented as a task notification: an inbound
7
+ * channel message (Discord, Telegram, webhook event rules …). */
8
+ export const PROMPT_ORIGIN_CHANNEL = 'channel';
9
+
10
+ /** A turn the runtime starts because another bot or agent reported back: a
11
+ * bot-home wake (daemon.mjs `wake`) or a group-room member's relay of its
12
+ * reported work (agent-room-turn.ts). System-caused, though presented as a
13
+ * prompt. */
14
+ export const PROMPT_ORIGIN_AGENT_REPORT = 'agent-report';
15
+
16
+ const KNOWN_ORIGINS = new Set([PROMPT_ORIGIN_CHANNEL, PROMPT_ORIGIN_AGENT_REPORT]);
17
+
18
+ /** `value` when it is a known provenance, else null. */
19
+ export function promptOriginOf(value) {
20
+ return KNOWN_ORIGINS.has(value) ? value : null;
21
+ }
@@ -15,7 +15,7 @@ export const ACCOUNT_PROVIDERS = Object.freeze([
15
15
  'antigravity-oauth',
16
16
  'opencode-go-oauth',
17
17
  ]);
18
- const MAX_ACCOUNTS = 20;
18
+ export const MAX_ACCOUNTS = 20;
19
19
  // `dataDir` defaults to this process's data dir; an isolated runtime passes
20
20
  // the host's dir explicitly while its own MIXDOG_DATA_DIR points elsewhere.
21
21
  const file = (dataDir = resolvePluginData()) => join(dataDir, 'provider-accounts.json');
@@ -597,6 +597,12 @@ export function providerAccountExhaustedForModelOnly(row, now = Date.now(), mode
597
597
  // enough that yesterday's number cannot send every fallback to one account.
598
598
  const USAGE_EVIDENCE_MAX_AGE_MS = 60 * 60_000;
599
599
 
600
+ /** Whether a usage reading taken at `checkedAt` may still steer a decision. */
601
+ export function quotaReadingFresh(checkedAt, now = Date.now()) {
602
+ const at = Number(checkedAt);
603
+ return at > 0 && now - at <= USAGE_EVIDENCE_MAX_AGE_MS;
604
+ }
605
+
600
606
  // Remaining headroom across every window that applies to this model, or null
601
607
  // when nothing recent measured the account. The fullest window decides: an
602
608
  // account with 4% of its burst window left cannot serve now, however untouched
@@ -0,0 +1,29 @@
1
+ // Display names for provider ids; an unknown id displays as itself.
2
+ const PROVIDER_DISPLAY_NAMES = new Map([
3
+ ['openai-oauth', 'OpenAI OAuth'],
4
+ ['anthropic-oauth', 'Anthropic OAuth'],
5
+ ['grok-oauth', 'Grok OAuth'],
6
+ ['muse-code-oauth', 'Muse Code'],
7
+ ['command-code-oauth', 'Command Code'],
8
+ ['mimo-oauth', 'MiMo'],
9
+ ['cursor-oauth', 'Cursor OAuth'],
10
+ ['devin-oauth', 'Devin'],
11
+ ['antigravity-oauth', 'Antigravity OAuth'],
12
+ ['openai', 'OpenAI API'],
13
+ ['openai-api', 'OpenAI API'],
14
+ ['anthropic', 'Anthropic API'],
15
+ ['anthropic-api', 'Anthropic API'],
16
+ ['gemini', 'Gemini API'],
17
+ ['gemini-api', 'Gemini API'],
18
+ ['xai', 'xAI API'],
19
+ ['xai-api', 'xAI API'],
20
+ ['deepseek', 'DeepSeek API'],
21
+ ['deepseek-api', 'DeepSeek API'],
22
+ ['opencode-go-oauth', 'OpenCode Go OAuth'],
23
+ ['openrouter', 'OpenRouter'],
24
+ ['mixdog-local', 'Local Provider'],
25
+ ['default', 'Default'],
26
+ ]);
27
+
28
+ export const providerDisplayName = (provider) =>
29
+ PROVIDER_DISPLAY_NAMES.get(String(provider || '').toLowerCase()) ?? (provider || 'Provider');
@@ -8,6 +8,8 @@ export const TIMEOUT_PHASE = Object.freeze({
8
8
  FIRST_CHUNK: 'first-chunk',
9
9
  // A stream that already produced output went silent.
10
10
  IDLE: 'idle',
11
+ // A sign-in token refresh did not answer: an auth problem, not a slow model.
12
+ AUTH: 'auth',
11
13
  });
12
14
 
13
15
  /** A timeout before the model produced anything: headers or first chunk. */
@@ -4,6 +4,8 @@ export interface RoutineTrigger {
4
4
  event?: string;
5
5
  }
6
6
 
7
+ export declare const ROUTINE_QUIET: 'ROUTINE_QUIET';
8
+ export declare function isRoutineQuietReply(text: unknown): boolean;
7
9
  export declare function routineTriggerHeader(input: {
8
10
  kind: string;
9
11
  name: string;
@@ -9,6 +9,15 @@ import { localTimestampParts } from './time-format.mjs';
9
9
  const HEADER_PREFIX = '[Routine trigger]';
10
10
  const KINDS = new Set(['schedule', 'webhook']);
11
11
 
12
+ /** The whole reply of a routine turn with nothing new to tell: kept in the
13
+ * record, never painted, previewed, counted unread or notified. */
14
+ export const ROUTINE_QUIET = 'ROUTINE_QUIET';
15
+
16
+ /** Whether a reply is exactly the routine quiet sentinel. */
17
+ export function isRoutineQuietReply(text) {
18
+ return String(text ?? '').trim() === ROUTINE_QUIET;
19
+ }
20
+
12
21
  /** The first line of a routine turn's prompt: which routine fired, when (local
13
22
  * time and zone: the turn gets no other date), and how to treat the turn.
14
23
  * It is the routine's only conduct text. Only the turn's content carries it,
@@ -24,6 +33,7 @@ export function routineTriggerHeader({ kind, name, project = '', now = new Date(
24
33
  'Payments and permanent deletes are refused here: report that and suggest doing it from the chat.',
25
34
  'If a sign-in, code or CAPTCHA blocks you, stop and say which site needs the user.',
26
35
  'Reply in this chat in plain words, without tool names; if you handed work off, say it started — its result arrives here later.',
36
+ `Tell what is new in a fitting tone; if nothing is worth telling, reply with exactly ${ROUTINE_QUIET}.`,
27
37
  ...(project ? [`Project folder: ${project}`] : []),
28
38
  ].join(' ');
29
39
  }
@@ -1,15 +1,7 @@
1
1
  export interface SensitiveScope {
2
+ /** A Mixdog home or data folder, as path segments; only its secret subset is denied. */
2
3
  root: string[];
3
- /** Subfolders of the root that stay reachable. */
4
- except: readonly string[];
5
- /** Whole tree (a registered private root) or Mixdog's secret subset. */
6
- whole: boolean;
7
4
  }
8
- export function sensitivePathSegments(path: string): string[];
9
- export function matchesSensitivePath(
10
- path: string,
11
- within?: readonly SensitiveScope[],
12
- options?: { envTemplates?: boolean }
13
- ): boolean;
5
+ export function matchesSensitivePath(path: string, within?: readonly SensitiveScope[]): boolean;
14
6
  export function mixdogSensitiveScopes(extraData?: readonly string[]): SensitiveScope[];
15
7
  export function isSensitiveLocalPath(path: string, options?: { extraData?: readonly string[] }): boolean;
@@ -1,8 +1,7 @@
1
- // The credential / secret file deny list, shared by every lane that hands a
2
- // local file to someone else: the desktop's remote (paired-device) file lanes
3
- // (apps/desktop/src/main/remote-sensitive-paths.ts adds its registered private
4
- // roots and async canonicalization) and the runtime's attachment hand-offs
5
- // between bots and agents (session-runtime/handoff-attachments.mjs).
1
+ // The credential / secret file deny list of the runtime's attachment
2
+ // hand-offs between bots and agents (session-runtime/handoff-attachments.mjs):
3
+ // an automatic transfer never carries a secret-bearing file. A paired device
4
+ // is the user's own and gets no such list (it opens what the desktop opens).
6
5
  //
7
6
  // Matching is on SEGMENTS of a path, lower-cased on every platform, so a
8
7
  // denied name cannot hide behind letter case, trailing dots/spaces or an NTFS
@@ -113,10 +112,6 @@ const CREDENTIAL_FILES = new Set([
113
112
  * checked in often enough with real values that no name is exempt) and
114
113
  * `<stage>.env`. */
115
114
  const ENV_FILE = /^(?:\.env.*|[^.]{1,64}(?:\.[^.]{1,64}){0,3}\.env)$/;
116
- /** The dotenv templates a person may still open on purpose from a paired
117
- * device (the desktop's remote file lanes, `envTemplates: true`); automatic
118
- * transfers between bots and agents never get this allowance. */
119
- const ENV_TEMPLATES = new Set(['.env.example', '.env.sample', '.env.template', '.env.dist']);
120
115
  /** Credential JSON a cloud or OAuth client writes or downloads:
121
116
  * `credentials.json`, `*credentials*.json`, `service-account*.json`,
122
117
  * `client_secret*.json`. */
@@ -143,10 +138,9 @@ function backupOriginal(name) {
143
138
 
144
139
  /** A file whose own name says it holds a secret, wherever it lives: a
145
140
  * credential file, a dotenv file, a key or keystore, or a backup of one. */
146
- function secretFileName(name, { envTemplates = false } = {}) {
141
+ function secretFileName(name) {
147
142
  const original = backupOriginal(name);
148
- if (CREDENTIAL_FILES.has(original) || CREDENTIAL_JSON.test(original)) return true;
149
- if (ENV_FILE.test(original)) return !(envTemplates && ENV_TEMPLATES.has(original));
143
+ if (CREDENTIAL_FILES.has(original) || CREDENTIAL_JSON.test(original) || ENV_FILE.test(original)) return true;
150
144
  const dot = original.lastIndexOf('.');
151
145
  if (dot >= 0 && KEY_EXTENSIONS.has(original.slice(dot))) return true;
152
146
  return SSH_PRIVATE_KEY.test(original);
@@ -156,14 +150,7 @@ function secretFileName(name, { envTemplates = false } = {}) {
156
150
  // (tool results, shell output, media), which stay reachable; only the files
157
151
  // and folders that carry provider tokens, API keys, pairing and bridge secrets
158
152
  // are denied.
159
- const MIXDOG_SECRET_DIRECTORIES = new Set([
160
- 'secrets',
161
- 'mcp-oauth',
162
- 'provider-accounts',
163
- 'telegram',
164
- 'relay-data',
165
- 'dev-profile',
166
- ]);
153
+ const MIXDOG_SECRET_DIRECTORIES = new Set(['secrets', 'mcp-oauth', 'provider-accounts', 'relay-data', 'dev-profile']);
167
154
  const MIXDOG_SECRET_FILE =
168
155
  /^\.?(mixdog-config|provider-accounts|relay-hook-device|auth\.json|[a-z0-9-]+-oauth(-credentials)?\.json|[a-z0-9-]+-bridge\.json)/;
169
156
  const MIXDOG_SECRET_EXTENSIONS = new Set(['.dpapi', '.token']);
@@ -177,7 +164,7 @@ function mixdogSecret(rest) {
177
164
 
178
165
  /** The segments of a path as the rules compare them: no extended-path prefix,
179
166
  * no NTFS stream suffix, no trailing dots or spaces, lower case. */
180
- export function sensitivePathSegments(path) {
167
+ function sensitivePathSegments(path) {
181
168
  return String(path)
182
169
  .replace(/^[\\/]{2}[?.][\\/]/, '')
183
170
  .split(/[\\/]+/)
@@ -198,30 +185,21 @@ function hasRun(segments, run) {
198
185
  }
199
186
 
200
187
  /**
201
- * Whether `path` is denied under `within`: scopes `{ root: segments, except:
202
- * subfolder names, whole }` — Mixdog's home and data folders (`whole: false`,
203
- * only their secret subset is denied) and registered private roots (`whole:
204
- * true`, everything but `except`). Pass both the lexical and canonical form.
205
- * `envTemplates`: a user-initiated remote open may read the dotenv
206
- * templates; nothing else gets that allowance.
188
+ * Whether `path` is denied under `within`: scopes `{ root: segments }` —
189
+ * Mixdog's home and data folders, where only their secret subset is denied.
190
+ * Pass both the lexical and canonical form.
207
191
  */
208
- export function matchesSensitivePath(path, within = [], { envTemplates = false } = {}) {
192
+ export function matchesSensitivePath(path, within = []) {
209
193
  const segments = sensitivePathSegments(resolve(path));
210
194
  const name = segments[segments.length - 1] ?? '';
211
195
  if (segments.some((segment) => SECRET_DIRECTORIES.has(segment))) return true;
212
- if (secretFileName(name, { envTemplates }) || SECRET_SEQUENCES.some((run) => hasRun(segments, run))) return true;
213
- const containing = within.filter(
214
- ({ root }) => root.length > 0 && root.every((segment, index) => segments[index] === segment)
196
+ if (secretFileName(name) || SECRET_SEQUENCES.some((run) => hasRun(segments, run))) return true;
197
+ return within.some(
198
+ ({ root }) =>
199
+ root.length > 0 &&
200
+ root.every((segment, index) => segments[index] === segment) &&
201
+ mixdogSecret(segments.slice(root.length))
215
202
  );
216
- // Mixdog's home and data folders follow Mixdog's own rule even inside a
217
- // registered private root: a dev or isolated profile keeps them in the app
218
- // profile, and the tool output and media there stay reachable.
219
- const own = containing.filter(({ whole }) => !whole);
220
- if (own.length) return own.some(({ root }) => mixdogSecret(segments.slice(root.length)));
221
- return containing.some(({ root, except }) => {
222
- const rest = segments.slice(root.length);
223
- return rest.length > 0 && !except.includes(rest[0]);
224
- });
225
203
  }
226
204
 
227
205
  function realOrSelf(path) {
@@ -241,7 +219,7 @@ export function mixdogSensitiveScopes(extraData = []) {
241
219
  const scopes = [];
242
220
  for (const root of roots) {
243
221
  for (const form of new Set([resolve(root), realOrSelf(resolve(root))])) {
244
- scopes.push({ root: sensitivePathSegments(form), except: [], whole: false });
222
+ scopes.push({ root: sensitivePathSegments(form) });
245
223
  }
246
224
  }
247
225
  return scopes;
@@ -13,7 +13,54 @@ export function onSessionActivityChange(listener) {
13
13
  }
14
14
 
15
15
  export function emitSessionActivityChange(sessionId = '') {
16
- for (const listener of [...listeners]) {
16
+ emit(listeners, sessionId);
17
+ }
18
+
19
+ // A turn started on a session: the one point every turn passes through, whoever
20
+ // started it. Unlike the activity pulse it never fires on a settle or a wait.
21
+ const turnStartListeners = new Set();
22
+
23
+ export function onSessionTurnStart(listener) {
24
+ if (typeof listener !== 'function') return () => {};
25
+ turnStartListeners.add(listener);
26
+ return () => turnStartListeners.delete(listener);
27
+ }
28
+
29
+ export function emitSessionTurnStart(sessionId = '') {
30
+ emit(turnStartListeners, sessionId);
31
+ }
32
+
33
+ // A live session started or stopped showing an approval card waiting on the
34
+ // user (its runtime state's `toolApproval`): the session catalog, which
35
+ // stamps that on each row, listens and re-reads.
36
+ const approvalCardListeners = new Set();
37
+
38
+ export function onSessionApprovalCardChange(listener) {
39
+ if (typeof listener !== 'function') return () => {};
40
+ approvalCardListeners.add(listener);
41
+ return () => approvalCardListeners.delete(listener);
42
+ }
43
+
44
+ export function emitSessionApprovalCardChange(sessionId = '') {
45
+ emit(approvalCardListeners, sessionId);
46
+ }
47
+
48
+ // The bots whose sends are in flight in a session changed (bot-owned-work.mjs):
49
+ // the session catalog stamps them on each row (`handedBy`) and re-reads.
50
+ const botWorkListeners = new Set();
51
+
52
+ export function onSessionBotWorkChange(listener) {
53
+ if (typeof listener !== 'function') return () => {};
54
+ botWorkListeners.add(listener);
55
+ return () => botWorkListeners.delete(listener);
56
+ }
57
+
58
+ export function emitSessionBotWorkChange(sessionId = '') {
59
+ emit(botWorkListeners, sessionId);
60
+ }
61
+
62
+ function emit(targets, sessionId) {
63
+ for (const listener of [...targets]) {
17
64
  try {
18
65
  listener(String(sessionId || ''));
19
66
  } catch {
@@ -0,0 +1,37 @@
1
+ // Per-session scratch directory under Mixdog's data directory. Processes the
2
+ // `shell` tool starts see it as TEMP/TMP/TMPDIR, so scratch files never land in
3
+ // the project, and it is removed with its session (session-delete.mjs).
4
+ import { mkdirSync, rmSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { resolvePluginData } from './plugin-paths.mjs';
7
+
8
+ const SESSION_ID = /^[A-Za-z0-9_-]+$/;
9
+
10
+ /** The session's scratch path (not created), or '' for an unusable id. */
11
+ export function sessionScratchDir(sessionId) {
12
+ const id = typeof sessionId === 'string' ? sessionId : '';
13
+ return SESSION_ID.test(id) ? join(resolvePluginData(), 'scratch', id) : '';
14
+ }
15
+
16
+ /** Create the session's scratch directory if absent; returns its path ('' without a session id). */
17
+ export function ensureSessionScratchDir(sessionId) {
18
+ const dir = sessionScratchDir(sessionId);
19
+ if (dir) mkdirSync(dir, { recursive: true });
20
+ return dir;
21
+ }
22
+
23
+ export function removeSessionScratchDir(sessionId) {
24
+ const dir = sessionScratchDir(sessionId);
25
+ if (dir) rmSync(dir, { recursive: true, force: true });
26
+ }
27
+
28
+ /** Point an env's temp variables at the session's scratch directory. */
29
+ export function applySessionScratchEnv(env, sessionId) {
30
+ const dir = ensureSessionScratchDir(sessionId);
31
+ if (dir) {
32
+ env.TEMP = dir;
33
+ env.TMP = dir;
34
+ env.TMPDIR = dir;
35
+ }
36
+ return env;
37
+ }
@@ -4,7 +4,7 @@ export type SessionSidebarChange =
4
4
  | { archived: true; cascade: false }
5
5
  | { forgotten: true };
6
6
  export type SessionSidebarHost = (sessionId: unknown, change: unknown) => unknown;
7
- export type SessionSidebarState = { archived: Record<string, number> };
7
+ export type SessionSidebarState = { archived: Record<string, number>; favorites?: Record<string, number> };
8
8
  export function registerSessionSidebarHost(
9
9
  host: SessionSidebarHost,
10
10
  options?: { state?: (() => SessionSidebarState | Promise<SessionSidebarState>) | null }
@@ -41,11 +41,15 @@ export async function applySessionSidebarChange(sessionId, change) {
41
41
  await active.host(sessionId, change);
42
42
  }
43
43
 
44
- /** The active desktop's flags, `{ archived }` (session id → epoch-ms stamp);
45
- * null when no desktop (or one without a state reader) is attached. */
44
+ /** The active desktop's flags, `{ archived, favorites? }` (session id →
45
+ * epoch-ms stamp; `favorites` only from a desktop that reports them); null
46
+ * when no desktop (or one without a state reader) is attached. */
46
47
  export async function readSessionSidebarState() {
47
48
  const active = hosts().at(-1);
48
49
  if (!active?.state) return null;
49
50
  const state = await active.state();
50
- return { archived: { ...state?.archived } };
51
+ return {
52
+ archived: { ...state?.archived },
53
+ ...(state?.favorites && typeof state.favorites === 'object' ? { favorites: { ...state.favorites } } : {}),
54
+ };
51
55
  }