mixdog 1.3.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/LICENSES/oh-my-pi-MIT.txt +23 -0
  2. package/NOTICE.md +17 -2
  3. package/README.md +11 -0
  4. package/package.json +3 -11
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/embed-google-oauth-client.mjs +49 -0
  7. package/scripts/gate-local.mjs +4 -2
  8. package/scripts/lib/test-spawn-ledger.mjs +46 -1
  9. package/scripts/native-release-status.mjs +2 -2
  10. package/scripts/recall-history-cases.json +1 -1
  11. package/scripts/suite-health.mjs +10 -1
  12. package/scripts/test.mjs +12 -11
  13. package/src/agents/reviewer/AGENT.md +7 -11
  14. package/src/agents/reviewer/agent.json +1 -1
  15. package/src/defaults/shipped-history.json +25 -1
  16. package/src/defaults/skills/browser-use/SKILL.md +77 -17
  17. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  18. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  19. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  20. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  21. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  22. package/src/defaults/skills/setup/SKILL.md +29 -2
  23. package/src/defaults/skills/setup/references/actions.md +117 -28
  24. package/src/defaults/skills/setup/references/surfaces.md +77 -35
  25. package/src/defaults/skills/setup-guide/SKILL.md +3 -3
  26. package/src/defaults/skills/visualize/SKILL.md +2 -1
  27. package/src/lib/keychain-cjs.cjs +359 -29
  28. package/src/lib/rules-builder.cjs +10 -8
  29. package/src/rules/agent/AGENT.md +6 -6
  30. package/src/rules/agent-mode/AGENT_MODE.md +10 -2
  31. package/src/rules/agent-mode/MIXY.md +12 -0
  32. package/src/rules/lead/LEAD.md +18 -20
  33. package/src/rules/shared/05-tool-calls.md +5 -6
  34. package/src/rules/shared/10-tool-workflow.md +2 -0
  35. package/src/rules/shared/60-verification.md +7 -5
  36. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  37. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
  38. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  39. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/config.mjs +34 -0
  41. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  42. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  43. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  44. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  45. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
  46. package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
  49. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
  50. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
  51. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
  53. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
  54. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
  55. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
  56. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  60. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
  61. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  63. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
  65. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
  66. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
  67. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
  68. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
  69. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
  70. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  71. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
  73. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
  74. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  75. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
  76. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
  77. package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
  78. package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
  79. package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
  81. package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
  82. package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
  83. package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
  84. package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
  85. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
  86. package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
  87. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
  88. package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
  89. package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
  90. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
  91. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
  92. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
  93. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
  94. package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
  95. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
  97. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  98. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
  99. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  100. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  101. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  109. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  113. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
  114. package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
  115. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  116. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  117. package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
  118. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  119. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  120. package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
  121. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  122. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  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 +1 -1
  127. package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
  128. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  131. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  132. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
  133. package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
  134. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
  135. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
  136. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
  137. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  138. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
  139. package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
  140. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  141. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
  142. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  143. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  144. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
  145. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
  146. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
  147. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  148. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
  149. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  151. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  152. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
  153. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
  154. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
  155. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  156. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
  157. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  158. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
  159. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  163. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
  164. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
  165. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  166. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  167. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
  168. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  172. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  173. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  174. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  175. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  176. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  177. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  178. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  179. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  180. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
  182. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  183. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  184. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  185. package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
  186. package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
  187. package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
  188. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  189. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  191. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
  192. package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
  193. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
  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-persistence-events.mjs +29 -0
  203. package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  207. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  208. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  209. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
  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/tool-batch/state.mjs +4 -0
  214. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
  216. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  217. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  218. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  219. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  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.mjs +25 -5
  225. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  226. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  227. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  228. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  229. package/src/runtime/browser-bridge/client.mjs +73 -8
  230. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  231. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  232. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  233. package/src/runtime/channels/lib/config.mjs +6 -61
  234. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  235. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  236. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  237. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  238. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  239. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  240. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  241. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  242. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  243. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  244. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  245. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  246. package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
  247. package/src/runtime/channels/lib/scheduler.mjs +135 -154
  248. package/src/runtime/channels/lib/schedules-db.mjs +83 -11
  249. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  250. package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
  251. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  252. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  253. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  254. package/src/runtime/channels/lib/worker-main.mjs +19 -34
  255. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  256. package/src/runtime/computer-bridge/actions.mjs +9 -1
  257. package/src/runtime/computer-bridge/client.mjs +180 -8
  258. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  259. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  260. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  261. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  262. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  263. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  264. package/src/runtime/connectors/google-mcp.mjs +10 -12
  265. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  266. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  268. package/src/runtime/office/com/com-adapter.mjs +17 -1
  269. package/src/runtime/office/com/office-com-host.ps1 +51 -1
  270. package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
  271. package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
  272. package/src/runtime/office/pdf/document-preview.mjs +326 -8
  273. package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
  274. package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
  275. package/src/runtime/office/portable/portable-soffice.mjs +45 -5
  276. package/src/runtime/session-search/session-search-db.mjs +73 -9
  277. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  278. package/src/runtime/shared/abort-race.mjs +89 -0
  279. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  280. package/src/runtime/shared/agent-kind.mjs +4 -3
  281. package/src/runtime/shared/bot-hops.mjs +62 -2
  282. package/src/runtime/shared/bounded-download.mjs +8 -3
  283. package/src/runtime/shared/config.mjs +51 -35
  284. package/src/runtime/shared/connector-accounts.mjs +20 -10
  285. package/src/runtime/shared/connector-catalog.d.mts +33 -2
  286. package/src/runtime/shared/connector-catalog.mjs +166 -8
  287. package/src/runtime/shared/connector-triggers.mjs +22 -19
  288. package/src/runtime/shared/defaults-separation.mjs +2 -14
  289. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  290. package/src/runtime/shared/developer-options.mjs +32 -1
  291. package/src/runtime/shared/err-text.mjs +24 -15
  292. package/src/runtime/shared/file-permissions.d.mts +0 -8
  293. package/src/runtime/shared/file-permissions.mjs +0 -11
  294. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  295. package/src/runtime/shared/google-oauth-client.generated.json +4 -0
  296. package/src/runtime/shared/google-oauth-client.mjs +34 -9
  297. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  298. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  299. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  300. package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
  301. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
  302. package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
  303. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  304. package/src/runtime/shared/llm/well-formed.mjs +66 -0
  305. package/src/runtime/shared/mixy-presence.mjs +22 -0
  306. package/src/runtime/shared/orchestration.d.mts +2 -0
  307. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  308. package/src/runtime/shared/profile-config.cjs +1 -1
  309. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  310. package/src/runtime/shared/prompt-origin.mjs +21 -0
  311. package/src/runtime/shared/provider-accounts.mjs +231 -59
  312. package/src/runtime/shared/provider-api-key.mjs +19 -8
  313. package/src/runtime/shared/provider-display-name.mjs +29 -0
  314. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  315. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  316. package/src/runtime/shared/routine-trigger.mjs +52 -0
  317. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  318. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  319. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  320. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  321. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  322. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  323. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  324. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  325. package/src/runtime/shared/transcript-metadata.mjs +38 -0
  326. package/src/runtime/shared/user-data-guard.mjs +30 -2
  327. package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
  328. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  329. package/src/runtime/terminal-bridge/client.mjs +10 -1
  330. package/src/runtime/visualize-bridge/client.mjs +27 -8
  331. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  332. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  333. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  334. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  335. package/src/session-runtime/agent-home-delivery.mjs +486 -179
  336. package/src/session-runtime/agent-room-delivery.mjs +28 -1
  337. package/src/session-runtime/agent-task-sessions.mjs +561 -199
  338. package/src/session-runtime/boot/apis.mjs +9 -0
  339. package/src/session-runtime/boot/begin.mjs +3 -2
  340. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  341. package/src/session-runtime/boot/providers.mjs +3 -0
  342. package/src/session-runtime/boot/tools.mjs +8 -1
  343. package/src/session-runtime/bot-chain.mjs +32 -0
  344. package/src/session-runtime/bot-status.mjs +7 -4
  345. package/src/session-runtime/channel-config-api.mjs +63 -17
  346. package/src/session-runtime/config-lifecycle.mjs +6 -0
  347. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  348. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  349. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  350. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
  351. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
  352. package/src/session-runtime/internal-tool-executor.mjs +4 -0
  353. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  354. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  355. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  356. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  357. package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
  358. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  359. package/src/session-runtime/mixy-heartbeat.mjs +927 -0
  360. package/src/session-runtime/model-route/route-tuning.mjs +10 -6
  361. package/src/session-runtime/notification-bus.mjs +31 -117
  362. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  363. package/src/session-runtime/notification-listeners.mjs +76 -0
  364. package/src/session-runtime/owned-automation-run.mjs +35 -13
  365. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  366. package/src/session-runtime/provider-auth/login.mjs +7 -0
  367. package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
  368. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  369. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  370. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  371. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  372. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  373. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  374. package/src/session-runtime/routine-home-turn.mjs +33 -6
  375. package/src/session-runtime/routine-owner.mjs +40 -9
  376. package/src/session-runtime/schedule-session-run.mjs +7 -3
  377. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  378. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
  379. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  380. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  381. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  382. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  383. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  384. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  385. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  386. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  387. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  388. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  389. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  390. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  391. package/src/session-runtime/services/channel-admin.mjs +73 -31
  392. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  393. package/src/session-runtime/services/channel-worker.mjs +1 -1
  394. package/src/session-runtime/services/provider-admin.mjs +128 -25
  395. package/src/session-runtime/services/schedule-access.mjs +14 -0
  396. package/src/session-runtime/services/seeds.mjs +0 -28
  397. package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
  398. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  399. package/src/session-runtime/session-create/session-options.mjs +8 -4
  400. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  401. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  402. package/src/session-runtime/settings-system-api.mjs +28 -1
  403. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  404. package/src/session-runtime/setup-tool/executor.mjs +187 -29
  405. package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
  406. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  407. package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
  408. package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
  409. package/src/session-runtime/task-session-route.mjs +99 -0
  410. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  411. package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
  412. package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
  413. package/src/session-runtime/tool-surface.mjs +4 -0
  414. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  415. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
  416. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  417. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
  418. package/src/session-runtime/workspace/transitions.mjs +10 -2
  419. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  420. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  421. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  422. package/src/standalone/channel-transport.mjs +1 -1
  423. package/src/standalone/daemon-channels-loader.mjs +10 -8
  424. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  425. package/src/standalone/daemon-session-control/request.mjs +51 -0
  426. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  427. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  428. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  429. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  430. package/src/standalone/daemon-session-control.mjs +161 -157
  431. package/src/standalone/daemon.mjs +33 -2
  432. package/src/standalone/session-client.mjs +7 -3
  433. package/src/standalone/session-protocol.mjs +10 -0
  434. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
  435. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  436. package/src/standalone/session-service/quota-resume.mjs +3 -0
  437. package/src/standalone/session-service/retention/busy.mjs +65 -1
  438. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  439. package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
  440. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  441. package/src/standalone/session-service.mjs +13 -0
  442. package/src/standalone/session-tool-output.mjs +21 -10
  443. package/src/tui/app/model-options.mjs +4 -27
  444. package/src/tui/dist/index.mjs +81 -65
  445. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  446. package/src/tui/session/initial-state.mjs +3 -0
  447. package/src/tui/session/notice-surface.mjs +23 -1
  448. package/src/tui/session/queue-helpers.mjs +3 -42
  449. package/src/tui/session/session-api/integrations.mjs +1 -0
  450. package/src/tui/session/session-api/lifecycle.mjs +8 -0
  451. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  452. package/src/tui/session/session-api/routes-write.mjs +5 -1
  453. package/src/tui/session/session-api/routes.mjs +1 -0
  454. package/src/tui/session/session-api/settings.mjs +30 -2
  455. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  456. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  457. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  458. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  459. package/src/tui/session/session-flow/drain.mjs +118 -149
  460. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  461. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  462. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  463. package/src/tui/session/tool-approval-request.mjs +39 -0
  464. package/src/tui/session/tool-approval-settle.mjs +62 -0
  465. package/src/tui/session/tool-approval.mjs +69 -115
  466. package/src/tui/session/tui-steering-persist.mjs +8 -1
  467. package/src/tui/session/turn-stream.mjs +36 -3
  468. package/src/tui/session/turn.mjs +23 -2
  469. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  470. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  471. package/scripts/code-graph-description-contract.mjs +0 -188
  472. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -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,40 @@ 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
+ function emit(targets, sessionId) {
49
+ for (const listener of [...targets]) {
17
50
  try {
18
51
  listener(String(sessionId || ''));
19
52
  } catch {
@@ -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
  }
@@ -0,0 +1,18 @@
1
+ // What must land in a session's transcript right before its next turn's
2
+ // prompt, whoever starts that turn: the daemon registers one prelude
3
+ // (agent-home-delivery.mjs postBeforeAgentTurn) and the prompt queue's drain
4
+ // (tui/session/session-flow/drain.mjs) awaits it before presenting a batch.
5
+
6
+ let prelude = null;
7
+
8
+ /** Register `fn(sessionId)` (null or a promise per turn); a non-function clears it. */
9
+ export function setSessionTurnPrelude(fn) {
10
+ prelude = typeof fn === 'function' ? fn : null;
11
+ }
12
+
13
+ /** The prelude's work before a turn of `sessionId`: a promise, or null when
14
+ * there is none (no await, so a turn with nothing owed starts as before). */
15
+ export function sessionTurnPrelude(sessionId) {
16
+ if (!prelude || !sessionId) return null;
17
+ return prelude(String(sessionId)) || null;
18
+ }
@@ -195,6 +195,8 @@ const TRANSCRIPT_HIDDEN_CONTROL_TOOL_NAMES = new Set([
195
195
  'update_goal',
196
196
  'load_tool',
197
197
  'tool_search',
198
+ // A bot's reaction shows as its emoji under the user's message, not as a card.
199
+ 'react',
198
200
  ]);
199
201
  // Skill loads are visible cards EXCEPT for built-in skills: those ride a
200
202
  // built-in feature whose own card (Browser Use, Document work, ...) follows
@@ -526,20 +526,7 @@ function summarizeCodeGraphResult({ text, translate }) {
526
526
  return null;
527
527
  }
528
528
 
529
- function summarizeFetchResult({ text, trimmed, args, normalized, translate }) {
530
- // Channel `fetch` (Discord message fetch — args carry channel/messageId/limit,
531
- // never url/uri) is not a WEB fetch: the status/size probes would miss and
532
- // the result fall to raw JSON, so it takes the generic JSON/text summarizer.
533
- if (normalized === 'fetch') {
534
- const a = parseToolArgs(args);
535
- const isChannelFetch =
536
- !firstText(a.url, a.uri) && Boolean(firstText(a.channel, a.channelId, a.chatId, a.messageId) || a.limit != null);
537
- if (isChannelFetch) {
538
- const n = countNonEmptyLines(text);
539
- if (trimmed && looksLineOriented(text) && n > 0) return countLabel(translate, n, 'message');
540
- return summarizeGenericResult(text, translate);
541
- }
542
- }
529
+ function summarizeFetchResult({ text }) {
543
530
  // Status: require a status-like context (HTTP NNN, "Status: NNN",
544
531
  // or "NNN OK"/"NNN Not Found") rather than any bare 3-digit number.
545
532
  const status =
@@ -2,6 +2,7 @@
2
2
  // These pure helpers keep live rows, persisted messages and restored rows on
3
3
  // the same field contract without rewriting older display-only transcripts.
4
4
  import { displayModelName } from './model-display.mjs';
5
+ import { botChainOf } from './bot-hops.mjs';
5
6
  import { routineTriggerField } from './routine-trigger.mjs';
6
7
 
7
8
  export function transcriptRouteMetadataFields(value) {
@@ -90,6 +91,11 @@ export function persistedUserTranscriptMetadata(value) {
90
91
  ...transcriptSourceFields(value),
91
92
  // Bot-to-bot chain position of a bot's message (session-runtime/bot-hops.mjs).
92
93
  ...(Number.isInteger(value.hop) && value.hop > 0 ? { hop: value.hop } : {}),
94
+ // The bots that handed this message on, sender last (bot-hops.mjs botChainOf).
95
+ ...botChainField(value.botChain),
96
+ // A group-room member turn: the newest user row of the room log its prompt
97
+ // shows (message-reactions.mjs counts a reaction only on that row).
98
+ ...(typeof value.roomRow === 'string' && value.roomRow ? { roomRow: value.roomRow } : {}),
93
99
  // The turn's change-record key (the outer prompt id), so a restored user
94
100
  // row still addresses its turn's Undo/Reapply.
95
101
  ...(typeof value.checkpointId === 'string' && value.checkpointId ? { checkpointId: value.checkpointId } : {}),
@@ -104,6 +110,11 @@ export function persistedUserTranscriptMetadata(value) {
104
110
  };
105
111
  }
106
112
 
113
+ function botChainField(value) {
114
+ const bots = botChainOf(value);
115
+ return bots.length ? { botChain: bots } : {};
116
+ }
117
+
107
118
  function submissionIdsField(value) {
108
119
  const ids = Array.isArray(value) ? value.filter((id) => typeof id === 'string' && id) : [];
109
120
  return ids.length ? { submissionIds: ids } : {};
@@ -125,6 +136,33 @@ export function transcriptAttachmentFields(value) {
125
136
  return attachmentsField(value?.attachments);
126
137
  }
127
138
 
139
+ const REACTOR_ID = /^[A-Za-z0-9._-]{1,80}$/;
140
+ const REACTION_EMOJI_MAX_LENGTH = 16;
141
+
142
+ /**
143
+ * The bots' reactions on a user message (`meta.transcript.reactions`):
144
+ * `[{ agentId, emoji, at }]`, one per bot, in the order they were left.
145
+ * Malformed entries and a bot's repeats are dropped.
146
+ */
147
+ export function normalizeMessageReactions(value) {
148
+ if (!Array.isArray(value)) return [];
149
+ const out = [];
150
+ for (const raw of value) {
151
+ const agentId = typeof raw?.agentId === 'string' ? raw.agentId : '';
152
+ const emoji = typeof raw?.emoji === 'string' ? raw.emoji : '';
153
+ if (!REACTOR_ID.test(agentId) || !emoji || emoji.length > REACTION_EMOJI_MAX_LENGTH) continue;
154
+ if (out.some((entry) => entry.agentId === agentId)) continue;
155
+ out.push({ agentId, emoji, ...(Number.isFinite(raw.at) ? { at: raw.at } : {}) });
156
+ }
157
+ return out;
158
+ }
159
+
160
+ /** The `reactions` field of a transcript row (absent when there are none). */
161
+ export function transcriptReactionsField(value) {
162
+ const reactions = normalizeMessageReactions(value?.reactions);
163
+ return reactions.length ? { reactions } : {};
164
+ }
165
+
128
166
  export function persistedAssistantTranscriptMetadata(value, fallbackAt = Date.now()) {
129
167
  if (!value || typeof value !== 'object') return null;
130
168
  const candidateAt = Number(value.assistantAt);
@@ -438,13 +438,41 @@ const ensuredDataDirs = new Set();
438
438
  * data dir per process.
439
439
  */
440
440
  export function ensureUserDataInitialized(dataDir) {
441
- if (!dataDir) return;
441
+ if (!dataDir || skipBackups()) return;
442
442
  const key = normalizeDataDir(dataDir);
443
443
  if (ensuredDataDirs.has(key)) return;
444
- if (!hasUserDataInitMarker(dataDir)) backupUserData(dataDir, 'initial-snapshot');
444
+ // The marker alone is no proof: a first snapshot whose manifest failed left
445
+ // it behind. Only a published backup of this incarnation counts, so a failed
446
+ // or interrupted one is taken again by the next read or restart.
447
+ if (!hasPublishedBackup(dataDir)) {
448
+ backupUserData(dataDir, 'initial-snapshot');
449
+ if (!hasPublishedBackup(dataDir)) return;
450
+ }
445
451
  ensuredDataDirs.add(key);
446
452
  }
447
453
 
454
+ /** Whether this data dir's scope holds a published backup of its current
455
+ * incarnation (marker instanceId). A transient manifest read failure throws. */
456
+ function hasPublishedBackup(dataDir) {
457
+ const instanceId = readInitInstanceId(dataDir);
458
+ if (!instanceId) return false;
459
+ const key = normalizeDataDir(dataDir);
460
+ const scopeDir = scopeDirFor(dataDir);
461
+ return listBackupDirsNewestFirst(scopeDir).some((name) => {
462
+ try {
463
+ const manifest = JSON.parse(readFileSync(manifestPath(scopeDir, name), 'utf8'));
464
+ return (
465
+ typeof manifest?.dataDir === 'string' &&
466
+ normalizeDataDir(manifest.dataDir) === key &&
467
+ manifest.instanceId === instanceId
468
+ );
469
+ } catch (error) {
470
+ if (isAbsentBackupError(error)) return false;
471
+ throw error;
472
+ }
473
+ });
474
+ }
475
+
448
476
  function skipBackups() {
449
477
  return process.env.MIXDOG_SKIP_USER_DATA_BACKUP === '1' || process.env.MIXDOG_SKIP_USER_DATA_BACKUP === 'true';
450
478
  }
@@ -15,9 +15,11 @@ const overlaps = (a, b) => isWithin(a, b) || isWithin(b, a);
15
15
  /**
16
16
  * The admission policy's external exception: a linked checkout Mixdog did not
17
17
  * create (not a project's main folder, not a registered managed worktree) is
18
- * never lock-blocked. Management takes no lease on it and its readers are
19
- * admitted whatever is held elsewhere; its running readers still refuse a
20
- * management action that touches it.
18
+ * not lock-blocked by what is held elsewhere. Management takes no lease on it
19
+ * and its readers are admitted whatever is held outside it; its running
20
+ * readers still refuse a management action that touches it. A management lease
21
+ * published inside the checkout itself (another profile, or a registry
22
+ * recovered empty) still refuses its readers.
21
23
  */
22
24
  export function isExternalCheckout(folder) {
23
25
  const layout = findGitLayout(folder, { strict: true });
@@ -291,7 +293,8 @@ function release(commonDir, token) {
291
293
 
292
294
  /**
293
295
  * Readers share a checkout freely. Null means a management writer currently
294
- * owns it; a reader in an external checkout is never held back. Throws
296
+ * owns it; a reader in an external checkout is admitted whatever is held
297
+ * outside that checkout, but refused by a management lease inside it. Throws
295
298
  * `workspace-admission-unreadable` when the ledger cannot be read, was
296
299
  * written by a newer build, or the grant could not be made durable in both
297
300
  * copies: no work may run unrecorded.
@@ -304,8 +307,12 @@ export function tryAdmitWorkspaceActivity(sessionId, cwd, { pid = process.pid, k
304
307
  const path = canonicalPath(cwd);
305
308
  const token = randomUUID();
306
309
  const lease = { kind, sessionId, pid, token, path };
310
+ // External is this profile's registry view: management another profile (or
311
+ // a registry recovered empty) published inside this very checkout still holds.
312
+ const checkout = canonicalPath(layout.topLevel);
313
+ const held = (other) => (external ? isWithin(checkout, other.path) : overlaps(other.path, path));
307
314
  const admitted = mutate(layout.commonDir, (leases) => {
308
- if (!external && leases.some((other) => other.kind === 'management' && overlaps(other.path, path))) return false;
315
+ if (leases.some((other) => other.kind === 'management' && held(other))) return false;
309
316
  leases.push(lease);
310
317
  return true;
311
318
  });
@@ -1,6 +1,7 @@
1
1
  // Temporary git repositories and data dirs for the worktree suites.
2
+ import '../git-direct.test-support.mjs';
2
3
  import { execFileSync } from 'node:child_process';
3
- import { chmodSync, cpSync, existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
4
+ import { appendFileSync, chmodSync, cpSync, existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
4
5
  import { tmpdir } from 'node:os';
5
6
  import { dirname, join } from 'node:path';
6
7
 
@@ -67,9 +68,13 @@ function repoTemplate(commit) {
67
68
  if (existing && existsSync(existing)) return existing;
68
69
  const dir = tempDir('mixdog-wt-template-');
69
70
  git(dir, 'init', '-q', '-b', 'main');
70
- git(dir, 'config', 'user.name', 'Mixdog Test');
71
- git(dir, 'config', 'user.email', 'mixdog@example.com');
72
- git(dir, 'config', 'core.autocrlf', 'false');
71
+ // One config write instead of a `git config` process per key. Auto
72
+ // maintenance is off, so no commit (the test's or a merge's) starts a
73
+ // `git maintenance run` child.
74
+ appendFileSync(
75
+ join(dir, '.git', 'config'),
76
+ '[user]\n\tname = Mixdog Test\n\temail = mixdog@example.com\n[core]\n\tautocrlf = false\n[maintenance]\n\tauto = false\n'
77
+ );
73
78
  if (commit) commitFile(dir, 'README.md', 'hello\n', 'initial');
74
79
  templates.set(commit, dir);
75
80
  return dir;
@@ -262,10 +262,19 @@ function oldestRowsOverCap(rows) {
262
262
  function formatRun(value) {
263
263
  const cursor = Number.isSafeInteger(value?.cursor) ? value.cursor : 0;
264
264
  const tab = value?.tab !== undefined ? ` tab=${value.tab}` : '';
265
+ let where = '';
266
+ if (value?.shown === true) where = 'The terminal is shown in the desktop window.';
267
+ else if (value?.shown === false) {
268
+ where =
269
+ 'The terminal is not shown in the desktop window right now (a paired phone may still show it); it keeps running.';
270
+ }
265
271
  return [
266
272
  `Terminal run: ${value?.id || `tab ${value?.tab}`} · ${tabLabel(value)} · cursor=${cursor}`,
267
273
  `Command sent to the side terminal. Read its output with action=read${tab} since=${cursor}.`,
268
- ].join('\n');
274
+ where,
275
+ ]
276
+ .filter(Boolean)
277
+ .join('\n');
269
278
  }
270
279
 
271
280
  async function readJson(response) {
@@ -6,6 +6,7 @@
6
6
  * Response: { ok:true, text, tabs?, path? } | { ok:false, error }.
7
7
  */
8
8
  import { bridgeDiscoveryChanged, readBridgeDiscovery, readBridgeDiscoveryDetail } from '../bridge-discovery.mjs';
9
+ import { visualizeOwnerToolMeta } from './tool-meta.mjs';
9
10
 
10
11
  const DISCOVERY_FILE = 'visualize-bridge.json';
11
12
  const REQUEST_TIMEOUT_MS = 30_000;
@@ -38,12 +39,14 @@ export function validateVisualizeArgs(args) {
38
39
  if (!args || typeof args !== 'object' || Array.isArray(args)) {
39
40
  return { ok: false, error: 'visualize arguments must be an object' };
40
41
  }
41
- if (!ACTIONS.has(args.action)) return { ok: false, error: 'action must be open, close, annotate, capture, export, check, or inspect' };
42
+ if (!ACTIONS.has(args.action))
43
+ return { ok: false, error: 'action must be open, close, annotate, capture, export, check, or inspect' };
42
44
  const input = args.input ?? {};
43
45
  if (typeof input !== 'object' || Array.isArray(input)) return { ok: false, error: 'input must be an object' };
44
46
  if (args.action === 'open' && input.content != null) {
45
47
  const content = input.content;
46
- if (input.targets != null) return { ok: false, error: 'open takes either input.targets or input.content, not both' };
48
+ if (input.targets != null)
49
+ return { ok: false, error: 'open takes either input.targets or input.content, not both' };
47
50
  if (typeof content !== 'object' || Array.isArray(content)) {
48
51
  return { ok: false, error: 'open input.content must be { kind, source, title? }' };
49
52
  }
@@ -79,7 +82,10 @@ export function validateVisualizeArgs(args) {
79
82
  return { ok: false, error: 'open input.version must be a positive integer (a kept version number)' };
80
83
  }
81
84
  }
82
- if (['annotate', 'capture', 'export', 'check', 'inspect'].includes(args.action) && (typeof input.tab !== 'string' || !input.tab)) {
85
+ if (
86
+ ['annotate', 'capture', 'export', 'check', 'inspect'].includes(args.action) &&
87
+ (typeof input.tab !== 'string' || !input.tab)
88
+ ) {
83
89
  return { ok: false, error: `${args.action} requires input.tab` };
84
90
  }
85
91
  if (args.action === 'inspect') {
@@ -92,7 +98,10 @@ export function validateVisualizeArgs(args) {
92
98
  !sized(viewport.width, 200, 3000) ||
93
99
  (viewport.height != null && !sized(viewport.height, 200, 4000))
94
100
  ) {
95
- return { ok: false, error: 'inspect input.viewport must be { width: 200-3000, height?: 200-4000 } in whole pixels' };
101
+ return {
102
+ ok: false,
103
+ error: 'inspect input.viewport must be { width: 200-3000, height?: 200-4000 } in whole pixels',
104
+ };
96
105
  }
97
106
  }
98
107
  if (selector != null && (typeof selector !== 'string' || !selector.trim() || selector.length > 500)) {
@@ -103,7 +112,8 @@ export function validateVisualizeArgs(args) {
103
112
  return { ok: false, error: 'capture requires input.output ending in .png' };
104
113
  }
105
114
  if (args.action === 'export') {
106
- if (input.format !== 'png' && input.format !== 'pdf') return { ok: false, error: 'export requires input.format png or pdf' };
115
+ if (input.format !== 'png' && input.format !== 'pdf')
116
+ return { ok: false, error: 'export requires input.format png or pdf' };
107
117
  if (typeof input.output !== 'string' || !new RegExp('\\.' + input.format + '$', 'i').test(input.output.trim())) {
108
118
  return { ok: false, error: `export requires input.output ending in .${input.format}` };
109
119
  }
@@ -160,7 +170,11 @@ export async function executeVisualizeTool(args, options = {}) {
160
170
  for (let attempt = 0; attempt < 2; attempt += 1) {
161
171
  const timeout = AbortSignal.timeout(validated.action === 'export' ? EXPORT_REQUEST_TIMEOUT_MS : REQUEST_TIMEOUT_MS);
162
172
  try {
163
- result = await requestBridge(discovery, payload, options.signal ? AbortSignal.any([timeout, options.signal]) : timeout);
173
+ result = await requestBridge(
174
+ discovery,
175
+ payload,
176
+ options.signal ? AbortSignal.any([timeout, options.signal]) : timeout
177
+ );
164
178
  break;
165
179
  } catch (error) {
166
180
  if (error?.name === 'TimeoutError') return toolError('visualize bridge timed out');
@@ -175,7 +189,12 @@ export async function executeVisualizeTool(args, options = {}) {
175
189
  }
176
190
  const { body, status } = result;
177
191
  if (!body?.ok) {
178
- return toolError(String(body?.error || `visualize bridge request failed (HTTP ${status})`).replace(/^Error:\s*/, ''));
192
+ return toolError(
193
+ String(body?.error || `visualize bridge request failed (HTTP ${status})`).replace(/^Error:\s*/, '')
194
+ );
179
195
  }
180
- return { content: [{ type: 'text', text: formatSuccess(body) }] };
196
+ // The host ran it for another session's panel: the owner rides as a field
197
+ // (toolMeta, kept with the stored result), never only as prose.
198
+ const toolMeta = visualizeOwnerToolMeta(body.ownerSessionId);
199
+ return { content: [{ type: 'text', text: formatSuccess(body) }], ...(toolMeta ? { toolMeta } : {}) };
181
200
  }
@@ -10,8 +10,8 @@ export const TOOL_DEFS = [
10
10
  name: 'visualize',
11
11
  title: 'Mixdog Visualize',
12
12
  description:
13
- "Show the user any artifact you produced or changed (mockups, docs, decks, sheets, PDFs, images, video, local dev pages) in the desktop side panel beside the chat. Panel tabs auto-reload when a file changes, so re-opening after edits is unnecessary. " +
14
- 'open: input.targets (1-3 file paths, 2-3 = side-by-side comparison; or a single http(s) URL alone, never mixed with files or other URLs), optional label, optional version (show that kept version of a file tab read-only; each open/reload that changes the files keeps a version). close: optional tab (omit = all). ' +
13
+ 'Show the user any artifact you produced or changed (mockups, docs, decks, sheets, PDFs, images, video, local dev pages) in the desktop side panel beside the chat. Panel tabs auto-reload when a file changes, so re-opening after edits is unnecessary. ' +
14
+ 'open: input.targets (1-3 file paths, relative to the working directory or absolute anywhere on this machine; 2-3 = side-by-side comparison; or a single http(s) URL alone, never mixed with files or other URLs), optional label, optional version (show that kept version of a file tab read-only; each open/reload that changes the files keeps a version). close: optional tab (omit = all). ' +
15
15
  'annotate: tab + add [{selector,label?}] | remove [selector] | clear; HTML pages only, never edits the file. capture: tab + output (.png path). ' +
16
16
  'export: tab + format (png|pdf) + output (project-relative path ending .png/.pdf) + optional version (a kept version of the tab) + optional overwrite (true replaces an existing file, only under design/; otherwise an existing file is refused). Writes a record of the approved page inside the project and returns its project-relative path, size/dimensions and the page problems seen while rendering. A single HTML page (file, or inline html/chart) is rendered whole in a hidden window, so the tab need not be on screen (PDF: HTML pages only; PNG has a pixel cap, over it export pdf or shorten the page); any other tab exports as capture does (the displayed tab, PNG only). Same project containment as capture: links and paths outside the working directory are refused. ' +
17
17
  'check: tab; HTML pages only; waits briefly for the page to settle and returns only its runtime problems (uncaught errors, unhandled rejections, CSP violations, console errors/warnings, missing local assets). ' +
@@ -36,7 +36,8 @@ export const TOOL_DEFS = [
36
36
  items: { type: 'string' },
37
37
  minItems: 1,
38
38
  maxItems: 3,
39
- description: 'open: 1-3 project-relative/absolute file paths (side by side), or exactly one http(s) URL as the only target.',
39
+ description:
40
+ 'open: 1-3 file paths (side by side), relative to the working directory or absolute in any folder, or exactly one http(s) URL as the only target.',
40
41
  },
41
42
  content: {
42
43
  type: 'object',
@@ -57,7 +58,8 @@ export const TOOL_DEFS = [
57
58
  version: {
58
59
  type: 'integer',
59
60
  minimum: 1,
60
- description: 'open (file tabs): kept version number to show read-only instead of the live files. export: kept version number to render instead of the live page.',
61
+ description:
62
+ 'open (file tabs): kept version number to show read-only instead of the live files. export: kept version number to render instead of the live page.',
61
63
  },
62
64
  tab: { type: 'string', description: 'Tab id from open (close: omit for all).' },
63
65
  add: {
@@ -72,9 +74,16 @@ export const TOOL_DEFS = [
72
74
  },
73
75
  remove: { type: 'array', items: { type: 'string' }, description: 'annotate: selectors to un-outline.' },
74
76
  clear: { type: 'boolean', description: 'annotate: remove all outlines.' },
75
- output: { type: 'string', description: 'capture: project path ending .png. export: project path ending .png or .pdf (matching format).' },
77
+ output: {
78
+ type: 'string',
79
+ description:
80
+ 'capture: project path ending .png. export: project path ending .png or .pdf (matching format).',
81
+ },
76
82
  format: { type: 'string', enum: ['png', 'pdf'], description: 'export: file format.' },
77
- overwrite: { type: 'boolean', description: 'export: replace an existing output file; honored only for paths under design/.' },
83
+ overwrite: {
84
+ type: 'boolean',
85
+ description: 'export: replace an existing output file; honored only for paths under design/.',
86
+ },
78
87
  viewport: {
79
88
  type: 'object',
80
89
  properties: {
@@ -83,9 +92,14 @@ export const TOOL_DEFS = [
83
92
  },
84
93
  required: ['width'],
85
94
  additionalProperties: false,
86
- description: 'inspect: size to lay the page out at (height defaults to 800); omit for the panel\'s current size.',
95
+ description:
96
+ "inspect: size to lay the page out at (height defaults to 800); omit for the panel's current size.",
97
+ },
98
+ selector: {
99
+ type: 'string',
100
+ maxLength: 500,
101
+ description: 'inspect: CSS selector scoping the read-out to one subtree.',
87
102
  },
88
- selector: { type: 'string', maxLength: 500, description: 'inspect: CSS selector scoping the read-out to one subtree.' },
89
103
  },
90
104
  additionalProperties: false,
91
105
  },
@@ -0,0 +1,5 @@
1
+ export declare const VISUALIZE_OWNER_META_CODE: 'visualize_owner';
2
+ export declare function visualizeOwnerMetaSessionId(value: unknown): string;
3
+ export declare function visualizeOwnerToolMeta(
4
+ ownerSessionId: unknown
5
+ ): { code: typeof VISUALIZE_OWNER_META_CODE; missing: string[]; ownerSessionId: string } | undefined;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The structured twin of a `visualize` result (the tool result's `toolMeta`,
3
+ * kept with the stored message and restored onto its transcript item): the
4
+ * desktop host ran the call for another session's work (a sub-agent, a room
5
+ * member) and keeps its tabs in that owner conversation's panel. Chat cards
6
+ * read the owner from here, never from the result text.
7
+ */
8
+ export const VISUALIZE_OWNER_META_CODE = 'visualize_owner';
9
+
10
+ const SESSION_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
11
+
12
+ /** A session id fit for `toolMeta.ownerSessionId`; '' for anything else. */
13
+ export function visualizeOwnerMetaSessionId(value) {
14
+ return typeof value === 'string' && SESSION_ID.test(value) ? value : '';
15
+ }
16
+
17
+ /** The `toolMeta` of a result run for `ownerSessionId`'s panel; undefined when
18
+ * the id is not a session id. */
19
+ export function visualizeOwnerToolMeta(ownerSessionId) {
20
+ const id = visualizeOwnerMetaSessionId(ownerSessionId);
21
+ return id ? { code: VISUALIZE_OWNER_META_CODE, missing: [], ownerSessionId: id } : undefined;
22
+ }
@@ -81,7 +81,7 @@ function fetchFailureKind(error) {
81
81
 
82
82
  export async function runFetchPipeline(
83
83
  url,
84
- { http, browser, signal, timeoutMs = 30000, hostPacer = pacer, onAttempt = () => {} } = {}
84
+ { http, browser, signal, timeoutMs = 30000, hostPacer = pacer, onAttempt = () => {}, sleep: pause = sleep } = {}
85
85
  ) {
86
86
  const budget = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 30000;
87
87
  const deadline = Date.now() + budget;
@@ -150,7 +150,7 @@ export async function runFetchPipeline(
150
150
  if (!transient || retry === 1) break;
151
151
  const delay = Math.max(400, error.retryAfterMs || 0);
152
152
  if (Date.now() + delay >= deadline) throw error;
153
- await sleep(delay, undefined, { signal: overall });
153
+ await pause(delay, undefined, { signal: overall });
154
154
  }
155
155
  }
156
156
  // Rendering does not fix a rate limit or a failing upstream server.