mixdog 1.3.1 → 1.3.3

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 (468) hide show
  1. package/README.md +8 -4
  2. package/package.json +8 -8
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/scripts/verify-runtime-dependency-versions.mjs +158 -0
  5. package/src/agents/reviewer/AGENT.md +5 -7
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +9 -1
  8. package/src/defaults/skills/browser-use/SKILL.md +6 -3
  9. package/src/defaults/skills/local-provider/SKILL.md +33 -6
  10. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  11. package/src/defaults/skills/setup/SKILL.md +23 -1
  12. package/src/defaults/skills/setup/references/actions.md +50 -3
  13. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  14. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  15. package/src/defaults/skills/visualize/SKILL.md +9 -6
  16. package/src/defaults/skills/visualize/references/craft.md +8 -1
  17. package/src/help.mjs +1 -1
  18. package/src/lib/keychain-cjs.cjs +4 -1
  19. package/src/lib/rules-builder.cjs +14 -7
  20. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  21. package/src/rules/agent-mode/MIXY.md +6 -8
  22. package/src/rules/lead/LEAD.md +7 -10
  23. package/src/rules/shared/10-tool-workflow.md +4 -0
  24. package/src/rules/shared/20-research.md +2 -0
  25. package/src/rules/shared/45-destructive.md +5 -1
  26. package/src/rules/shared/60-verification.md +6 -4
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  29. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  30. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  31. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  32. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  33. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  35. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  37. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  38. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  39. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  40. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  41. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
  45. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  46. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
  48. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
  50. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  51. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  52. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  54. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  55. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
  56. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  57. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  59. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  60. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  62. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  64. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  65. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  69. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  70. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  72. package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
  73. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
  74. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  75. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
  78. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  79. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
  81. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  82. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  83. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
  85. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
  86. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  87. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
  90. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  92. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  93. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  94. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
  95. package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
  96. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  97. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
  99. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
  100. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  101. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
  102. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
  103. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
  107. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
  108. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  109. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  110. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
  111. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
  112. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  113. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
  114. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  115. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  116. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  117. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  118. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
  119. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
  120. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
  121. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
  123. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
  124. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  125. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  126. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
  127. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  128. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
  129. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
  130. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
  131. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  132. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  134. package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
  135. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
  136. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
  137. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  138. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
  139. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  143. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
  144. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
  145. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  146. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  148. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  150. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
  151. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
  155. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  156. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
  157. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
  158. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  160. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  161. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
  163. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
  166. package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
  167. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
  168. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  169. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  170. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  171. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  172. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
  178. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
  179. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  180. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  181. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
  182. package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
  183. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
  184. package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
  185. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  186. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  189. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  190. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  196. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
  200. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  201. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  202. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  203. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  204. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
  206. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  207. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  208. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  209. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  210. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  211. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  212. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  213. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  214. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  215. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  216. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  217. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  218. package/src/runtime/attachments/legacy-office.mjs +110 -0
  219. package/src/runtime/attachments/limits.mjs +5 -1
  220. package/src/runtime/attachments/office-extract.mjs +3 -6
  221. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  222. package/src/runtime/attachments/prompt-restore.mjs +70 -0
  223. package/src/runtime/attachments/store.mjs +109 -33
  224. package/src/runtime/browser-bridge/client.mjs +73 -20
  225. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  226. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  227. package/src/runtime/channels/lib/state-file.mjs +15 -15
  228. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  230. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  231. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  232. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  233. package/src/runtime/effort-judge/judge-client.mjs +66 -16
  234. package/src/runtime/effort-judge/model-install.mjs +47 -7
  235. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  236. package/src/runtime/local-provider/catalog.mjs +5 -2
  237. package/src/runtime/local-provider/registered-models.mjs +64 -27
  238. package/src/runtime/local-provider/server.mjs +2 -3
  239. package/src/runtime/media/adapters/image-api.mjs +89 -0
  240. package/src/runtime/media/auth.mjs +64 -1
  241. package/src/runtime/media/catalog.mjs +66 -4
  242. package/src/runtime/media/jobs.mjs +4 -0
  243. package/src/runtime/media/lanes.mjs +46 -5
  244. package/src/runtime/media/media-usage.mjs +17 -0
  245. package/src/runtime/media/renditions.mjs +15 -15
  246. package/src/runtime/media/store.mjs +36 -35
  247. package/src/runtime/media/tool.mjs +8 -1
  248. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  249. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  250. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  251. package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
  252. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  253. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  254. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  255. package/src/runtime/office/core/journal.mjs +7 -14
  256. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  257. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  258. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  259. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  260. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  261. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  262. package/src/runtime/office/portable/portable-soffice.mjs +135 -29
  263. package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
  264. package/src/runtime/shared/agent-avatar.mjs +3 -2
  265. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  266. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  267. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  268. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  269. package/src/runtime/shared/agent-room-host.d.mts +31 -0
  270. package/src/runtime/shared/agent-room-host.mjs +58 -0
  271. package/src/runtime/shared/agent-store.mjs +24 -7
  272. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  273. package/src/runtime/shared/atomic-file.d.mts +50 -0
  274. package/src/runtime/shared/atomic-file.mjs +394 -132
  275. package/src/runtime/shared/background-tasks.mjs +7 -4
  276. package/src/runtime/shared/bot-hops.mjs +35 -0
  277. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  278. package/src/runtime/shared/bounded-download.mjs +4 -2
  279. package/src/runtime/shared/config.mjs +39 -9
  280. package/src/runtime/shared/defaults-separation.mjs +6 -2
  281. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  282. package/src/runtime/shared/err-text.mjs +4 -0
  283. package/src/runtime/shared/error-presentation.mjs +22 -4
  284. package/src/runtime/shared/file-has-data.mjs +28 -0
  285. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  286. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  287. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  288. package/src/runtime/shared/projects.mjs +50 -41
  289. package/src/runtime/shared/provider-accounts.mjs +17 -12
  290. package/src/runtime/shared/redact-directories.mjs +18 -0
  291. package/src/runtime/shared/remove-directory.mjs +28 -0
  292. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  293. package/src/runtime/shared/routine-trigger.mjs +14 -6
  294. package/src/runtime/shared/run-child.d.mts +3 -0
  295. package/src/runtime/shared/run-child.mjs +24 -3
  296. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  297. package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
  298. package/src/runtime/shared/session-sidebar-host.mjs +16 -3
  299. package/src/runtime/shared/singleton-owner.mjs +7 -6
  300. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  301. package/src/runtime/shared/staged-update.mjs +4 -2
  302. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  303. package/src/runtime/shared/tool-execution-contract.mjs +10 -0
  304. package/src/runtime/shared/tool-work-units.mjs +12 -9
  305. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  306. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  307. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  308. package/src/runtime/shared/turn-snapshot.mjs +277 -124
  309. package/src/runtime/shared/user-cwd.mjs +9 -4
  310. package/src/runtime/shared/user-data-guard.mjs +92 -61
  311. package/src/runtime/shared/utf8-cut.mjs +10 -0
  312. package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
  313. package/src/runtime/shared/worktrees/contract.mjs +11 -3
  314. package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
  315. package/src/runtime/shared/worktrees/manager.mjs +419 -52
  316. package/src/runtime/shared/worktrees/registry.mjs +29 -27
  317. package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
  318. package/src/runtime/tidy/core-install.mjs +26 -4
  319. package/src/runtime/tidy/install.mjs +91 -23
  320. package/src/runtime/tidy/process.mjs +41 -8
  321. package/src/runtime/tidy/resolve.mjs +10 -7
  322. package/src/runtime/web-search/lib/config.mjs +4 -16
  323. package/src/session-runtime/agent-home-delivery.mjs +542 -204
  324. package/src/session-runtime/agent-profile-changes.mjs +16 -0
  325. package/src/session-runtime/agent-room-delivery.mjs +81 -35
  326. package/src/session-runtime/agent-task-sessions.mjs +811 -179
  327. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  328. package/src/session-runtime/boot/apis.mjs +48 -24
  329. package/src/session-runtime/boot/config.mjs +1 -0
  330. package/src/session-runtime/boot/facade.mjs +16 -7
  331. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  332. package/src/session-runtime/boot/providers.mjs +2 -2
  333. package/src/session-runtime/boot/shared.mjs +12 -6
  334. package/src/session-runtime/boot/tools.mjs +23 -3
  335. package/src/session-runtime/bot-status.mjs +432 -0
  336. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  337. package/src/session-runtime/config-lifecycle.mjs +60 -55
  338. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  339. package/src/session-runtime/held-requests.mjs +31 -33
  340. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  341. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  342. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
  343. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  344. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  345. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  346. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  347. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  348. package/src/session-runtime/media-api.mjs +5 -2
  349. package/src/session-runtime/model-recency.mjs +1 -0
  350. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  351. package/src/session-runtime/model-route/set-route.mjs +61 -1
  352. package/src/session-runtime/owned-automation-run.mjs +8 -1
  353. package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
  354. package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
  355. package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
  356. package/src/session-runtime/route-resolution.mjs +16 -8
  357. package/src/session-runtime/route-state.mjs +1 -59
  358. package/src/session-runtime/routine-home-turn.mjs +5 -3
  359. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  360. package/src/session-runtime/runtime-paths.mjs +11 -5
  361. package/src/session-runtime/runtime-review-api.mjs +4 -1
  362. package/src/session-runtime/runtime-tunables.mjs +6 -0
  363. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  364. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  365. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  366. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  367. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
  368. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  369. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  370. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
  371. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  372. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  373. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  374. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  375. package/src/session-runtime/services/agent-tool.mjs +6 -0
  376. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  377. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  378. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  379. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  380. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  381. package/src/session-runtime/services/provider-admin.mjs +26 -6
  382. package/src/session-runtime/services/schedule-access.mjs +33 -4
  383. package/src/session-runtime/services/seeds.mjs +4 -3
  384. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  385. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  386. package/src/session-runtime/services/worktree-access.mjs +27 -0
  387. package/src/session-runtime/session-create/materialize.mjs +2 -3
  388. package/src/session-runtime/session-create/session-options.mjs +15 -2
  389. package/src/session-runtime/session-hooks.mjs +9 -17
  390. package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
  391. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  392. package/src/session-runtime/setup-tool/executor.mjs +12 -2
  393. package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
  394. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  395. package/src/session-runtime/skills-api.mjs +12 -5
  396. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
  397. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  398. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  399. package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
  400. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  401. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  402. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  403. package/src/session-runtime/tool-policy-refresh.mjs +48 -369
  404. package/src/session-runtime/tool-surface.mjs +10 -4
  405. package/src/session-runtime/turn/session-ops.mjs +8 -0
  406. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  407. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  408. package/src/session-runtime/turn/turn-run.mjs +7 -2
  409. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  410. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
  411. package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
  412. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  413. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  414. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  415. package/src/session-runtime/workspace/transition-steps.mjs +1 -18
  416. package/src/session-runtime/workspace/transitions.mjs +279 -67
  417. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  418. package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
  419. package/src/standalone/daemon-agent-control.mjs +4 -1
  420. package/src/standalone/daemon-lag-profiler.mjs +3 -1
  421. package/src/standalone/daemon-session-control.mjs +173 -15
  422. package/src/standalone/daemon-telemetry.mjs +9 -0
  423. package/src/standalone/daemon.mjs +33 -10
  424. package/src/standalone/local-session-runtime.mjs +33 -16
  425. package/src/standalone/session-client.mjs +7 -2
  426. package/src/standalone/session-protocol.mjs +8 -0
  427. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
  428. package/src/standalone/session-service/agent-route-follow.mjs +69 -0
  429. package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
  430. package/src/standalone/session-service.mjs +21 -0
  431. package/src/standalone/session-state-projection.mjs +19 -9
  432. package/src/standalone/session-tool-output.mjs +36 -9
  433. package/src/standalone/staged-import.mjs +47 -0
  434. package/src/tui/App.jsx +2 -0
  435. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  436. package/src/tui/app/message-selector.mjs +15 -2
  437. package/src/tui/app/model-options.mjs +16 -2
  438. package/src/tui/app/model-picker.mjs +2 -1
  439. package/src/tui/app/route-pickers.mjs +3 -1
  440. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  441. package/src/tui/app/slash-commands.mjs +1 -1
  442. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  443. package/src/tui/dist/index.mjs +617 -369
  444. package/src/tui/paste-attachments.mjs +0 -1
  445. package/src/tui/paste-text-policy.mjs +4 -5
  446. package/src/tui/prompt-history-store.mjs +16 -21
  447. package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
  448. package/src/tui/session/context-state.mjs +4 -0
  449. package/src/tui/session/notice-surface.mjs +3 -0
  450. package/src/tui/session/session-api/intake/abort.mjs +52 -18
  451. package/src/tui/session/session-api/intake/rewind.mjs +39 -4
  452. package/src/tui/session/session-api/media.mjs +2 -1
  453. package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
  454. package/src/tui/session/session-api/routes-write.mjs +203 -0
  455. package/src/tui/session/session-api/routes.mjs +23 -247
  456. package/src/tui/session/session-api/settings.mjs +14 -1
  457. package/src/tui/session/session-flow/drain.mjs +28 -3
  458. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  459. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  460. package/src/tui/session/session-flow/steering.mjs +8 -3
  461. package/src/tui/session/tool-approval.mjs +142 -14
  462. package/src/tui/session/transcript-intake.mjs +2 -0
  463. package/src/tui/session/turn.mjs +23 -14
  464. package/src/tui/transcript-tool-failures.mjs +8 -2
  465. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  466. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  467. package/src/rules/agent-mode/ROUTINE.md +0 -6
  468. package/src/session-runtime/room-member-gate.mjs +0 -48
@@ -23113,10 +23113,10 @@ var require_stack_utils = __commonJS({
23113
23113
  });
23114
23114
 
23115
23115
  // src/runtime/shared/file-permissions.mjs
23116
- import { existsSync as existsSync4 } from "node:fs";
23117
- import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
23116
+ import { existsSync as existsSync3 } from "node:fs";
23117
+ import { dirname, join, resolve as resolve2 } from "node:path";
23118
23118
  import { execFile as execFile2, execFileSync as execFileSync2, spawn as spawn3 } from "node:child_process";
23119
- import { fileURLToPath as fileURLToPath2 } from "node:url";
23119
+ import { fileURLToPath } from "node:url";
23120
23120
  import { MessageChannel as MessageChannel2, receiveMessageOnPort, SHARE_ENV, Worker } from "node:worker_threads";
23121
23121
  function execFileAsync(file, args, options) {
23122
23122
  return new Promise((done, fail) => {
@@ -23169,7 +23169,7 @@ function startAclHelper(icacls, sid) {
23169
23169
  let child;
23170
23170
  try {
23171
23171
  child = spawn3(command.executable, command.args, {
23172
- cwd: dirname3(icacls),
23172
+ cwd: dirname(icacls),
23173
23173
  stdio: ["pipe", "pipe", "ignore"],
23174
23174
  windowsHide: true
23175
23175
  });
@@ -23250,7 +23250,7 @@ function syncBridge() {
23250
23250
  if (aclSyncBridge || aclSyncBridgeBroken) return aclSyncBridge;
23251
23251
  try {
23252
23252
  const url = new URL("./file-permissions-worker.mjs", import.meta.url);
23253
- if (!existsSync4(fileURLToPath2(url))) throw new Error("Windows ACL bridge worker is not shipped here");
23253
+ if (!existsSync3(fileURLToPath(url))) throw new Error("Windows ACL bridge worker is not shipped here");
23254
23254
  const { port1, port2 } = new MessageChannel2();
23255
23255
  const signal = new Int32Array(new SharedArrayBuffer(12));
23256
23256
  const worker = new Worker(url, {
@@ -23304,7 +23304,7 @@ function askSyncBridge(targetPath) {
23304
23304
  }
23305
23305
  function systemToolPath(name) {
23306
23306
  const systemRoot = process.env.SystemRoot || process.env.windir;
23307
- return systemRoot ? join3(systemRoot, "System32", name) : null;
23307
+ return systemRoot ? join(systemRoot, "System32", name) : null;
23308
23308
  }
23309
23309
  function currentUserSid(output) {
23310
23310
  return String(output).trim().match(/^"(?:[^"]|"")*","(S-\d+(?:-\d+)+)"$/)?.[1] || null;
@@ -23316,7 +23316,7 @@ function unresolvedUserError() {
23316
23316
  }
23317
23317
  function resolveCurrentUserPrincipal() {
23318
23318
  const whoami = systemToolPath("whoami.exe");
23319
- if (whoami && existsSync4(whoami)) {
23319
+ if (whoami && existsSync3(whoami)) {
23320
23320
  try {
23321
23321
  const output = execFileSync2(whoami, ["/user", "/fo", "csv", "/nh"], {
23322
23322
  encoding: "utf8",
@@ -23332,7 +23332,7 @@ function resolveCurrentUserPrincipal() {
23332
23332
  }
23333
23333
  async function resolveCurrentUserPrincipalAsync() {
23334
23334
  const whoami = systemToolPath("whoami.exe");
23335
- if (whoami && existsSync4(whoami)) {
23335
+ if (whoami && existsSync3(whoami)) {
23336
23336
  try {
23337
23337
  const { stdout: stdout2 } = await execFileAsync(whoami, ["/user", "/fo", "csv", "/nh"], {
23338
23338
  encoding: "utf8",
@@ -23352,7 +23352,7 @@ function icaclsPath() {
23352
23352
  error.code = "EACLNOROOT";
23353
23353
  throw error;
23354
23354
  }
23355
- if (!existsSync4(path2)) {
23355
+ if (!existsSync3(path2)) {
23356
23356
  const error = new Error(`icacls.exe not found at ${path2}; refusing to leave secret world-readable`);
23357
23357
  error.code = "EACLNOICACLS";
23358
23358
  throw error;
@@ -23361,7 +23361,7 @@ function icaclsPath() {
23361
23361
  }
23362
23362
  function powerShellCommand(icacls, script) {
23363
23363
  return {
23364
- executable: join3(dirname3(icacls), "WindowsPowerShell", "v1.0", "powershell.exe"),
23364
+ executable: join(dirname(icacls), "WindowsPowerShell", "v1.0", "powershell.exe"),
23365
23365
  label: "Windows ACL update",
23366
23366
  args: ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(script, "utf16le").toString("base64")]
23367
23367
  };
@@ -23402,7 +23402,7 @@ function enforceOwnerOnlyAclWin32(targetPath, { fresh = false } = {}) {
23402
23402
  if (process.platform !== "win32") return;
23403
23403
  const icacls = icaclsPath();
23404
23404
  if (fresh) {
23405
- const reply = askSyncBridge(resolve3(targetPath));
23405
+ const reply = askSyncBridge(resolve2(targetPath));
23406
23406
  if (reply.ok === true) return;
23407
23407
  if (reply.ok === false) throw Object.assign(new Error(reply.message), { code: reply.code || "EACLFAIL" });
23408
23408
  if (reply.timedOut) {
@@ -23425,7 +23425,7 @@ async function enforceOwnerOnlyAclWin32Async(targetPath, { fresh = false } = {})
23425
23425
  if (process.platform !== "win32") return;
23426
23426
  const icacls = icaclsPath();
23427
23427
  if (cachedUserSid === null) cachedUserSid = await resolveCurrentUserPrincipalAsync();
23428
- const reply = await askAclHelper(resolve3(targetPath), icacls, cachedUserSid);
23428
+ const reply = await askAclHelper(resolve2(targetPath), icacls, cachedUserSid);
23429
23429
  if (reply.status === "ok" || reply.status === "fixed") return;
23430
23430
  if (reply.status === "err") {
23431
23431
  throw aclFailure(targetPath, new Error(reply.detail || "unknown error"), "Windows ACL helper");
@@ -23500,13 +23500,13 @@ var init_sleep = __esm({
23500
23500
  // src/runtime/shared/file-lock.mjs
23501
23501
  import {
23502
23502
  linkSync,
23503
- mkdirSync as mkdirSync2,
23503
+ mkdirSync,
23504
23504
  readdirSync,
23505
- readFileSync as readFileSync3,
23506
- renameSync as renameSync2,
23505
+ readFileSync as readFileSync2,
23506
+ renameSync,
23507
23507
  statSync,
23508
23508
  unlinkSync as unlinkSync2,
23509
- writeFileSync as writeFileSync2
23509
+ writeFileSync
23510
23510
  } from "node:fs";
23511
23511
  import {
23512
23512
  link as linkAsync,
@@ -23516,7 +23516,7 @@ import {
23516
23516
  unlink as unlinkAsync,
23517
23517
  writeFile as writeFileAsync
23518
23518
  } from "node:fs/promises";
23519
- import { basename as basename2, dirname as dirname4, join as join4 } from "node:path";
23519
+ import { basename as basename2, dirname as dirname2, join as join2 } from "node:path";
23520
23520
  import { randomBytes as randomBytes2 } from "node:crypto";
23521
23521
  import { AsyncLocalStorage } from "node:async_hooks";
23522
23522
  function markOsHeld(lockPath) {
@@ -23780,7 +23780,7 @@ function lockStaleMs(opts) {
23780
23780
  function sweepStagedLockFiles(lockPath, now = Date.now()) {
23781
23781
  if (sweptLockPaths.has(lockPath)) return 0;
23782
23782
  sweptLockPaths.add(lockPath);
23783
- const dir = dirname4(lockPath);
23783
+ const dir = dirname2(lockPath);
23784
23784
  const prefix = `${basename2(lockPath)}.`;
23785
23785
  let removed = 0;
23786
23786
  let names;
@@ -23793,7 +23793,7 @@ function sweepStagedLockFiles(lockPath, now = Date.now()) {
23793
23793
  if (!name.startsWith(prefix)) continue;
23794
23794
  const match = /^(?:reclaim\.)?(\d+)\.[0-9a-f]{16}\.tmp$/.exec(name.slice(prefix.length));
23795
23795
  if (!match) continue;
23796
- const path2 = join4(dir, name);
23796
+ const path2 = join2(dir, name);
23797
23797
  try {
23798
23798
  if (now - statSync(path2).mtimeMs < STAGED_LOCK_FILE_STALE_MS) continue;
23799
23799
  if (ownerIsLive({ pid: Number(match[1]), token: null })) continue;
@@ -23808,7 +23808,7 @@ function* acquireLock(lockPath, opts, mode, markHeld, timing = null) {
23808
23808
  const timeoutMs = lockTimeoutMs(opts);
23809
23809
  const staleMs = lockStaleMs(opts);
23810
23810
  const deadline = Date.now() + timeoutMs;
23811
- yield ["mkdirp", dirname4(lockPath)];
23811
+ yield ["mkdirp", dirname2(lockPath)];
23812
23812
  sweepStagedLockFiles(lockPath);
23813
23813
  const waitStartedAt = Date.now();
23814
23814
  let attempt = 0;
@@ -23907,10 +23907,10 @@ var init_file_lock = __esm({
23907
23907
  SYNC_IO = {
23908
23908
  stat: (path2) => statSync(path2),
23909
23909
  statId: (path2) => statSync(path2, { bigint: true }),
23910
- rename: (from, to) => renameSync2(from, to),
23911
- read: (path2) => readFileSync3(path2, "utf8"),
23912
- mkdirp: (path2) => mkdirSync2(path2, { recursive: true }),
23913
- createExclusive: (path2, data) => writeFileSync2(path2, data, { encoding: "utf8", mode: 384, flag: "wx" }),
23910
+ rename: (from, to) => renameSync(from, to),
23911
+ read: (path2) => readFileSync2(path2, "utf8"),
23912
+ mkdirp: (path2) => mkdirSync(path2, { recursive: true }),
23913
+ createExclusive: (path2, data) => writeFileSync(path2, data, { encoding: "utf8", mode: 384, flag: "wx" }),
23914
23914
  link: (from, to) => linkSync(from, to),
23915
23915
  unlink: (path2) => unlinkSync2(path2),
23916
23916
  sleep: (ms) => sleepSync(ms)
@@ -23932,15 +23932,16 @@ var init_file_lock = __esm({
23932
23932
  // src/runtime/shared/atomic-file.mjs
23933
23933
  import {
23934
23934
  closeSync,
23935
- existsSync as existsSync5,
23935
+ existsSync as existsSync4,
23936
23936
  fsyncSync,
23937
23937
  linkSync as linkSync2,
23938
- mkdirSync as mkdirSync3,
23938
+ mkdirSync as mkdirSync2,
23939
23939
  openSync,
23940
- readFileSync as readFileSync4,
23941
- renameSync as renameSync3,
23940
+ readFileSync as readFileSync3,
23941
+ renameSync as renameSync2,
23942
+ statSync as statSync2,
23942
23943
  unlinkSync as unlinkSync3,
23943
- writeFileSync as writeFileSync3
23944
+ writeFileSync as writeFileSync2
23944
23945
  } from "node:fs";
23945
23946
  import {
23946
23947
  link as linkAsync2,
@@ -23948,11 +23949,10 @@ import {
23948
23949
  open as openAsync,
23949
23950
  readFile as readFileAsync3,
23950
23951
  rename as renameAsync,
23951
- unlink as unlinkAsync2,
23952
- writeFile as writeFileAsync2
23952
+ unlink as unlinkAsync2
23953
23953
  } from "node:fs/promises";
23954
23954
  import { tmpdir as tmpdir2 } from "node:os";
23955
- import { dirname as dirname5, basename as basename3, join as join5, relative, isAbsolute as isAbsolute2, resolve as resolve4 } from "node:path";
23955
+ import { dirname as dirname3, basename as basename3, join as join3, relative, isAbsolute as isAbsolute2, resolve as resolve3 } from "node:path";
23956
23956
  import { randomBytes as randomBytes3 } from "node:crypto";
23957
23957
  function _drainAdaptiveFileIo() {
23958
23958
  while (_ioActive < _ioLimit && _ioQueue.length > 0) {
@@ -23987,16 +23987,25 @@ function _runAdaptiveFileIo(run) {
23987
23987
  function renameBackoffs(opts) {
23988
23988
  return Array.isArray(opts.backoffs) && opts.backoffs.length > 0 ? opts.backoffs : DEFAULT_BACKOFFS_MS;
23989
23989
  }
23990
- function nextRenameDelayMs(err, attempt, backoffs) {
23990
+ function isReadOnlyFile(path2) {
23991
+ try {
23992
+ const stat2 = statSync2(path2);
23993
+ return stat2.isFile() && (stat2.mode & 128) === 0;
23994
+ } catch {
23995
+ return false;
23996
+ }
23997
+ }
23998
+ function nextRenameDelayMs(err, attempt, backoffs, dst) {
23991
23999
  if (!RETRY_CODES.has(err?.code) || attempt >= backoffs.length) return null;
24000
+ if ((err.code === "EPERM" || err.code === "EACCES") && isReadOnlyFile(dst)) return null;
23992
24001
  return backoffs[attempt] + Math.floor(Math.random() * Math.min(50, Math.max(1, backoffs[attempt])));
23993
24002
  }
23994
24003
  function renameAttempt(src, dst, attempt, opts = {}) {
23995
24004
  try {
23996
- renameSync3(src, dst);
24005
+ renameSync2(src, dst);
23997
24006
  return null;
23998
24007
  } catch (err) {
23999
- const delayMs = nextRenameDelayMs(err, attempt, renameBackoffs(opts));
24008
+ const delayMs = nextRenameDelayMs(err, attempt, renameBackoffs(opts), dst);
24000
24009
  if (delayMs === null) throw err;
24001
24010
  return delayMs;
24002
24011
  }
@@ -24017,13 +24026,16 @@ async function renameWithRetry(src, dst, opts = {}) {
24017
24026
  return true;
24018
24027
  } catch (err) {
24019
24028
  lastErr = err;
24020
- const delayMs = nextRenameDelayMs(err, attempt, backoffs);
24029
+ const delayMs = nextRenameDelayMs(err, attempt, backoffs, dst);
24021
24030
  if (delayMs === null) break;
24022
24031
  await sleep(Math.max(1, Number(delayMs) || 1));
24023
24032
  }
24024
24033
  }
24025
24034
  throw lastErr;
24026
24035
  }
24036
+ function isFileFsyncUnsupported(err) {
24037
+ return FILE_FSYNC_TOLERATED.includes(err?.code);
24038
+ }
24027
24039
  function fsyncPathSync(path2, tolerated) {
24028
24040
  let fd = null;
24029
24041
  try {
@@ -24052,120 +24064,188 @@ async function fsyncPathAsync(path2, tolerated) {
24052
24064
  }
24053
24065
  }
24054
24066
  }
24067
+ function writeFileDurableSync(path2, data, opts) {
24068
+ const fd = openSync(path2, opts.flag || "wx", opts.mode !== void 0 ? opts.mode : 384);
24069
+ try {
24070
+ writeFileSync2(fd, data, { encoding: opts.encoding || "utf8" });
24071
+ if (opts.fsync !== false) {
24072
+ try {
24073
+ fsyncSync(fd);
24074
+ } catch (err) {
24075
+ if (!isFileFsyncUnsupported(err)) throw err;
24076
+ }
24077
+ }
24078
+ } finally {
24079
+ closeSync(fd);
24080
+ }
24081
+ }
24082
+ async function writeFileDurableAsync(path2, data, opts) {
24083
+ const handle = await openAsync(path2, opts.flag || "wx", opts.mode !== void 0 ? opts.mode : 384);
24084
+ try {
24085
+ await handle.writeFile(data, { encoding: opts.encoding || "utf8" });
24086
+ if (opts.fsync !== false) {
24087
+ try {
24088
+ await handle.sync();
24089
+ } catch (err) {
24090
+ if (!isFileFsyncUnsupported(err)) throw err;
24091
+ }
24092
+ }
24093
+ } finally {
24094
+ await handle.close();
24095
+ }
24096
+ }
24097
+ function holdsNoData(content) {
24098
+ if (typeof content === "string") return /^[\0\s]*$/.test(content);
24099
+ for (const byte of content) {
24100
+ if (byte !== 0 && byte !== 32 && byte !== 9 && byte !== 10 && byte !== 13) return false;
24101
+ }
24102
+ return true;
24103
+ }
24104
+ function keepBackupSync(filePath, opts) {
24105
+ let previous;
24106
+ try {
24107
+ previous = readFileSync3(filePath);
24108
+ } catch (err) {
24109
+ if (err?.code === "ENOENT") return;
24110
+ throw err;
24111
+ }
24112
+ if (keepsAsBackup(opts, previous)) publishFileSync(`${filePath}.bak`, previous, backupWriteOpts(opts));
24113
+ }
24114
+ async function keepBackupAsync(filePath, opts) {
24115
+ let previous;
24116
+ try {
24117
+ previous = await readFileAsync3(filePath);
24118
+ } catch (err) {
24119
+ if (err?.code === "ENOENT") return;
24120
+ throw err;
24121
+ }
24122
+ if (keepsAsBackup(opts, previous)) await publishFileAsync(`${filePath}.bak`, previous, backupWriteOpts(opts));
24123
+ }
24055
24124
  function assertTestSecretWriteSafe(filePath, opts) {
24056
24125
  if (opts.secret !== true || !process.env.NODE_TEST_CONTEXT) return;
24057
- const rel = relative(resolve4(tmpdir2()).toLowerCase(), resolve4(filePath).toLowerCase());
24126
+ const rel = relative(resolve3(tmpdir2()).toLowerCase(), resolve3(filePath).toLowerCase());
24058
24127
  if (rel === "" || !rel.startsWith("..") && !isAbsolute2(rel)) return;
24059
24128
  throw new Error(`refusing to write a secret outside the temp dir under node --test: ${filePath}`);
24060
24129
  }
24061
24130
  function writeFileAtomicSync(filePath, data, opts = {}) {
24062
24131
  assertTestSecretWriteSafe(filePath, opts);
24063
- const run = () => {
24064
- const dir = dirname5(filePath);
24065
- mkdirSync3(dir, { recursive: true });
24066
- const tmp = join5(dir, `.${basename3(filePath)}.${randomBytes3(12).toString("hex")}.tmp`);
24067
- try {
24068
- const writeOpts = {
24069
- encoding: opts.encoding || "utf8",
24070
- flag: "wx",
24071
- mode: opts.mode !== void 0 ? opts.mode : 384
24072
- };
24073
- writeFileSync3(tmp, data, writeOpts);
24074
- if (opts.secret === true) enforceOwnerOnlyAclWin32(tmp, { fresh: true });
24075
- if (opts.fsync !== false) fsyncPathSync(tmp, FILE_FSYNC_TOLERATED);
24076
- if (opts.createOnly === true) {
24077
- try {
24078
- linkSync2(tmp, filePath);
24079
- } catch (err) {
24080
- try {
24081
- unlinkSync3(tmp);
24082
- } catch {
24083
- }
24084
- if (err?.code === "EEXIST") return false;
24085
- throw err;
24086
- }
24132
+ const run = () => publishFileSync(filePath, data, opts);
24133
+ if (opts.lock === true) {
24134
+ return withFileLockSync(`${filePath}.lock`, run, opts);
24135
+ }
24136
+ return run();
24137
+ }
24138
+ function publishTempPath(filePath) {
24139
+ return join3(dirname3(filePath), `${basename3(filePath)}.mixdog-tmp-${randomBytes3(8).toString("hex")}`);
24140
+ }
24141
+ function publishFileSync(filePath, data, opts) {
24142
+ const dir = dirname3(filePath);
24143
+ mkdirSync2(dir, { recursive: true });
24144
+ const tmp = publishTempPath(filePath);
24145
+ try {
24146
+ writeFileDurableSync(tmp, data, { ...opts, flag: "wx" });
24147
+ if (opts.secret === true) enforceOwnerOnlyAclWin32(tmp, { fresh: true });
24148
+ if (opts.backup === true && opts.createOnly !== true) keepBackupSync(filePath, opts);
24149
+ if (opts.createOnly === true) {
24150
+ try {
24151
+ linkSync2(tmp, filePath);
24152
+ } catch (err) {
24087
24153
  try {
24088
24154
  unlinkSync3(tmp);
24089
24155
  } catch {
24090
24156
  }
24091
- } else {
24092
- try {
24093
- renameWithRetrySync(tmp, filePath, opts);
24094
- } catch (err) {
24095
- if (opts.renameFallback === "truncate" && opts.secret !== true && process.platform === "win32" && RETRY_CODES.has(err?.code)) {
24096
- const data2 = readFileSync4(tmp);
24097
- writeFileSync3(filePath, data2, { mode: opts.mode !== void 0 ? opts.mode : 384 });
24098
- try {
24099
- unlinkSync3(tmp);
24100
- } catch {
24101
- }
24102
- } else {
24103
- throw err;
24104
- }
24105
- }
24157
+ if (err?.code === "EEXIST") return false;
24158
+ throw err;
24106
24159
  }
24107
- if (opts.fsyncDir === true) fsyncPathSync(dir, DIR_FSYNC_TOLERATED);
24108
- return true;
24109
- } catch (err) {
24110
24160
  try {
24111
- if (existsSync5(tmp)) unlinkSync3(tmp);
24161
+ unlinkSync3(tmp);
24112
24162
  } catch {
24113
24163
  }
24114
- throw err;
24164
+ } else {
24165
+ try {
24166
+ renameWithRetrySync(tmp, filePath, opts);
24167
+ } catch (err) {
24168
+ if (opts.renameFallback === "truncate" && opts.secret !== true && process.platform === "win32" && RETRY_CODES.has(err?.code)) {
24169
+ writeFileDurableSync(filePath, readFileSync3(tmp), { ...opts, flag: "w", encoding: void 0 });
24170
+ try {
24171
+ unlinkSync3(tmp);
24172
+ } catch {
24173
+ }
24174
+ } else {
24175
+ throw err;
24176
+ }
24177
+ }
24115
24178
  }
24116
- };
24117
- if (opts.lock === true) {
24118
- return withFileLockSync(`${filePath}.lock`, run, opts);
24179
+ if (opts.fsyncDir === true) fsyncPathSync(dir, DIR_FSYNC_TOLERATED);
24180
+ return true;
24181
+ } catch (err) {
24182
+ try {
24183
+ if (existsSync4(tmp)) unlinkSync3(tmp);
24184
+ } catch {
24185
+ }
24186
+ throw err;
24119
24187
  }
24120
- return run();
24121
24188
  }
24122
24189
  function writeJsonAtomicSync(filePath, value, opts = {}) {
24123
24190
  return writeFileAtomicSync(filePath, `${JSON.stringify(value, null, opts.compact ? 0 : 2)}
24124
- `, opts);
24191
+ `, jsonBackupOpts(opts));
24192
+ }
24193
+ function parseJsonText(text) {
24194
+ return JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text);
24195
+ }
24196
+ function isJsonContent(content) {
24197
+ try {
24198
+ parseJsonText(String(content));
24199
+ return true;
24200
+ } catch {
24201
+ return false;
24202
+ }
24203
+ }
24204
+ function readContentOrNullSync(filePath) {
24205
+ try {
24206
+ return readFileSync3(filePath);
24207
+ } catch (err) {
24208
+ if (err?.code === "ENOENT") return null;
24209
+ throw err;
24210
+ }
24211
+ }
24212
+ function reportDamaged(filePath, error, outcome) {
24213
+ const reason = String(error?.message || error).replace(/[\0-\x1f]/g, (c) => `\\x${c.charCodeAt(0).toString(16)}`);
24214
+ process.stderr.write(`[state] ${filePath} is damaged (${reason}); ${outcome}
24215
+ `);
24216
+ }
24217
+ function recoverDamagedSync(filePath, parse, opts) {
24218
+ const current = parse(readContentOrNullSync(filePath));
24219
+ if (current.state !== "damaged") return current;
24220
+ const backup = opts.restoreBackup === false ? NO_BACKUP : parse(readContentOrNullSync(`${filePath}.bak`));
24221
+ if (backup.state !== "ok" && opts.keepWithoutBackup) {
24222
+ reportDamaged(filePath, current.error, KEPT_IN_PLACE);
24223
+ return { state: "absent" };
24224
+ }
24225
+ const aside = quarantinePath(filePath);
24226
+ renameWithRetrySync(filePath, aside, opts);
24227
+ if (backup.state === "ok") publishFileSync(filePath, backup.content, restoreOpts(opts));
24228
+ reportDamaged(filePath, current.error, recoveredOutcome(aside, backup));
24229
+ return backup.state === "ok" ? backup : { state: "absent" };
24230
+ }
24231
+ function readJsonStateSync(filePath, { validate: validate2, decode, fallback = null, ...opts } = {}) {
24232
+ const parse = stateParser(validate2, decode);
24233
+ const first = parse(readContentOrNullSync(filePath));
24234
+ if (first.state === "ok") return first.value;
24235
+ if (first.state !== "damaged") return fallback;
24236
+ let recovered;
24237
+ try {
24238
+ recovered = withFileLockSync(`${filePath}.lock`, () => recoverDamagedSync(filePath, parse, opts), opts);
24239
+ } catch (err) {
24240
+ if (!LOCK_BUSY_CODES.has(err?.code)) throw err;
24241
+ reportDamaged(filePath, first.error, "left in place while another writer holds its lock");
24242
+ return fallback;
24243
+ }
24244
+ return recovered.state === "ok" ? recovered.value : fallback;
24125
24245
  }
24126
24246
  async function writeFileAtomicAsync(filePath, data, opts = {}) {
24127
24247
  assertTestSecretWriteSafe(filePath, opts);
24128
- const run = async () => {
24129
- const dir = dirname5(filePath);
24130
- await mkdirAsync2(dir, { recursive: true });
24131
- const tmp = join5(dir, `.${basename3(filePath)}.${randomBytes3(12).toString("hex")}.tmp`);
24132
- try {
24133
- const writeOpts = {
24134
- encoding: opts.encoding || "utf8",
24135
- flag: "wx",
24136
- mode: opts.mode !== void 0 ? opts.mode : 384
24137
- };
24138
- await writeFileAsync2(tmp, data, writeOpts);
24139
- if (opts.secret === true) await enforceOwnerOnlyAclWin32Async(tmp, { fresh: true });
24140
- if (opts.fsync !== false) await fsyncPathAsync(tmp, FILE_FSYNC_TOLERATED);
24141
- if (opts.createOnly === true) {
24142
- try {
24143
- await linkAsync2(tmp, filePath);
24144
- } catch (err) {
24145
- try {
24146
- await unlinkAsync2(tmp);
24147
- } catch {
24148
- }
24149
- if (err?.code === "EEXIST") return false;
24150
- throw err;
24151
- }
24152
- try {
24153
- await unlinkAsync2(tmp);
24154
- } catch {
24155
- }
24156
- } else {
24157
- await renameWithRetry(tmp, filePath, opts);
24158
- }
24159
- if (opts.fsyncDir === true) await fsyncPathAsync(dir, DIR_FSYNC_TOLERATED);
24160
- return true;
24161
- } catch (err) {
24162
- try {
24163
- await unlinkAsync2(tmp);
24164
- } catch {
24165
- }
24166
- throw err;
24167
- }
24168
- };
24248
+ const run = () => publishFileAsync(filePath, data, opts);
24169
24249
  if (opts.lock !== true) return _runAdaptiveFileIo(run);
24170
24250
  const lockPath = `${filePath}.lock`;
24171
24251
  return withFileLock(
@@ -24189,11 +24269,47 @@ async function writeFileAtomicAsync(filePath, data, opts = {}) {
24189
24269
  opts
24190
24270
  );
24191
24271
  }
24272
+ async function publishFileAsync(filePath, data, opts) {
24273
+ const dir = dirname3(filePath);
24274
+ await mkdirAsync2(dir, { recursive: true });
24275
+ const tmp = publishTempPath(filePath);
24276
+ try {
24277
+ await writeFileDurableAsync(tmp, data, { ...opts, flag: "wx" });
24278
+ if (opts.secret === true) await enforceOwnerOnlyAclWin32Async(tmp, { fresh: true });
24279
+ if (opts.backup === true && opts.createOnly !== true) await keepBackupAsync(filePath, opts);
24280
+ if (opts.createOnly === true) {
24281
+ try {
24282
+ await linkAsync2(tmp, filePath);
24283
+ } catch (err) {
24284
+ try {
24285
+ await unlinkAsync2(tmp);
24286
+ } catch {
24287
+ }
24288
+ if (err?.code === "EEXIST") return false;
24289
+ throw err;
24290
+ }
24291
+ try {
24292
+ await unlinkAsync2(tmp);
24293
+ } catch {
24294
+ }
24295
+ } else {
24296
+ await renameWithRetry(tmp, filePath, opts);
24297
+ }
24298
+ if (opts.fsyncDir === true) await fsyncPathAsync(dir, DIR_FSYNC_TOLERATED);
24299
+ return true;
24300
+ } catch (err) {
24301
+ try {
24302
+ await unlinkAsync2(tmp);
24303
+ } catch {
24304
+ }
24305
+ throw err;
24306
+ }
24307
+ }
24192
24308
  function writeJsonAtomicAsync(filePath, value, opts = {}) {
24193
24309
  return writeFileAtomicAsync(filePath, `${JSON.stringify(value, null, opts.compact ? 0 : 2)}
24194
- `, opts);
24310
+ `, jsonBackupOpts(opts));
24195
24311
  }
24196
- var _ioCeiling, _ioLimit, _ioActive, _ioFastStreak, _ioQueue, RETRY_CODES, FILE_FSYNC_TOLERATED, DIR_FSYNC_TOLERATED;
24312
+ var _ioCeiling, _ioLimit, _ioActive, _ioFastStreak, _ioQueue, RETRY_CODES, FILE_FSYNC_TOLERATED, DIR_FSYNC_TOLERATED, keepsAsBackup, backupWriteOpts, jsonBackupOpts, quarantinePath, LOCK_BUSY_CODES, STATE_UNSUPPORTED, acceptAny, utf8Text, stateParser, restoreOpts, KEPT_IN_PLACE, NO_BACKUP, recoveredOutcome, parseAnyJson;
24197
24313
  var init_atomic_file = __esm({
24198
24314
  "src/runtime/shared/atomic-file.mjs"() {
24199
24315
  init_file_lock();
@@ -24207,16 +24323,68 @@ var init_atomic_file = __esm({
24207
24323
  RETRY_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY", "EEXIST"]);
24208
24324
  FILE_FSYNC_TOLERATED = ["EPERM", "ENOTSUP", "EINVAL"];
24209
24325
  DIR_FSYNC_TOLERATED = ["EPERM", "ENOTSUP", "EINVAL", "EACCES"];
24326
+ keepsAsBackup = (opts, content) => opts.backupAccepts ? opts.backupAccepts(content) : !holdsNoData(content);
24327
+ backupWriteOpts = (opts) => ({ mode: opts.mode, secret: opts.secret, fsync: opts.fsync });
24328
+ jsonBackupOpts = (opts) => opts.backup === true && !opts.backupAccepts ? { ...opts, backupAccepts: isJsonContent } : opts;
24329
+ quarantinePath = (filePath) => {
24330
+ let stamp = Date.now();
24331
+ while (existsSync4(`${filePath}.corrupt-${stamp}`)) stamp += 1;
24332
+ return `${filePath}.corrupt-${stamp}`;
24333
+ };
24334
+ LOCK_BUSY_CODES = /* @__PURE__ */ new Set(["ELOCKCONTENDED", "ELOCKTIMEOUT"]);
24335
+ STATE_UNSUPPORTED = "ESTATEUNSUPPORTED";
24336
+ acceptAny = (value) => value;
24337
+ utf8Text = (content) => content.toString("utf8");
24338
+ stateParser = (validate2 = acceptAny, decode = utf8Text) => (content) => {
24339
+ if (content === null) return { state: "absent" };
24340
+ try {
24341
+ return { state: "ok", value: validate2(parseJsonText(decode(content))), content };
24342
+ } catch (error) {
24343
+ return { state: error?.code === STATE_UNSUPPORTED ? "unsupported" : "damaged", error };
24344
+ }
24345
+ };
24346
+ restoreOpts = (opts) => ({ mode: opts.mode, secret: opts.secret });
24347
+ KEPT_IN_PLACE = "left in place: no good copy to restore";
24348
+ NO_BACKUP = { state: "absent" };
24349
+ recoveredOutcome = (aside, backup) => `kept as ${aside}${backup.state === "ok" ? "; restored the last good copy" : "; starting empty"}`;
24350
+ parseAnyJson = stateParser();
24210
24351
  }
24211
24352
  });
24212
24353
 
24213
24354
  // src/runtime/shared/json-file.mjs
24214
- import { readFileSync as readFileSync5 } from "node:fs";
24355
+ import { readFileSync as readFileSync4 } from "node:fs";
24215
24356
  var init_json_file = __esm({
24216
24357
  "src/runtime/shared/json-file.mjs"() {
24217
24358
  }
24218
24359
  });
24219
24360
 
24361
+ // src/runtime/shared/file-has-data.mjs
24362
+ import { closeSync as closeSync2, openSync as openSync2, readSync } from "node:fs";
24363
+ function fileHasData(path2) {
24364
+ let fd;
24365
+ try {
24366
+ fd = openSync2(path2, "r");
24367
+ const head = Buffer.alloc(PROBE_BYTES);
24368
+ return !holdsNoData(head.subarray(0, readSync(fd, head, 0, PROBE_BYTES, 0)));
24369
+ } catch (err) {
24370
+ return err?.code !== "ENOENT" && err?.code !== "ENOTDIR";
24371
+ } finally {
24372
+ if (fd !== void 0) {
24373
+ try {
24374
+ closeSync2(fd);
24375
+ } catch {
24376
+ }
24377
+ }
24378
+ }
24379
+ }
24380
+ var PROBE_BYTES;
24381
+ var init_file_has_data = __esm({
24382
+ "src/runtime/shared/file-has-data.mjs"() {
24383
+ init_atomic_file();
24384
+ PROBE_BYTES = 4096;
24385
+ }
24386
+ });
24387
+
24220
24388
  // src/runtime/shared/bounded-download.mjs
24221
24389
  import { createWriteStream, rmSync as rmSync2 } from "node:fs";
24222
24390
  import { Transform as Transform2 } from "node:stream";
@@ -24241,7 +24409,7 @@ function responseByteLength(response, maximum, label) {
24241
24409
  }
24242
24410
  return advertised;
24243
24411
  }
24244
- async function streamResponseToFile(response, destPath, { maxBytes, expectedBytes = 0, label = "download", onProgress = null } = {}) {
24412
+ async function streamResponseToFile(response, destPath, { maxBytes, expectedBytes = 0, label = "download", onProgress = null, signal = void 0 } = {}) {
24245
24413
  const { maximum, expected } = downloadBounds(maxBytes, expectedBytes, label);
24246
24414
  const advertised = responseByteLength(response, maximum, label);
24247
24415
  if (expected && advertised && advertised !== expected) {
@@ -24277,7 +24445,7 @@ async function streamResponseToFile(response, destPath, { maxBytes, expectedByte
24277
24445
  }
24278
24446
  });
24279
24447
  try {
24280
- await pipeline(response.body, guard, createWriteStream(destPath));
24448
+ await pipeline(response.body, guard, createWriteStream(destPath, { flush: true }), { signal });
24281
24449
  return downloaded;
24282
24450
  } catch (error) {
24283
24451
  try {
@@ -24298,13 +24466,13 @@ var init_bounded_download = __esm({
24298
24466
  // src/runtime/shared/native-asset.mjs
24299
24467
  import { createHash as createHash5 } from "node:crypto";
24300
24468
  import {
24301
- chmodSync as chmodSync2,
24469
+ chmodSync,
24302
24470
  createReadStream,
24303
- existsSync as existsSync12,
24471
+ existsSync as existsSync11,
24304
24472
  mkdirSync as mkdirSync6,
24305
- readFileSync as readFileSync13,
24306
- readdirSync as readdirSync3,
24307
- renameSync as renameSync4,
24473
+ readFileSync as readFileSync12,
24474
+ readdirSync as readdirSync4,
24475
+ renameSync as renameSync3,
24308
24476
  rmSync as rmSync3
24309
24477
  } from "node:fs";
24310
24478
  import { join as join13 } from "node:path";
@@ -24368,21 +24536,21 @@ __export(voice_runtime_fetcher_exports, {
24368
24536
  });
24369
24537
  import { createHash as createHash6 } from "node:crypto";
24370
24538
  import {
24371
- chmodSync as chmodSync3,
24372
- closeSync as closeSync2,
24539
+ chmodSync as chmodSync2,
24540
+ closeSync as closeSync3,
24373
24541
  createReadStream as createReadStream2,
24374
24542
  createWriteStream as createWriteStream2,
24375
- existsSync as existsSync13,
24543
+ existsSync as existsSync12,
24376
24544
  mkdirSync as mkdirSync7,
24377
- openSync as openSync2,
24378
- readFileSync as readFileSync14,
24379
- readdirSync as readdirSync4,
24545
+ openSync as openSync3,
24546
+ readFileSync as readFileSync13,
24547
+ readdirSync as readdirSync5,
24380
24548
  rmSync as rmSync4,
24381
- statSync as statSync5,
24382
- writeFileSync as writeFileSync5
24549
+ statSync as statSync6,
24550
+ writeFileSync as writeFileSync3
24383
24551
  } from "node:fs";
24384
24552
  import { setTimeout as sleep2 } from "node:timers/promises";
24385
- import { join as join14, dirname as dirname11 } from "node:path";
24553
+ import { join as join14, dirname as dirname10 } from "node:path";
24386
24554
  import { fileURLToPath as fileURLToPath4 } from "node:url";
24387
24555
  import { pipeline as pipeline2 } from "node:stream/promises";
24388
24556
  import { spawnSync } from "node:child_process";
@@ -24400,7 +24568,7 @@ function lockAgeMs(holderTs, waitSince) {
24400
24568
  }
24401
24569
  function _readInstallLockToken(lockPath) {
24402
24570
  try {
24403
- const raw = readFileSync14(lockPath, "utf8").trim();
24571
+ const raw = readFileSync13(lockPath, "utf8").trim();
24404
24572
  if (!raw) return null;
24405
24573
  const [pidLine, tsLine = ""] = raw.split(/\r?\n/);
24406
24574
  const pid = Number(pidLine);
@@ -24418,7 +24586,7 @@ function _installLockTokenMatches(lockPath, expectedPid, expectedTs, expectedTok
24418
24586
  }
24419
24587
  function chmodExtractedBinariesPosix(rootDir, executable) {
24420
24588
  if (process.platform === "win32") return;
24421
- const execDir = join14(rootDir, dirname11(executable));
24589
+ const execDir = join14(rootDir, dirname10(executable));
24422
24590
  const targets = /* @__PURE__ */ new Set([
24423
24591
  join14(rootDir, executable),
24424
24592
  join14(execDir, "whisper-cli"),
@@ -24426,9 +24594,9 @@ function chmodExtractedBinariesPosix(rootDir, executable) {
24426
24594
  join14(execDir, "ffmpeg")
24427
24595
  ]);
24428
24596
  for (const p of targets) {
24429
- if (existsSync13(p)) {
24597
+ if (existsSync12(p)) {
24430
24598
  try {
24431
- chmodSync3(p, 493);
24599
+ chmodSync2(p, 493);
24432
24600
  } catch {
24433
24601
  }
24434
24602
  }
@@ -24450,9 +24618,9 @@ async function _withInstallLock(rootDir, lockName, fn, { pollMs = 250 } = {}) {
24450
24618
  };
24451
24619
  while (true) {
24452
24620
  try {
24453
- fd = openSync2(lockPath, "wx");
24621
+ fd = openSync3(lockPath, "wx");
24454
24622
  try {
24455
- writeFileSync5(lockPath, `${process.pid}
24623
+ writeFileSync3(lockPath, `${process.pid}
24456
24624
  ${Date.now()}`);
24457
24625
  } catch {
24458
24626
  }
@@ -24505,7 +24673,7 @@ ${Date.now()}`);
24505
24673
  if (released) return;
24506
24674
  released = true;
24507
24675
  try {
24508
- if (fd != null) closeSync2(fd);
24676
+ if (fd != null) closeSync3(fd);
24509
24677
  } catch {
24510
24678
  }
24511
24679
  try {
@@ -24522,21 +24690,21 @@ ${Date.now()}`);
24522
24690
  }
24523
24691
  }
24524
24692
  function readBundledManifest() {
24525
- return JSON.parse(readFileSync14(BUNDLED_MANIFEST_PATH, "utf8"));
24693
+ return JSON.parse(readFileSync13(BUNDLED_MANIFEST_PATH, "utf8"));
24526
24694
  }
24527
24695
  function readActiveName(rootDir) {
24528
24696
  const activeFile = join14(rootDir, "active-version");
24529
- if (!existsSync13(activeFile)) return "";
24530
- return readFileSync14(activeFile, "utf8").trim();
24697
+ if (!existsSync12(activeFile)) return "";
24698
+ return readFileSync13(activeFile, "utf8").trim();
24531
24699
  }
24532
24700
  async function loadManifest(dataDir2) {
24533
- if (existsSync13(BUNDLED_MANIFEST_PATH)) {
24701
+ if (existsSync12(BUNDLED_MANIFEST_PATH)) {
24534
24702
  return readBundledManifest();
24535
24703
  }
24536
24704
  const cachedPath = join14(dataDir2, "voice-runtime", "manifest.json");
24537
- if (existsSync13(cachedPath)) {
24705
+ if (existsSync12(cachedPath)) {
24538
24706
  try {
24539
- return JSON.parse(readFileSync14(cachedPath, "utf8"));
24707
+ return JSON.parse(readFileSync13(cachedPath, "utf8"));
24540
24708
  } catch {
24541
24709
  }
24542
24710
  }
@@ -24544,7 +24712,7 @@ async function loadManifest(dataDir2) {
24544
24712
  if (!res.ok) throw new Error(`[voice-runtime] manifest fetch failed: ${res.status} ${res.statusText}`);
24545
24713
  const manifest = await res.json();
24546
24714
  mkdirSync7(join14(dataDir2, "voice-runtime"), { recursive: true });
24547
- writeFileSync5(cachedPath, JSON.stringify(manifest, null, 2));
24715
+ writeJsonAtomicSync(cachedPath, manifest);
24548
24716
  return manifest;
24549
24717
  }
24550
24718
  function verifySha256(filePath, expected) {
@@ -24560,18 +24728,18 @@ function detectCudaMajorsWin32() {
24560
24728
  }
24561
24729
  for (const root of windowsProgramRoots()) {
24562
24730
  const standardRoot = join14(root, "NVIDIA GPU Computing Toolkit", "CUDA");
24563
- if (!existsSync13(standardRoot)) continue;
24731
+ if (!existsSync12(standardRoot)) continue;
24564
24732
  try {
24565
- for (const e of readdirSync4(standardRoot)) {
24733
+ for (const e of readdirSync5(standardRoot)) {
24566
24734
  if (/^v\d+\.\d+/.test(e)) dirs.push(join14(standardRoot, e, "bin"));
24567
24735
  }
24568
24736
  } catch {
24569
24737
  }
24570
24738
  }
24571
24739
  for (const d of dirs) {
24572
- if (!existsSync13(d)) continue;
24740
+ if (!existsSync12(d)) continue;
24573
24741
  try {
24574
- for (const f of readdirSync4(d)) {
24742
+ for (const f of readdirSync5(d)) {
24575
24743
  const m = /^cublas64_(\d+)\.dll$/i.exec(f);
24576
24744
  if (m) found.add(Number(m[1]));
24577
24745
  }
@@ -24591,9 +24759,9 @@ function detectCudaMajorsLinux() {
24591
24759
  const cudaPath = process.env.CUDA_PATH;
24592
24760
  if (cudaPath) dirs.push(`${cudaPath}/lib64`);
24593
24761
  for (const d of dirs) {
24594
- if (!existsSync13(d)) continue;
24762
+ if (!existsSync12(d)) continue;
24595
24763
  try {
24596
- for (const f of readdirSync4(d)) {
24764
+ for (const f of readdirSync5(d)) {
24597
24765
  const m = /^libcublas\.so\.(\d+)$/.exec(f);
24598
24766
  if (m) found.add(Number(m[1]));
24599
24767
  }
@@ -24614,7 +24782,7 @@ function hasNvidiaDriver() {
24614
24782
  const sys = windowsSystemRoot();
24615
24783
  if (!sys) return false;
24616
24784
  for (const f of ["System32\\nvidia-smi.exe", "System32\\nvml.dll"]) {
24617
- if (existsSync13(join14(sys, f))) return true;
24785
+ if (existsSync12(join14(sys, f))) return true;
24618
24786
  }
24619
24787
  return false;
24620
24788
  }
@@ -24652,10 +24820,10 @@ async function processExtras(extras, stagingDir, onProgress) {
24652
24820
  for (const f of extra.files) {
24653
24821
  const src = join14(extractDir, f.from);
24654
24822
  const dst = join14(stagingDir, f.to);
24655
- if (!existsSync13(src)) {
24823
+ if (!existsSync12(src)) {
24656
24824
  throw new Error(`[voice-runtime] extra ${tag}: expected file ${f.from} not present after extract`);
24657
24825
  }
24658
- mkdirSync7(dirname11(dst), { recursive: true });
24826
+ mkdirSync7(dirname10(dst), { recursive: true });
24659
24827
  renameWithRetrySync(src, dst);
24660
24828
  }
24661
24829
  try {
@@ -24669,8 +24837,8 @@ async function processExtras(extras, stagingDir, onProgress) {
24669
24837
  }
24670
24838
  }
24671
24839
  function gcStaleVersions(rootDir, activeName, prefix) {
24672
- if (!existsSync13(rootDir)) return;
24673
- for (const entry of readdirSync4(rootDir)) {
24840
+ if (!existsSync12(rootDir)) return;
24841
+ for (const entry of readdirSync5(rootDir)) {
24674
24842
  if (entry === activeName || entry === "active-version" || entry === "manifest.json") continue;
24675
24843
  if (!entry.startsWith(prefix) && !entry.startsWith("staging-")) continue;
24676
24844
  try {
@@ -24682,18 +24850,18 @@ function gcStaleVersions(rootDir, activeName, prefix) {
24682
24850
  function gcPublishedRuntimeSiblings(rootDir, prefix) {
24683
24851
  try {
24684
24852
  const activeName = readActiveName(rootDir);
24685
- if (!activeName.startsWith(prefix) || !existsSync13(join14(rootDir, activeName))) return;
24853
+ if (!activeName.startsWith(prefix) || !existsSync12(join14(rootDir, activeName))) return;
24686
24854
  gcStaleVersions(rootDir, activeName, prefix);
24687
24855
  } catch {
24688
24856
  }
24689
24857
  }
24690
24858
  async function extractGz(gzPath, destPath) {
24691
- await pipeline2(createReadStream2(gzPath), createGunzip(), createWriteStream2(destPath));
24859
+ await pipeline2(createReadStream2(gzPath), createGunzip(), createWriteStream2(destPath, { flush: true }));
24692
24860
  }
24693
24861
  function gcStagingPartials(dir) {
24694
- if (!existsSync13(dir)) return;
24862
+ if (!existsSync12(dir)) return;
24695
24863
  try {
24696
- for (const entry of readdirSync4(dir)) {
24864
+ for (const entry of readdirSync5(dir)) {
24697
24865
  if (!entry.startsWith(".staging-")) continue;
24698
24866
  try {
24699
24867
  rmSync4(join14(dir, entry), { force: true });
@@ -24771,12 +24939,12 @@ async function ensureWhisperRuntime(dataDir2, onProgress = null) {
24771
24939
  const activeName = `whisper-${ver}-${variant.id}`;
24772
24940
  const activeDir = join14(rootDir, activeName);
24773
24941
  const whisperCmd = join14(activeDir, variant.executable);
24774
- if (existsSync13(whisperCmd)) {
24942
+ if (fileHasData(whisperCmd)) {
24775
24943
  gcStaleVersions(rootDir, activeName, "whisper-");
24776
24944
  return { whisperCmd, version: ver, variantId: variant.id };
24777
24945
  }
24778
24946
  return _withInstallLock(rootDir, "install", async () => {
24779
- if (existsSync13(whisperCmd)) {
24947
+ if (fileHasData(whisperCmd)) {
24780
24948
  gcStaleVersions(rootDir, activeName, "whisper-");
24781
24949
  return { whisperCmd, version: ver, variantId: variant.id };
24782
24950
  }
@@ -24800,7 +24968,7 @@ async function ensureWhisperRuntime(dataDir2, onProgress = null) {
24800
24968
  rmSync4(archivePath, { force: true });
24801
24969
  chmodExtractedBinariesPosix(stagingDir, variant.executable);
24802
24970
  const stagedExec = join14(stagingDir, variant.executable);
24803
- if (!existsSync13(stagedExec)) {
24971
+ if (!existsSync12(stagedExec)) {
24804
24972
  throw new Error(`[voice-runtime] expected executable ${variant.executable} not present after extract`);
24805
24973
  }
24806
24974
  await processExtras(variant.extras, stagingDir, onProgress);
@@ -24822,7 +24990,7 @@ async function ensureWhisperRuntime(dataDir2, onProgress = null) {
24822
24990
  function isManagedWhisperStale(dataDir2) {
24823
24991
  const activeName = readActiveName(join14(dataDir2, "voice-runtime"));
24824
24992
  if (!activeName) return false;
24825
- if (!existsSync13(BUNDLED_MANIFEST_PATH)) return false;
24993
+ if (!existsSync12(BUNDLED_MANIFEST_PATH)) return false;
24826
24994
  try {
24827
24995
  const manifest = readBundledManifest();
24828
24996
  if (!manifest?.version) return false;
@@ -24835,7 +25003,7 @@ function resolveManagedWhisperCmd(dataDir2) {
24835
25003
  const activeName = readActiveName(join14(dataDir2, "voice-runtime"));
24836
25004
  if (!activeName) return null;
24837
25005
  const activeDir = join14(dataDir2, "voice-runtime", activeName);
24838
- if (existsSync13(BUNDLED_MANIFEST_PATH)) {
25006
+ if (existsSync12(BUNDLED_MANIFEST_PATH)) {
24839
25007
  try {
24840
25008
  const manifest = readBundledManifest();
24841
25009
  const key = platformEntryKey(manifest.platforms);
@@ -24846,7 +25014,7 @@ function resolveManagedWhisperCmd(dataDir2) {
24846
25014
  const variant = variants.find((v) => v.id === variantId);
24847
25015
  if (variant?.executable) {
24848
25016
  const p = join14(activeDir, variant.executable);
24849
- if (existsSync13(p)) return p;
25017
+ if (fileHasData(p)) return p;
24850
25018
  }
24851
25019
  }
24852
25020
  } catch {
@@ -24854,17 +25022,17 @@ function resolveManagedWhisperCmd(dataDir2) {
24854
25022
  }
24855
25023
  for (const c of ["Release/whisper-cli.exe", "Release/whisper-cli"]) {
24856
25024
  const p = join14(activeDir, c);
24857
- if (existsSync13(p)) return p;
25025
+ if (fileHasData(p)) return p;
24858
25026
  }
24859
25027
  return null;
24860
25028
  }
24861
25029
  function resolveManagedWhisperModelById(dataDir2, modelId = "standard") {
24862
- if (!existsSync13(BUNDLED_MANIFEST_PATH)) return null;
25030
+ if (!existsSync12(BUNDLED_MANIFEST_PATH)) return null;
24863
25031
  const entry = manifestModelEntry(readBundledManifest(), modelId);
24864
25032
  if (!entry?.filename) return null;
24865
25033
  const modelDir = join14(dataDir2, "voice", "models");
24866
25034
  const p = join14(modelDir, entry.filename);
24867
- return existsSync13(p) ? p : null;
25035
+ return fileHasData(p) ? p : null;
24868
25036
  }
24869
25037
  function gcUnknownModelFiles(manifest, modelDir) {
24870
25038
  const keep = /* @__PURE__ */ new Set();
@@ -24874,7 +25042,7 @@ function gcUnknownModelFiles(manifest, modelDir) {
24874
25042
  if (keep.size === 0) return;
24875
25043
  let names = [];
24876
25044
  try {
24877
- names = readdirSync4(modelDir);
25045
+ names = readdirSync5(modelDir);
24878
25046
  } catch {
24879
25047
  return;
24880
25048
  }
@@ -24903,12 +25071,12 @@ async function ensureFfmpegRuntime(dataDir2, onProgress = null) {
24903
25071
  const activeName = `ffmpeg-${ver}`;
24904
25072
  const activeDir = join14(rootDir, activeName);
24905
25073
  const ffmpegPath = join14(activeDir, platformEntry.executable);
24906
- if (existsSync13(ffmpegPath)) {
25074
+ if (fileHasData(ffmpegPath)) {
24907
25075
  gcStaleVersions(rootDir, activeName, "ffmpeg-");
24908
25076
  return { ffmpegPath, version: ver };
24909
25077
  }
24910
25078
  return _withInstallLock(rootDir, "install", async () => {
24911
- if (existsSync13(ffmpegPath)) {
25079
+ if (fileHasData(ffmpegPath)) {
24912
25080
  gcStaleVersions(rootDir, activeName, "ffmpeg-");
24913
25081
  return { ffmpegPath, version: ver };
24914
25082
  }
@@ -24937,13 +25105,13 @@ async function ensureFfmpegRuntime(dataDir2, onProgress = null) {
24937
25105
  throw new Error(`[voice-runtime] ffmpeg manifest format must be "gz" or "zip", got "${platformEntry.format}"`);
24938
25106
  }
24939
25107
  rmSync4(archivePath, { force: true });
24940
- if (!existsSync13(stagedExec)) {
25108
+ if (!existsSync12(stagedExec)) {
24941
25109
  throw new Error(
24942
25110
  `[voice-runtime] expected ffmpeg executable ${platformEntry.executable} not present after extract`
24943
25111
  );
24944
25112
  }
24945
25113
  if (process.platform !== "win32") {
24946
- chmodSync3(stagedExec, 493);
25114
+ chmodSync2(stagedExec, 493);
24947
25115
  }
24948
25116
  renameWithRetrySync(stagingDir, activeDir);
24949
25117
  writeFileAtomicSync(join14(rootDir, "active-version"), activeName, { fsyncDir: true });
@@ -24964,21 +25132,21 @@ function resolveManagedFfmpegPath(dataDir2) {
24964
25132
  const activeName = readActiveName(join14(dataDir2, "ffmpeg-runtime"));
24965
25133
  if (!activeName) return null;
24966
25134
  const activeDir = join14(dataDir2, "ffmpeg-runtime", activeName);
24967
- if (existsSync13(BUNDLED_MANIFEST_PATH)) {
25135
+ if (existsSync12(BUNDLED_MANIFEST_PATH)) {
24968
25136
  try {
24969
25137
  const manifest = readBundledManifest();
24970
25138
  const key = platformEntryKey(manifest.ffmpeg?.platforms);
24971
25139
  const platformEntry = manifest.ffmpeg?.platforms?.[key];
24972
25140
  if (platformEntry?.executable) {
24973
25141
  const p = join14(activeDir, platformEntry.executable);
24974
- if (existsSync13(p)) return p;
25142
+ if (fileHasData(p)) return p;
24975
25143
  }
24976
25144
  } catch {
24977
25145
  }
24978
25146
  }
24979
25147
  for (const c of ["ffmpeg.exe", "ffmpeg"]) {
24980
25148
  const p = join14(activeDir, c);
24981
- if (existsSync13(p)) return p;
25149
+ if (fileHasData(p)) return p;
24982
25150
  }
24983
25151
  return null;
24984
25152
  }
@@ -24993,7 +25161,7 @@ function voiceRuntimeInfo(runtime) {
24993
25161
  let modelBytes = model?.size || 0;
24994
25162
  if (runtime.modelPath) {
24995
25163
  try {
24996
- modelBytes = statSync5(runtime.modelPath).size;
25164
+ modelBytes = statSync6(runtime.modelPath).size;
24997
25165
  } catch {
24998
25166
  modelBytes = 0;
24999
25167
  }
@@ -25016,8 +25184,8 @@ function resolveVoiceRuntime(dataDir2, { modelId = "standard", cleanup = true }
25016
25184
  const managedModelPath = resolveManagedWhisperModelById(dataDir2, modelId);
25017
25185
  const managedFfmpegPath = resolveManagedFfmpegPath(dataDir2);
25018
25186
  const ext = process.platform === "win32" ? ".exe" : "";
25019
- const managedServerCmd = managedWhisperCmd ? join14(dirname11(managedWhisperCmd), `whisper-server${ext}`) : null;
25020
- const serverCmd = managedServerCmd && existsSync13(managedServerCmd) ? managedServerCmd : null;
25187
+ const managedServerCmd = managedWhisperCmd ? join14(dirname10(managedWhisperCmd), `whisper-server${ext}`) : null;
25188
+ const serverCmd = managedServerCmd && fileHasData(managedServerCmd) ? managedServerCmd : null;
25021
25189
  return {
25022
25190
  kind: "managed-whisper.cpp",
25023
25191
  label: "whisper.cpp",
@@ -25044,7 +25212,7 @@ async function ensureWhisperModel(dataDir2, onProgress = null, modelId = "standa
25044
25212
  }
25045
25213
  const modelDir = join14(dataDir2, "voice", "models");
25046
25214
  const modelPath = join14(modelDir, model.filename);
25047
- if (existsSync13(modelPath)) {
25215
+ if (existsSync12(modelPath)) {
25048
25216
  const actual = await sha256File(modelPath);
25049
25217
  if (actual === model.sha256) {
25050
25218
  return { modelPath, modelId: model.id, size: model.size };
@@ -25059,7 +25227,7 @@ async function ensureWhisperModel(dataDir2, onProgress = null, modelId = "standa
25059
25227
  }
25060
25228
  }
25061
25229
  return _withInstallLock(modelDir, "install", async () => {
25062
- if (existsSync13(modelPath)) {
25230
+ if (existsSync12(modelPath)) {
25063
25231
  const actual = await sha256File(modelPath);
25064
25232
  if (actual === model.sha256) {
25065
25233
  gcUnknownModelFiles(manifest, modelDir);
@@ -25101,6 +25269,7 @@ var BUNDLED_MANIFEST_PATH, MANIFEST_URL, LOCK_WAIT_CODES2, LOCK_MAX_AGE_MS, Voic
25101
25269
  var init_voice_runtime_fetcher = __esm({
25102
25270
  "src/runtime/channels/lib/voice-runtime-fetcher.mjs"() {
25103
25271
  init_atomic_file();
25272
+ init_file_has_data();
25104
25273
  init_bounded_download();
25105
25274
  init_native_asset();
25106
25275
  init_windows_roots();
@@ -35336,9 +35505,9 @@ import { useState as useState4, useEffect as useEffect5 } from "react";
35336
35505
  import { useState as useState5, useEffect as useEffect6, useCallback as useCallback4, useMemo as useMemo4 } from "react";
35337
35506
 
35338
35507
  // src/tui/index.jsx
35339
- import { closeSync as closeSync3, constants as fsConstants, createWriteStream as createWriteStream3, mkdirSync as mkdirSync8, openSync as openSync3, readSync } from "node:fs";
35508
+ import { closeSync as closeSync4, constants as fsConstants, createWriteStream as createWriteStream3, mkdirSync as mkdirSync8, openSync as openSync4, readSync as readSync2 } from "node:fs";
35340
35509
  import { tmpdir as tmpdir4 } from "node:os";
35341
- import { dirname as dirname12, join as join17 } from "node:path";
35510
+ import { dirname as dirname11, join as join17 } from "node:path";
35342
35511
  import { performance as performance2 } from "node:perf_hooks";
35343
35512
  import { format } from "node:util";
35344
35513
 
@@ -35664,7 +35833,7 @@ var SLASH_COMMANDS = [
35664
35833
  {
35665
35834
  name: "autoclear",
35666
35835
  usage: "/autoclear",
35667
- params: "[on|off|duration]",
35836
+ params: "[on|off|duration|unlimited]",
35668
35837
  description: "Reduce cache-miss cost after long idle gaps"
35669
35838
  },
35670
35839
  { name: "resume", usage: "/resume", params: "[id]", description: "Resume a saved chat" },
@@ -41360,6 +41529,14 @@ function gitStageUnit(a) {
41360
41529
  effect: true
41361
41530
  });
41362
41531
  }
41532
+ function gitWorktreeUnit(a) {
41533
+ return unitDescriptor("Git", {
41534
+ active: "Managing",
41535
+ done: "Managed",
41536
+ noun: "worktree",
41537
+ effect: String(a.worktree_action || "").toLowerCase() !== "list"
41538
+ });
41539
+ }
41363
41540
  function cwdUnit(a) {
41364
41541
  const action = String(a.action || a.type || "").toLowerCase();
41365
41542
  if (action === "set" || action === "set-project" || !action && a.path) {
@@ -41390,14 +41567,6 @@ function cwdUnit(a) {
41390
41567
  const [active, done] = words;
41391
41568
  return unitDescriptor("Setup", { active, done, noun: "session", effect: true });
41392
41569
  }
41393
- if (action.endsWith("-worktree") || action.endsWith("-worktrees")) {
41394
- return unitDescriptor("Setup", {
41395
- active: "Managing",
41396
- done: "Managed",
41397
- noun: "worktree",
41398
- effect: !action.startsWith("list")
41399
- });
41400
- }
41401
41570
  if (action === "status") {
41402
41571
  return unitDescriptor("Setup", { active: "Checking", done: "Checked", noun: "session" });
41403
41572
  }
@@ -41492,7 +41661,7 @@ var TOOL_UNITS = /* @__PURE__ */ new Map([
41492
41661
  ["job_wait", shellUnit],
41493
41662
  [
41494
41663
  "git",
41495
- (a) => a.action === "stage" ? gitStageUnit(a) : unitDescriptor("Git", { count: queryCount(a, "command", "commands") || 1, noun: "Git command" })
41664
+ (a) => a.action === "stage" ? gitStageUnit(a) : a.action === "worktree" ? gitWorktreeUnit(a) : unitDescriptor("Git", { count: queryCount(a, "command", "commands") || 1, noun: "Git command" })
41496
41665
  ],
41497
41666
  ["github", githubUnit],
41498
41667
  // Preserve the staging work unit when rendering historical transcripts.
@@ -41995,7 +42164,8 @@ function pathOrId(path2, id, max) {
41995
42164
  }
41996
42165
 
41997
42166
  // src/tui/transcript-tool-failures.mjs
41998
- var HOOK_DENIAL_RE = /^Error:\s*tool\s*"[^"]*"\s*denied by hook\b/im;
42167
+ var HOOK_DENIAL_RE = /^Error:\s*tool\s*"[^"]*"\s*(?:denied by hook|was not approved)\b/im;
42168
+ var NOT_APPROVED_INSTRUCTION_RE = /\s*Do not retry it or reach the same result another way; report it\.\s*$/;
41999
42169
  var APPROVAL_UNAVAILABLE_RE = /denied by hook:\s*approval required but no approval UI is available/i;
42000
42170
  function toolItemResultText(item) {
42001
42171
  if (!item) return "";
@@ -42019,7 +42189,7 @@ function isHookApprovalDenialToolItem(item) {
42019
42189
  function formatHookDenialDetail(text) {
42020
42190
  const raw = String(text ?? "").trim();
42021
42191
  if (!raw) return "";
42022
- const stripped = raw.replace(/^Error:\s*tool\s*"[^"]*"\s*/i, "").trim();
42192
+ const stripped = raw.replace(/^Error:\s*tool\s*"[^"]*"\s*/i, "").replace(NOT_APPROVED_INSTRUCTION_RE, "").trim();
42023
42193
  return stripped || raw;
42024
42194
  }
42025
42195
  function isFullyFailedToolBatch(item) {
@@ -42759,6 +42929,7 @@ var DETERMINISTIC_KINDS = /* @__PURE__ */ new Set([
42759
42929
  // src/runtime/shared/error-presentation.mjs
42760
42930
  var DETAILS_LIMIT = 12e3;
42761
42931
  var SUMMARY_LIMIT = 180;
42932
+ var LATEST_TURN_RE = /The latest message cannot fit this model \(LATEST_TURN_EXCEEDS_CONTEXT\)(: it needs about [\d,]+ tokens, but the [^.]* window leaves [\d,]+ for it\.)([\s\S]*)$/;
42762
42933
  function safeErrorDetails(value) {
42763
42934
  let text = String(value ?? "").replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\r\n?/g, "\n").replace(/\b((?:https?|ssh):\/\/)[^/\s@]+@/gi, "$1[redacted]@").replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "$1 [redacted]").replace(/([?&](?:token|access_token|refresh_token|api_key|key|code|signature|sig)=)[^&\s"'<>]+/gi, "$1[redacted]").replace(
42764
42935
  /(["']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|authorization|cookie|password|secret|client_secret)["']?\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;}]+)/gi,
@@ -42951,11 +43122,15 @@ function describeError(value) {
42951
43122
  let recoveryTemplate = "";
42952
43123
  let params = null;
42953
43124
  const transport = transportErrorText(value) || typed && transportErrorText({ code: code.toUpperCase(), status }) || (!typed || descriptor.kind === "transient" ? transportErrorText(raw) : null);
42954
- if (value?.reauthRequired === true || !typed && /invalid_grant|refresh token (?:expired|revoked|not available)|sign in again|sign-in expired|Reauth Required/i.test(raw)) {
43125
+ if (value?.reauthRequired === true || !typed && /invalid_grant|refresh token (?:expired|revoked|not available)|sign in again|sign-in expired|Reauth Required/i.test(
43126
+ raw
43127
+ )) {
42955
43128
  kind = "reauth";
42956
43129
  summary = "Sign-in expired.";
42957
43130
  recovery = "Sign in again from Providers.";
42958
- } else if (descriptor.kind === "auth" || descriptor.kind === "unknown" && /\b(?:invalid|expired)\s+(?:api[ _-]?key|access token|credentials?)\b|Provider authentication failed/i.test(clean3)) {
43131
+ } else if (descriptor.kind === "auth" || descriptor.kind === "unknown" && /\b(?:invalid|expired)\s+(?:api[ _-]?key|access token|credentials?)\b|Provider authentication failed/i.test(
43132
+ clean3
43133
+ )) {
42959
43134
  kind = "authentication";
42960
43135
  summary = "Provider authentication failed";
42961
43136
  recovery = "Check your sign-in or API key, then try again.";
@@ -42977,6 +43152,11 @@ function describeError(value) {
42977
43152
  kind = "not-found";
42978
43153
  summary = firstReadableLine(clean3) || NOT_FOUND_SUMMARY;
42979
43154
  recovery = MODEL_UNAVAILABLE_COPY[2];
43155
+ } else if (LATEST_TURN_RE.test(clean3)) {
43156
+ const [, headline, rest] = LATEST_TURN_RE.exec(clean3);
43157
+ kind = "latest-turn-size";
43158
+ summary = `The latest message cannot fit this model${headline}`;
43159
+ recovery = rest.trim();
42980
43160
  } else if (descriptor.kind === "unknown" && /payload too large|request (?:body )?too large|The request exceeds the size limit/i.test(clean3)) {
42981
43161
  [kind, summary, recovery] = KIND_COPY.request_too_large;
42982
43162
  } else if (copy) {
@@ -43003,6 +43183,7 @@ function describeError(value) {
43003
43183
  }
43004
43184
  summary = summary.replace(/\s+/g, " ").trim() || "Something went wrong.";
43005
43185
  if (summary.length > SUMMARY_LIMIT) summary = `${summary.slice(0, SUMMARY_LIMIT - 1).trimEnd()}\u2026`;
43186
+ const fingerprintStatus = kind === "reauth" ? "" : status || "";
43006
43187
  return {
43007
43188
  kind,
43008
43189
  summary,
@@ -43015,7 +43196,7 @@ function describeError(value) {
43015
43196
  details,
43016
43197
  // Known structured failures ignore volatile indexes; unknown failures
43017
43198
  // retain their diagnostic identity rather than merging unrelated work.
43018
- fingerprint: kind === "unknown" ? details : `${kind}:${kind === "reauth" ? "" : status || ""}`
43199
+ fingerprint: kind === "unknown" ? details : `${kind}:${fingerprintStatus}`
43019
43200
  };
43020
43201
  }
43021
43202
 
@@ -43228,6 +43409,8 @@ function presentErrorText(error, options = {}) {
43228
43409
  if (code === "TOOL_SURFACE_EXCEEDS_CONTEXT" || /\(TOOL_SURFACE_EXCEEDS_CONTEXT\)/.test(text)) {
43229
43410
  return stripErrorPrefix(text);
43230
43411
  }
43412
+ const latestTurn = /The latest message cannot fit this model \(LATEST_TURN_EXCEEDS_CONTEXT\)[\s\S]*$/.exec(text);
43413
+ if (latestTurn) return latestTurn[0].replace(" (LATEST_TURN_EXCEEDS_CONTEXT)", "");
43231
43414
  const jsonError = extractJsonError(text);
43232
43415
  if (jsonError) text = jsonError;
43233
43416
  const embeddedError = extractEmbeddedError(text);
@@ -47359,17 +47542,14 @@ async function resizeImageBuffer(buffer, ext, { maxTokens = DEFAULT_IMAGE_MAX_TO
47359
47542
  var TOOL_IMAGE_TARGET_BASE64_SIZE = 256 * 1024;
47360
47543
 
47361
47544
  // src/tui/paste-text-policy.mjs
47362
- var PASTE_TOKEN_MIN_LINES = 10;
47363
- var PASTE_TOKEN_MIN_CHARS = 200;
47545
+ var PASTE_TOKEN_MIN_CHARS = 1e3;
47364
47546
  function pastedTextLineCount(text) {
47365
47547
  const value = String(text ?? "").replace(/\r\n?/g, "\n").replace(/\n+$/, "");
47366
47548
  if (!value) return 0;
47367
47549
  return value.split("\n").length;
47368
47550
  }
47369
47551
  function shouldFoldPastedText(text) {
47370
- const value = String(text ?? "");
47371
- if (!value) return false;
47372
- return pastedTextLineCount(value) >= PASTE_TOKEN_MIN_LINES || value.length > PASTE_TOKEN_MIN_CHARS;
47552
+ return String(text ?? "").length > PASTE_TOKEN_MIN_CHARS;
47373
47553
  }
47374
47554
  function formatPastedTextRef(id, text) {
47375
47555
  return `[Pasted text #${id} +${pastedTextLineCount(text)} lines]`;
@@ -47944,15 +48124,16 @@ function projectNameFromPath(value) {
47944
48124
  }
47945
48125
 
47946
48126
  // src/tui/prompt-history-store.mjs
48127
+ init_atomic_file();
47947
48128
  import { createHash as createHash2 } from "node:crypto";
47948
- import { chmodSync, existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "node:fs";
47949
- import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
47950
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
48129
+ import { mkdirSync as mkdirSync3 } from "node:fs";
48130
+ import { mkdir } from "node:fs/promises";
48131
+ import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
47951
48132
 
47952
48133
  // src/runtime/shared/plugin-paths.mjs
47953
48134
  import { homedir } from "node:os";
47954
- import { dirname, join } from "node:path";
47955
- import { fileURLToPath } from "node:url";
48135
+ import { dirname as dirname4, join as join4 } from "node:path";
48136
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
47956
48137
  var homeBase = null;
47957
48138
  var homePath = null;
47958
48139
  var dataBase = null;
@@ -47963,7 +48144,7 @@ function mixdogHome() {
47963
48144
  const home = homedir();
47964
48145
  if (home !== homeBase) {
47965
48146
  homeBase = home;
47966
- homePath = join(home, ".mixdog");
48147
+ homePath = join4(home, ".mixdog");
47967
48148
  }
47968
48149
  return homePath;
47969
48150
  }
@@ -47973,7 +48154,7 @@ function resolvePluginData() {
47973
48154
  const home = mixdogHome();
47974
48155
  if (home !== dataBase) {
47975
48156
  dataBase = home;
47976
- dataPath = join(home, "data");
48157
+ dataPath = join4(home, "data");
47977
48158
  }
47978
48159
  return dataPath;
47979
48160
  }
@@ -47985,10 +48166,16 @@ function promptHistoryKey(value) {
47985
48166
  return String(value || "").trim().replace(/\s+/g, " ");
47986
48167
  }
47987
48168
  function readEntries(filePath) {
47988
- if (!filePath || !existsSync3(filePath)) return [];
48169
+ if (!filePath) return [];
47989
48170
  try {
47990
- const parsed = JSON.parse(readFileSync2(filePath, "utf8"));
47991
- const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
48171
+ const parsed = readJsonStateSync(filePath, {
48172
+ mode: 384,
48173
+ validate: (value) => {
48174
+ if (!Array.isArray(value?.entries)) throw new TypeError("prompt history has no entries");
48175
+ return value;
48176
+ }
48177
+ });
48178
+ const entries = parsed ? parsed.entries : [];
47992
48179
  return entries.map((entry) => String(entry || "").trim()).filter((entry) => promptHistoryKey(entry));
47993
48180
  } catch {
47994
48181
  return [];
@@ -48001,14 +48188,8 @@ function buildPayload(entries) {
48001
48188
  function writeEntriesSync(filePath, entries) {
48002
48189
  if (!filePath) return false;
48003
48190
  try {
48004
- mkdirSync(dirname2(filePath), { recursive: true, mode: 448 });
48005
- const tmp = `${filePath}.${process.pid}.tmp`;
48006
- writeFileSync(tmp, buildPayload(entries), { encoding: "utf8", mode: 384 });
48007
- renameSync(tmp, filePath);
48008
- try {
48009
- chmodSync(filePath, 384);
48010
- } catch {
48011
- }
48191
+ mkdirSync3(dirname5(filePath), { recursive: true, mode: 448 });
48192
+ writeFileAtomicSync(filePath, buildPayload(entries), { mode: 384 });
48012
48193
  return true;
48013
48194
  } catch {
48014
48195
  return false;
@@ -48402,8 +48583,11 @@ function compactElapsed(value) {
48402
48583
  if (!Number.isFinite(n) || n <= 0) return "";
48403
48584
  return `${Math.max(1, Math.ceil(n / 1e3))}s`;
48404
48585
  }
48586
+ function compactionRunning(compaction) {
48587
+ return compaction.inProgress === true || compaction.lastStage === "compacting";
48588
+ }
48405
48589
  function compactionState(compaction) {
48406
- const compactRunning = compaction.inProgress === true || compaction.lastStage === "compacting";
48590
+ const compactRunning = compactionRunning(compaction);
48407
48591
  const autoClearFailed = compaction.lastStage === "auto_clear_failed" || !!compaction.lastClearCompactError;
48408
48592
  const autoClearStage = compaction.lastStage === "auto_clear" || compaction.lastClearAt;
48409
48593
  const compactInterrupted = compaction.lastStage === "interrupted";
@@ -48452,7 +48636,7 @@ function contextMetrics({ toolsStatus, mcpStatus, skillsStatus, pluginsStatus, c
48452
48636
  );
48453
48637
  const cacheDenom = Number(usage.lastContextTokens || 0) || cachedRead + freshInput + cacheWrite;
48454
48638
  const compactState = compactionState(compaction);
48455
- const compactDuration = compactElapsed(compaction.lastDurationMs);
48639
+ const compactDuration = compactionRunning(compaction) ? "" : compactElapsed(compaction.lastDurationMs);
48456
48640
  return {
48457
48641
  tools,
48458
48642
  mcp,
@@ -50026,7 +50210,7 @@ function useGlobalKeyInput({
50026
50210
  // src/runtime/shared/update-checker.mjs
50027
50211
  init_atomic_file();
50028
50212
  init_json_file();
50029
- import { existsSync as existsSync6, readFileSync as readFileSync6, realpathSync } from "node:fs";
50213
+ import { existsSync as existsSync5, readFileSync as readFileSync5, realpathSync } from "node:fs";
50030
50214
  import { basename as basename4, delimiter, dirname as dirname6, join as join6 } from "node:path";
50031
50215
  import { fileURLToPath as fileURLToPath3 } from "node:url";
50032
50216
  var PACKAGE_NAME = "mixdog";
@@ -50039,7 +50223,7 @@ var _localVersionCache = null;
50039
50223
  function localPackageVersion() {
50040
50224
  if (_localVersionCache) return _localVersionCache;
50041
50225
  try {
50042
- const raw = JSON.parse(readFileSync6(_PACKAGE_JSON_PATH, "utf8"));
50226
+ const raw = JSON.parse(readFileSync5(_PACKAGE_JSON_PATH, "utf8"));
50043
50227
  _localVersionCache = String(raw?.version || "0.0.0");
50044
50228
  } catch {
50045
50229
  _localVersionCache = "0.0.0";
@@ -50786,13 +50970,13 @@ import { homedir as homedir2 } from "node:os";
50786
50970
  import { join as join10 } from "node:path";
50787
50971
 
50788
50972
  // src/runtime/shared/llm/model-catalog.mjs
50789
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "node:fs";
50973
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
50790
50974
  import { join as join8 } from "node:path";
50791
50975
  import { createHash as createHash3 } from "node:crypto";
50792
50976
  init_atomic_file();
50793
50977
 
50794
50978
  // src/runtime/shared/llm/provider-catalog-cache.mjs
50795
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
50979
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
50796
50980
  import { join as join7 } from "node:path";
50797
50981
 
50798
50982
  // src/runtime/shared/llm/provider-model-identities.mjs
@@ -51187,8 +51371,8 @@ var PRICING_CATALOG_REFRESH_MS = 6 * 60 * 60 * 1e3;
51187
51371
  var PRICING_CATALOG_RETRY_MS = 6e4;
51188
51372
  function readDiskCatalog(filePath) {
51189
51373
  try {
51190
- if (!existsSync8(filePath)) return null;
51191
- const raw = JSON.parse(readFileSync8(filePath, "utf-8"));
51374
+ if (!existsSync7(filePath)) return null;
51375
+ const raw = JSON.parse(readFileSync7(filePath, "utf-8"));
51192
51376
  return raw?.data ? raw : null;
51193
51377
  } catch {
51194
51378
  return null;
@@ -51268,7 +51452,7 @@ function createCatalogSource({ url, cacheFile, project, label = "" }) {
51268
51452
  warmFromDiskSync() {
51269
51453
  if (cache3) return;
51270
51454
  try {
51271
- const raw = JSON.parse(readFileSync8(cachePath(), "utf-8"));
51455
+ const raw = JSON.parse(readFileSync7(cachePath(), "utf-8"));
51272
51456
  if (raw?.data) {
51273
51457
  cache3 = project(raw.data);
51274
51458
  cacheAt = raw.fetchedAt || Date.now();
@@ -51392,11 +51576,11 @@ function isPidAlive(value) {
51392
51576
  }
51393
51577
 
51394
51578
  // src/runtime/shared/worktrees/git-head.mjs
51395
- import { readFileSync as readFileSync9, statSync as statSync2, watch } from "node:fs";
51579
+ import { readFileSync as readFileSync8, statSync as statSync3, watch } from "node:fs";
51396
51580
  import { dirname as dirname8, resolve as resolve6 } from "node:path";
51397
51581
 
51398
51582
  // src/runtime/shared/worktrees/paths.mjs
51399
- import { existsSync as existsSync9, realpathSync as realpathSync2 } from "node:fs";
51583
+ import { existsSync as existsSync8, realpathSync as realpathSync2 } from "node:fs";
51400
51584
  import { dirname as dirname7, isAbsolute as isAbsolute3, join as join9, relative as relative2, resolve as resolve5, sep } from "node:path";
51401
51585
 
51402
51586
  // src/runtime/shared/statusline/statusline-segments.mjs
@@ -51485,8 +51669,7 @@ async function refreshShellJobsStatus(ownerPid) {
51485
51669
  continue;
51486
51670
  }
51487
51671
  const unconfirmed = detail?.terminationUnconfirmed === true;
51488
- if (unconfirmed ? !isHeldShellJobAlive(detail, ownerByJob.get(id)) : !await isShellJobAlive(detail, p, dir, id))
51489
- continue;
51672
+ if (unconfirmed ? !isPidAlive(ownerByJob.get(id) || 0) : !await isShellJobAlive(detail, p, dir, id)) continue;
51490
51673
  if (detail?.foreground === true && !unconfirmed) continue;
51491
51674
  const ownedByThisHost = ownerByJob.get(id) === ownerPid;
51492
51675
  if (ownedByThisHost) count++;
@@ -51575,12 +51758,6 @@ function summarizeShellJobs({ count, oldestMs, jobs, bySession }) {
51575
51758
  }
51576
51759
  return { count, elapsedLabel, jobs, sessions };
51577
51760
  }
51578
- function isHeldShellJobAlive(detail, ownerPid) {
51579
- if (!isPidAlive(ownerPid || 0)) return false;
51580
- if (detail?.ownerSessionClosed !== true) return true;
51581
- const survivors = Array.isArray(detail?.survivorPids) ? detail.survivorPids : [];
51582
- return [detail?.pid, ...survivors].some((pid) => isPidAlive(positiveInt(pid)));
51583
- }
51584
51761
  async function isShellJobAlive(detail, detailPath, dir, id) {
51585
51762
  const pid = positiveInt(detail?.pid);
51586
51763
  if (!pid) return false;
@@ -58611,7 +58788,9 @@ function createExtensionPickers({
58611
58788
 
58612
58789
  // src/tui/app/maintenance-pickers/auto-clear-picker.mjs
58613
58790
  var HOUR_MS = 60 * 60 * 1e3;
58614
- var formatDurationInput = (ms) => {
58791
+ var UNLIMITED_INPUT = "unlimited";
58792
+ var formatDurationInput = (ms, unlimited = false) => {
58793
+ if (unlimited) return UNLIMITED_INPUT;
58615
58794
  const value = Math.max(0, Math.round(Number(ms) || 0));
58616
58795
  if (value > 0 && value % HOUR_MS === 0) return `${value / HOUR_MS}h`;
58617
58796
  if (value > 0 && value % 6e4 === 0) return `${value / 6e4}m`;
@@ -58642,7 +58821,7 @@ function createAutoClearPicker({
58642
58821
  label: entry.provider,
58643
58822
  marker: entry.provider === provider ? "\u2713" : "",
58644
58823
  markerColor: theme2.success,
58645
- meta: `${formatDuration2(entry.idleMs)}${entry.custom ? " custom" : ""}`,
58824
+ meta: `${entry.unlimited ? "Unlimited" : formatDuration2(entry.idleMs)}${entry.custom ? " custom" : ""}`,
58646
58825
  description: `Default idle window for ${entry.provider}. Enter to edit as text.`,
58647
58826
  _action: "provider-default",
58648
58827
  _entry: entry
@@ -58661,10 +58840,10 @@ function createAutoClearPicker({
58661
58840
  store.pushNotice("autoclear unavailable", "warn");
58662
58841
  return;
58663
58842
  }
58664
- store.pushNotice(
58665
- next.enabled ? `autoclear on \xB7 idle ${formatDuration2(next.idleMs)}` : "autoclear off",
58666
- "info"
58667
- );
58843
+ let notice = "autoclear off";
58844
+ if (next.enabled)
58845
+ notice = next.unlimited ? "autoclear on \xB7 unlimited" : `autoclear on \xB7 idle ${formatDuration2(next.idleMs)}`;
58846
+ store.pushNotice(notice, "info");
58668
58847
  }).catch((e) => store.pushNotice(`autoclear failed: ${e?.message || e}`, "error")).finally(settled);
58669
58848
  };
58670
58849
  const openProviderDurationEditor = (entry) => {
@@ -58673,8 +58852,8 @@ function createAutoClearPicker({
58673
58852
  setSettingsPrompt({
58674
58853
  kind: "autoclear-provider",
58675
58854
  label: `Auto-clear \xB7 ${entry.provider}`,
58676
- hint: `Type a duration like 10m, 1h, or 24h. Empty resets to built-in ${formatDurationInput(entry.builtInMs)}.`,
58677
- initialValue: formatDurationInput(entry.idleMs),
58855
+ hint: `Type a duration like 10m, 1h, or 24h, or ${UNLIMITED_INPUT} to never auto-clear. Empty resets to built-in ${formatDurationInput(entry.builtInMs, entry.builtInUnlimited)}.`,
58856
+ initialValue: formatDurationInput(entry.idleMs, entry.unlimited),
58678
58857
  provider: entry.provider,
58679
58858
  builtInMs: entry.builtInMs,
58680
58859
  returnTo
@@ -58702,16 +58881,18 @@ function createAutoClearPicker({
58702
58881
  const current = await readCurrent();
58703
58882
  const enabled = current?.enabled !== false;
58704
58883
  const idleMs = Number(current?.idleMs || HOUR_MS);
58884
+ const unlimited = current?.unlimited === true;
58705
58885
  const cacheTtlLabel = !enabled || idleMs >= HOUR_MS ? "1h" : "5m";
58706
58886
  let sourceLabel = `${current?.provider || "default"} default`;
58707
58887
  if (current?.providerCustom) sourceLabel = `${current.provider} override`;
58708
58888
  else if (current?.custom) sourceLabel = "global";
58889
+ const clearText = unlimited ? `Never clear idle sessions (${sourceLabel})` : `Clear idle sessions after ${formatDuration2(idleMs)} (${sourceLabel})`;
58709
58890
  const items = [
58710
58891
  {
58711
58892
  value: "toggle",
58712
58893
  label: "Auto-clear",
58713
58894
  meta: enabled ? "On" : "Off",
58714
- description: enabled ? `Clear idle sessions after ${formatDuration2(idleMs)} (${sourceLabel}) \xB7 lead cache TTL ${cacheTtlLabel}.` : "Idle auto-clear disabled.",
58895
+ description: enabled ? `${clearText} \xB7 lead cache TTL ${cacheTtlLabel}.` : "Idle auto-clear disabled.",
58715
58896
  _action: "toggle"
58716
58897
  },
58717
58898
  {
@@ -58723,7 +58904,7 @@ function createAutoClearPicker({
58723
58904
  ];
58724
58905
  own.paint({
58725
58906
  title: "Auto-clear",
58726
- description: `Clear idle context after ${enabled ? `${formatDuration2(idleMs)} (${sourceLabel})` : "never"} \xB7 lead cache TTL ${cacheTtlLabel}.`,
58907
+ description: `Clear idle context after ${enabled && !unlimited ? `${formatDuration2(idleMs)} (${sourceLabel})` : "never"} \xB7 lead cache TTL ${cacheTtlLabel}.`,
58727
58908
  help: "\u2191/\u2193 Select \xB7 \u2190/\u2192 Toggle On/Off \xB7 Enter Open/Toggle \xB7 Esc Close",
58728
58909
  indexMode: "always",
58729
58910
  labelWidth: 10,
@@ -59386,6 +59567,15 @@ var loadProviderDisplayNames = async (store) => {
59386
59567
  }
59387
59568
  return names;
59388
59569
  };
59570
+ var catalogProviderDisplayNames = (models, setupNames = null) => {
59571
+ const names = {};
59572
+ for (const model of Array.isArray(models) ? models : []) {
59573
+ const provider = String(model?.provider || "").trim();
59574
+ const name = String(model?.providerName || "").trim();
59575
+ if (provider.startsWith("custom-") && name) names[provider] = name;
59576
+ }
59577
+ return { ...names, ...setupNames };
59578
+ };
59389
59579
  var loadProviderCatalogFailures = async (store) => {
59390
59580
  let setup = null;
59391
59581
  try {
@@ -59499,9 +59689,9 @@ var routeSpeedLabel = (route) => {
59499
59689
  return route.modelParameters?.serviceTier === "ultrafast" ? "Ultrafast" : "Fast";
59500
59690
  };
59501
59691
  var routeModelDetails = (route) => [routeModelDisplayName(route), route.effort ? effortDisplayLabel(route.effort) : "", routeSpeedLabel(route)].filter(Boolean).join(" \xB7 ");
59502
- var routeLabel = (route) => {
59692
+ var routeLabel = (route, providerNames = null) => {
59503
59693
  if (!route?.provider || !route?.model) return "(unset)";
59504
- return [providerDisplayName(route.provider), routeModelDetails(route)].filter(Boolean).join(" \xB7 ");
59694
+ return [resolveProviderDisplayName(route.provider, providerNames), routeModelDetails(route)].filter(Boolean).join(" \xB7 ");
59505
59695
  };
59506
59696
  var routeModelLabel = (route) => route?.model ? routeModelDetails(route) : "(unset)";
59507
59697
  var agentModelProfile = (route) => route?.model ? routeModelDetails(route) : "";
@@ -59891,7 +60081,7 @@ function createOnboardingSteps({
59891
60081
  import { spawn as spawn4 } from "node:child_process";
59892
60082
 
59893
60083
  // src/runtime/shared/wsl.mjs
59894
- import { readFileSync as readFileSync10 } from "node:fs";
60084
+ import { readFileSync as readFileSync9 } from "node:fs";
59895
60085
  var _isWSL;
59896
60086
  function isWSL() {
59897
60087
  if (_isWSL !== void 0) return _isWSL;
@@ -59904,7 +60094,7 @@ function isWSL() {
59904
60094
  return _isWSL;
59905
60095
  }
59906
60096
  try {
59907
- const v = readFileSync10("/proc/version", "utf8").toLowerCase();
60097
+ const v = readFileSync9("/proc/version", "utf8").toLowerCase();
59908
60098
  _isWSL = v.includes("microsoft") || v.includes("wsl");
59909
60099
  } catch {
59910
60100
  _isWSL = false;
@@ -60978,7 +61168,7 @@ function createModelPicker({
60978
61168
  return;
60979
61169
  }
60980
61170
  const models = normalizeModelOptions(providerModels);
60981
- const providerNames = await loadProviderDisplayNames(store);
61171
+ const providerNames = catalogProviderDisplayNames(models, await loadProviderDisplayNames(store));
60982
61172
  const catalogFailures = await loadProviderCatalogFailures(store);
60983
61173
  const activeRoute = options.currentRoute || {
60984
61174
  provider: state.provider,
@@ -61406,7 +61596,8 @@ function createRoutePickers({
61406
61596
  store.pushNotice("Web-search model save is already running.", "warn");
61407
61597
  return;
61408
61598
  }
61409
- store.pushNotice(`Web-search model set to ${routeLabel2(result)}`, "info");
61599
+ const providerNames = await loadProviderDisplayNames(store);
61600
+ store.pushNotice(`Web-search model set to ${routeLabel2(result, providerNames)}`, "info");
61410
61601
  return result;
61411
61602
  },
61412
61603
  onAfterSelect: null
@@ -61481,8 +61672,8 @@ function createSettingsNavigation({ openers, openSettingsPicker, setSettingsProm
61481
61672
  }
61482
61673
 
61483
61674
  // src/runtime/shared/config.mjs
61484
- import { readFileSync as readFileSync12, statSync as statSync4, mkdirSync as mkdirSync5, existsSync as existsSync11, watch as fsWatch } from "node:fs";
61485
- import { join as join12, dirname as dirname10 } from "node:path";
61675
+ import { readFileSync as readFileSync11, readdirSync as readdirSync3, statSync as statSync5, mkdirSync as mkdirSync5, existsSync as existsSync10, watch as fsWatch } from "node:fs";
61676
+ import { basename as basename6, join as join12, dirname as dirname9 } from "node:path";
61486
61677
  import { createRequire as createRequire3 } from "node:module";
61487
61678
 
61488
61679
  // src/runtime/shared/object.mjs
@@ -61497,24 +61688,16 @@ function isPlainObject(value) {
61497
61688
  init_atomic_file();
61498
61689
 
61499
61690
  // src/runtime/shared/user-data-guard.mjs
61691
+ init_atomic_file();
61692
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync10, rmSync, statSync as statSync4 } from "node:fs";
61500
61693
  import {
61501
- copyFileSync,
61502
- existsSync as existsSync10,
61503
- mkdirSync as mkdirSync4,
61504
- readdirSync as readdirSync2,
61505
- readFileSync as readFileSync11,
61506
- rmSync,
61507
- statSync as statSync3,
61508
- writeFileSync as writeFileSync4
61509
- } from "node:fs";
61510
- import {
61511
- copyFile as copyFileP,
61512
61694
  mkdir as mkdirP,
61695
+ readFile as readFileP,
61513
61696
  readdir as readdirP,
61514
61697
  rm as rmP,
61515
61698
  stat as statP
61516
61699
  } from "node:fs/promises";
61517
- import { basename as basename5, dirname as dirname9, join as join11, resolve as resolve7 } from "node:path";
61700
+ import { basename as basename5, join as join11, resolve as resolve7 } from "node:path";
61518
61701
  import { homedir as homedir3 } from "node:os";
61519
61702
  import { createHash as createHash4, randomUUID } from "node:crypto";
61520
61703
  function getBackupRoot() {
@@ -61601,12 +61784,24 @@ function safeReason(reason) {
61601
61784
  }
61602
61785
  function readInitInstanceId(dataDir2) {
61603
61786
  if (!dataDir2) return null;
61787
+ const path2 = join11(dataDir2, INIT_MARKER);
61788
+ let raw;
61604
61789
  try {
61605
- const marker = JSON.parse(readFileSync11(join11(dataDir2, INIT_MARKER), "utf8"));
61606
- return typeof marker?.instanceId === "string" && marker.instanceId ? marker.instanceId : null;
61607
- } catch {
61608
- return null;
61790
+ raw = readFileSync10(path2, "utf8");
61791
+ } catch (error) {
61792
+ if (error?.code === "ENOENT") return null;
61793
+ throw error;
61794
+ }
61795
+ let marker;
61796
+ try {
61797
+ marker = JSON.parse(raw);
61798
+ } catch (error) {
61799
+ throw new SyntaxError(`[user-data] invalid initialization marker at ${path2}`, { cause: error });
61800
+ }
61801
+ if (typeof marker?.instanceId !== "string" || !marker.instanceId) {
61802
+ throw new SyntaxError(`[user-data] initialization marker at ${path2} must have a non-empty instanceId`);
61609
61803
  }
61804
+ return marker.instanceId;
61610
61805
  }
61611
61806
  function hasUserDataInitMarker(dataDir2) {
61612
61807
  return readInitInstanceId(dataDir2) != null;
@@ -61618,7 +61813,7 @@ function legacyBackupRoots() {
61618
61813
  }
61619
61814
  function manifestDataDirMatches(backupDir, dataDirKey) {
61620
61815
  try {
61621
- const manifest = JSON.parse(readFileSync11(join11(backupDir, BACKUP_MANIFEST), "utf8"));
61816
+ const manifest = JSON.parse(readFileSync10(join11(backupDir, BACKUP_MANIFEST), "utf8"));
61622
61817
  return typeof manifest?.dataDir === "string" && normalizeDataDir(manifest.dataDir) === dataDirKey;
61623
61818
  } catch {
61624
61819
  return false;
@@ -61639,7 +61834,7 @@ function legacyMarkersFor(root, dataDir2, dataDirKey) {
61639
61834
  return names.filter((name) => {
61640
61835
  if (hashed.has(name)) return true;
61641
61836
  try {
61642
- const marker = JSON.parse(readFileSync11(join11(root, name), "utf8"));
61837
+ const marker = JSON.parse(readFileSync10(join11(root, name), "utf8"));
61643
61838
  return typeof marker?.dataDir === "string" && normalizeDataDir(marker.dataDir) === dataDirKey;
61644
61839
  } catch {
61645
61840
  return false;
@@ -61700,10 +61895,10 @@ function backupDirsNewestFirst(dir) {
61700
61895
  }
61701
61896
  function readRestorableConfig(backupDir, dataDirKey, instanceId) {
61702
61897
  try {
61703
- const manifest = JSON.parse(readFileSync11(join11(backupDir, BACKUP_MANIFEST), "utf8"));
61898
+ const manifest = JSON.parse(readFileSync10(join11(backupDir, BACKUP_MANIFEST), "utf8"));
61704
61899
  if (typeof manifest?.dataDir !== "string" || normalizeDataDir(manifest.dataDir) !== dataDirKey) return null;
61705
61900
  if (manifest.instanceId !== instanceId) return null;
61706
- const parsed = JSON.parse(readFileSync11(join11(backupDir, "mixdog-config.json"), "utf8"));
61901
+ const parsed = JSON.parse(readFileSync10(join11(backupDir, "mixdog-config.json"), "utf8"));
61707
61902
  return isStructurallyCompleteMixdogConfigBackup(parsed) ? parsed : null;
61708
61903
  } catch (error) {
61709
61904
  if (isAbsentBackupError(error)) return null;
@@ -61743,7 +61938,7 @@ function loadLegacyMixdogConfigFromBackup(dataDir2, options) {
61743
61938
  const evidence = findLegacyUserDataEvidence(dataDir2, options);
61744
61939
  for (const backupDir of evidence?.backups || []) {
61745
61940
  try {
61746
- const parsed = JSON.parse(readFileSync11(join11(backupDir, "mixdog-config.json"), "utf8"));
61941
+ const parsed = JSON.parse(readFileSync10(join11(backupDir, "mixdog-config.json"), "utf8"));
61747
61942
  if (isStructurallyCompleteMixdogConfigBackup(parsed)) return parsed;
61748
61943
  } catch {
61749
61944
  }
@@ -61751,7 +61946,7 @@ function loadLegacyMixdogConfigFromBackup(dataDir2, options) {
61751
61946
  return null;
61752
61947
  }
61753
61948
  function copyTree(src, dst, copied) {
61754
- const st = statSync3(src);
61949
+ const st = statSync4(src);
61755
61950
  if (st.isDirectory()) {
61756
61951
  for (const name of readdirSync2(src)) {
61757
61952
  copyTree(join11(src, name), join11(dst, name), copied);
@@ -61759,12 +61954,19 @@ function copyTree(src, dst, copied) {
61759
61954
  return;
61760
61955
  }
61761
61956
  if (!st.isFile()) return;
61762
- mkdirSync4(dirname9(dst), { recursive: true });
61763
- copyFileSync(src, dst);
61957
+ writeFileAtomicSync(dst, readFileSync10(src), { mode: st.mode & 511 });
61764
61958
  copied.push(dst);
61765
61959
  }
61960
+ var manifestPath = (scopeDir, name) => join11(scopeDir, name, BACKUP_MANIFEST);
61766
61961
  function pruneBackups(scopeDir, names, own) {
61767
- for (const name of backupsToPrune(names, own)) {
61962
+ const completed = names.filter((name) => {
61963
+ try {
61964
+ return statSync4(manifestPath(scopeDir, name)).isFile();
61965
+ } catch {
61966
+ return false;
61967
+ }
61968
+ });
61969
+ for (const name of backupsToPrune(completed, own)) {
61768
61970
  try {
61769
61971
  rmSync(join11(scopeDir, name), { recursive: true, force: true });
61770
61972
  } catch {
@@ -61773,18 +61975,14 @@ function pruneBackups(scopeDir, names, own) {
61773
61975
  }
61774
61976
  function markUserDataInitialized(dataDir2) {
61775
61977
  const existing = readInitInstanceId(dataDir2);
61776
- if (existing || !dataDir2 || !existsSync10(dataDir2)) return existing;
61978
+ if (existing || !dataDir2 || !existsSync9(dataDir2)) return existing;
61777
61979
  try {
61778
- writeFileSync4(
61779
- join11(dataDir2, INIT_MARKER),
61780
- `${JSON.stringify(
61781
- { instanceId: randomUUID(), dataDir: resolve7(dataDir2), createdAt: (/* @__PURE__ */ new Date()).toISOString() },
61782
- null,
61783
- 2
61784
- )}
61785
- `,
61786
- // An unreadable marker is replaced; a concurrent creator's valid one wins.
61787
- { encoding: "utf8", flag: existsSync10(join11(dataDir2, INIT_MARKER)) ? "w" : "wx" }
61980
+ const marker = join11(dataDir2, INIT_MARKER);
61981
+ writeJsonAtomicSync(
61982
+ marker,
61983
+ { instanceId: randomUUID(), dataDir: resolve7(dataDir2), createdAt: (/* @__PURE__ */ new Date()).toISOString() },
61984
+ // Another creator may have published since the absent read; it wins.
61985
+ { createOnly: true }
61788
61986
  );
61789
61987
  } catch {
61790
61988
  }
@@ -61795,43 +61993,47 @@ function ensureUserDataInitialized(dataDir2) {
61795
61993
  if (!dataDir2) return;
61796
61994
  const key = normalizeDataDir(dataDir2);
61797
61995
  if (ensuredDataDirs.has(key)) return;
61798
- ensuredDataDirs.add(key);
61799
61996
  if (!hasUserDataInitMarker(dataDir2)) backupUserData(dataDir2, "initial-snapshot");
61997
+ ensuredDataDirs.add(key);
61800
61998
  }
61801
61999
  function skipBackups() {
61802
62000
  return process.env.MIXDOG_SKIP_USER_DATA_BACKUP === "1" || process.env.MIXDOG_SKIP_USER_DATA_BACKUP === "true";
61803
62001
  }
61804
62002
  function finishBackup(dataDir2, backupDir, reason) {
61805
62003
  const instanceId = markUserDataInitialized(dataDir2);
61806
- writeFileSync4(
61807
- join11(backupDir, BACKUP_MANIFEST),
61808
- `${JSON.stringify(
61809
- { dataDir: resolve7(dataDir2), instanceId, reason, createdAt: (/* @__PURE__ */ new Date()).toISOString() },
61810
- null,
61811
- 2
61812
- )}
61813
- `,
61814
- "utf8"
61815
- );
62004
+ writeJsonAtomicSync(join11(backupDir, BACKUP_MANIFEST), {
62005
+ dataDir: resolve7(dataDir2),
62006
+ instanceId,
62007
+ reason,
62008
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
62009
+ });
61816
62010
  restoreLookupCache.delete(normalizeDataDir(dataDir2));
61817
62011
  legacyEvidenceCache.delete(normalizeDataDir(dataDir2));
61818
62012
  }
61819
62013
  function backupUserData(dataDir2, reason = "snapshot", { dirs = [] } = {}) {
61820
62014
  if (skipBackups()) return { dir: null, copied: [] };
61821
- if (!dataDir2 || !existsSync10(dataDir2)) return { dir: null, copied: [] };
62015
+ if (!dataDir2 || !existsSync9(dataDir2)) return { dir: null, copied: [] };
61822
62016
  const scopeDir = scopeDirFor(dataDir2);
61823
62017
  const { backupDir, names } = reserveBackupDir(scopeDir, reason);
61824
62018
  const copied = [];
61825
- for (const rel of USER_DATA_FILES) {
61826
- const src = join11(dataDir2, rel);
61827
- if (existsSync10(src)) copyTree(src, join11(backupDir, rel), copied);
61828
- }
61829
- for (const rel of [...USER_DATA_DIRS, ...dirs]) {
61830
- const src = join11(dataDir2, rel);
61831
- if (existsSync10(src)) copyTree(src, join11(backupDir, rel), copied);
62019
+ try {
62020
+ for (const rel of USER_DATA_FILES) {
62021
+ const src = join11(dataDir2, rel);
62022
+ if (existsSync9(src)) copyTree(src, join11(backupDir, rel), copied);
62023
+ }
62024
+ for (const rel of [...USER_DATA_DIRS, ...dirs]) {
62025
+ const src = join11(dataDir2, rel);
62026
+ if (existsSync9(src)) copyTree(src, join11(backupDir, rel), copied);
62027
+ }
62028
+ if (copied.length > 0) finishBackup(dataDir2, backupDir, reason);
62029
+ } catch (error) {
62030
+ try {
62031
+ rmSync(backupDir, { recursive: true, force: true });
62032
+ } catch {
62033
+ }
62034
+ throw error;
61832
62035
  }
61833
62036
  if (copied.length > 0) {
61834
- finishBackup(dataDir2, backupDir, reason);
61835
62037
  pruneBackups(scopeDir, names, basename5(backupDir));
61836
62038
  if (process.env.MIXDOG_SETUP_QUIET !== "1") {
61837
62039
  process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}
@@ -61851,12 +62053,18 @@ async function copyTreeAsync(src, dst, copied) {
61851
62053
  return;
61852
62054
  }
61853
62055
  if (!st.isFile()) return;
61854
- await mkdirP(dirname9(dst), { recursive: true });
61855
- await copyFileP(src, dst);
62056
+ await writeFileAtomicAsync(dst, await readFileP(src), { mode: st.mode & 511 });
61856
62057
  copied.push(dst);
61857
62058
  }
61858
62059
  async function pruneBackupsAsync(scopeDir, names, own) {
61859
- for (const name of backupsToPrune(names, own)) {
62060
+ const completed = [];
62061
+ for (const name of names) {
62062
+ try {
62063
+ if ((await statP(manifestPath(scopeDir, name))).isFile()) completed.push(name);
62064
+ } catch {
62065
+ }
62066
+ }
62067
+ for (const name of backupsToPrune(completed, own)) {
61860
62068
  try {
61861
62069
  await rmP(join11(scopeDir, name), { recursive: true, force: true });
61862
62070
  } catch {
@@ -61865,20 +62073,28 @@ async function pruneBackupsAsync(scopeDir, names, own) {
61865
62073
  }
61866
62074
  async function backupUserDataAsync(dataDir2, reason = "snapshot") {
61867
62075
  if (skipBackups()) return { dir: null, copied: [] };
61868
- if (!dataDir2 || !existsSync10(dataDir2)) return { dir: null, copied: [] };
62076
+ if (!dataDir2 || !existsSync9(dataDir2)) return { dir: null, copied: [] };
61869
62077
  const scopeDir = scopeDirFor(dataDir2);
61870
62078
  const { backupDir, names } = await reserveBackupDirAsync(scopeDir, reason);
61871
62079
  const copied = [];
61872
- for (const rel of USER_DATA_FILES) {
61873
- const src = join11(dataDir2, rel);
61874
- if (existsSync10(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
61875
- }
61876
- for (const rel of USER_DATA_DIRS) {
61877
- const src = join11(dataDir2, rel);
61878
- if (existsSync10(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
62080
+ try {
62081
+ for (const rel of USER_DATA_FILES) {
62082
+ const src = join11(dataDir2, rel);
62083
+ if (existsSync9(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
62084
+ }
62085
+ for (const rel of USER_DATA_DIRS) {
62086
+ const src = join11(dataDir2, rel);
62087
+ if (existsSync9(src)) await copyTreeAsync(src, join11(backupDir, rel), copied);
62088
+ }
62089
+ if (copied.length > 0) finishBackup(dataDir2, backupDir, reason);
62090
+ } catch (error) {
62091
+ try {
62092
+ await rmP(backupDir, { recursive: true, force: true });
62093
+ } catch {
62094
+ }
62095
+ throw error;
61879
62096
  }
61880
62097
  if (copied.length > 0) {
61881
- finishBackup(dataDir2, backupDir, reason);
61882
62098
  await pruneBackupsAsync(scopeDir, names, basename5(backupDir));
61883
62099
  if (process.env.MIXDOG_SETUP_QUIET !== "1") {
61884
62100
  process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}
@@ -61950,7 +62166,7 @@ function readConfigRawCached() {
61950
62166
  return _configReadCache.raw;
61951
62167
  }
61952
62168
  try {
61953
- const st2 = statSync4(configPath());
62169
+ const st2 = statSync5(configPath());
61954
62170
  if (st2.mtimeMs === _configReadCache.mtimeMs && st2.size === _configReadCache.size) {
61955
62171
  _configReadCache.atMs = now;
61956
62172
  return _configReadCache.raw;
@@ -61960,7 +62176,7 @@ function readConfigRawCached() {
61960
62176
  }
61961
62177
  let st = null;
61962
62178
  try {
61963
- st = statSync4(configPath());
62179
+ st = statSync5(configPath());
61964
62180
  } catch (err) {
61965
62181
  if (err.code === "ENOENT") {
61966
62182
  _configReadCache = null;
@@ -61972,7 +62188,7 @@ function readConfigRawCached() {
61972
62188
  }
61973
62189
  let raw;
61974
62190
  try {
61975
- raw = readFileSync12(configPath(), "utf8");
62191
+ raw = readFileSync11(configPath(), "utf8");
61976
62192
  } catch (err) {
61977
62193
  if (err.code === "ENOENT") {
61978
62194
  _configReadCache = null;
@@ -62039,7 +62255,7 @@ function readConfigFile() {
62039
62255
  const raw = readConfigRawCached();
62040
62256
  if (raw == null) return null;
62041
62257
  try {
62042
- return JSON.parse(raw);
62258
+ return parseJsonText(raw);
62043
62259
  } catch (err) {
62044
62260
  invalidateConfigReadCache();
62045
62261
  quarantineMalformedConfig(err);
@@ -62047,7 +62263,7 @@ function readConfigFile() {
62047
62263
  }
62048
62264
  }
62049
62265
  function writeConfigFile(data, postBackupReason = "post-config-write") {
62050
- mkdirSync5(dirname10(configPath()), { recursive: true, mode: 448 });
62266
+ mkdirSync5(dirname9(configPath()), { recursive: true, mode: 448 });
62051
62267
  try {
62052
62268
  backupUserData(resolvePluginData(), "pre-config-write");
62053
62269
  } catch {
@@ -62071,7 +62287,7 @@ function persistRestoredConfig(restored) {
62071
62287
  const wrote = withFileLockSync(
62072
62288
  `${path2}.lock`,
62073
62289
  () => {
62074
- if (existsSync11(path2)) return false;
62290
+ if (existsSync10(path2)) return false;
62075
62291
  writeAll(restored, "post-restore");
62076
62292
  return true;
62077
62293
  },
@@ -62117,23 +62333,42 @@ function readAll() {
62117
62333
  return {};
62118
62334
  }
62119
62335
  function quarantineMalformedConfig(parseErr) {
62120
- const corrupt = `${configPath()}.corrupt-${Date.now()}`;
62336
+ const corrupt = quarantinePath(configPath());
62337
+ let keptAside = false;
62121
62338
  try {
62122
- if (existsSync11(configPath())) renameWithRetrySync(configPath(), corrupt);
62339
+ if (existsSync10(configPath())) renameWithRetrySync(configPath(), corrupt);
62340
+ keptAside = existsSync10(corrupt);
62123
62341
  } catch {
62124
62342
  }
62125
62343
  process.stderr.write(
62126
62344
  `[config] mixdog-config.json is malformed (${parseErr.message}). Renamed to ${corrupt}. Restore it or delete to start fresh.
62127
62345
  `
62128
62346
  );
62347
+ return keptAside;
62129
62348
  }
62130
- function restoreAllForRmWOrThrow(reason) {
62349
+ function hasQuarantinedCopy() {
62350
+ const dir = dirname9(configPath());
62351
+ const prefix = `${basename6(configPath())}.corrupt-`;
62352
+ try {
62353
+ return readdirSync3(dir).some((name) => name.startsWith(prefix));
62354
+ } catch {
62355
+ return false;
62356
+ }
62357
+ }
62358
+ function restoreAllForRmWOrThrow(reason, keptAside = false) {
62131
62359
  const restored = loadLatestMixdogConfigFromBackup(resolvePluginData(), { fresh: true });
62132
62360
  if (restored && isPlainObject(restored)) {
62133
62361
  process.stderr.write(`[config] RMW read: restored mixdog-config.json from latest user-data backup (${reason})
62134
62362
  `);
62135
62363
  return restored;
62136
62364
  }
62365
+ if (keptAside) {
62366
+ process.stderr.write(
62367
+ `[config] RMW read: mixdog-config.json is unusable (${reason}); its bytes are kept aside and no backup exists; starting empty
62368
+ `
62369
+ );
62370
+ return {};
62371
+ }
62137
62372
  const legacy = findLegacyUserDataEvidence(resolvePluginData(), { fresh: true });
62138
62373
  if (legacy) {
62139
62374
  const err = new Error(
@@ -62149,11 +62384,11 @@ function restoreAllForRmWOrThrow(reason) {
62149
62384
  function readAllForRmW() {
62150
62385
  let raw;
62151
62386
  try {
62152
- raw = readFileSync12(configPath(), "utf8");
62387
+ raw = readFileSync11(configPath(), "utf8");
62153
62388
  } catch (err) {
62154
62389
  if (err.code === "ENOENT") {
62155
62390
  if (isUserDataRecoveryExpected(resolvePluginData(), { fresh: true })) {
62156
- return restoreAllForRmWOrThrow("config file missing after user-data was initialized");
62391
+ return restoreAllForRmWOrThrow("config file missing after user-data was initialized", hasQuarantinedCopy());
62157
62392
  }
62158
62393
  return {};
62159
62394
  }
@@ -62162,12 +62397,11 @@ function readAllForRmW() {
62162
62397
  throw err;
62163
62398
  }
62164
62399
  try {
62165
- const parsed = JSON.parse(raw);
62400
+ const parsed = parseJsonText(raw);
62166
62401
  if (!isPlainObject(parsed)) throw new SyntaxError("config root must be a JSON object");
62167
62402
  return parsed;
62168
62403
  } catch (parseErr) {
62169
- quarantineMalformedConfig(parseErr);
62170
- return restoreAllForRmWOrThrow(parseErr.message);
62404
+ return restoreAllForRmWOrThrow(parseErr.message, quarantineMalformedConfig(parseErr));
62171
62405
  }
62172
62406
  }
62173
62407
  var CONFIG_CHANGE_DEBOUNCE_MS = 100;
@@ -62196,7 +62430,7 @@ function readSection(section) {
62196
62430
  return stripGeneratedMarker(readAll()[section] ?? null) ?? {};
62197
62431
  }
62198
62432
  async function writeConfigFileAsync(data) {
62199
- mkdirSync5(dirname10(configPath()), { recursive: true, mode: 448 });
62433
+ mkdirSync5(dirname9(configPath()), { recursive: true, mode: 448 });
62200
62434
  try {
62201
62435
  await backupUserDataAsync(resolvePluginData(), "pre-config-write");
62202
62436
  } catch {
@@ -62517,8 +62751,8 @@ function buildSettingsItems({
62517
62751
  {
62518
62752
  value: "autoclear",
62519
62753
  label: "Auto-clear",
62520
- meta: autoClearEnabled ? `On (${formatDuration2(autoClear.idleMs)})` : "Off",
62521
- description: autoClearEnabled ? `Clear idle sessions after ${formatDuration2(autoClear.idleMs)}${autoClearSource}. Enter for options.` : "Idle auto-clear disabled. Enter for options.",
62754
+ meta: autoClearEnabled ? `On (${autoClear.unlimited ? "Unlimited" : formatDuration2(autoClear.idleMs)})` : "Off",
62755
+ description: autoClearEnabled ? `${autoClear.unlimited ? "Never clear idle sessions" : `Clear idle sessions after ${formatDuration2(autoClear.idleMs)}`}${autoClearSource}. Enter for options.` : "Idle auto-clear disabled. Enter for options.",
62522
62756
  _action: "autoclear"
62523
62757
  },
62524
62758
  {
@@ -63773,7 +64007,9 @@ function useMessageSelector({
63773
64007
  setPromptDraftOverride,
63774
64008
  syncPromptLayoutRows,
63775
64009
  showPromptHint,
63776
- clearPromptHint
64010
+ clearPromptHint,
64011
+ installPastedImages,
64012
+ installPastedTexts
63777
64013
  }) {
63778
64014
  const hasUserMessages = useMemo7(
63779
64015
  () => (Array.isArray(state.items) ? state.items : []).some(
@@ -63789,11 +64025,21 @@ function useMessageSelector({
63789
64025
  return false;
63790
64026
  }
63791
64027
  clearPromptHint();
64028
+ if (restored.pastedImages) installPastedImages?.(restored.pastedImages, { merge: true });
64029
+ if (restored.pastedTexts) installPastedTexts?.(restored.pastedTexts, { merge: true });
63792
64030
  syncPromptLayoutRows(text);
63793
64031
  setPromptDraftOverride({ id: Date.now(), value: text });
64032
+ if (restored.notice) showPromptHint(restored.notice, "error");
63794
64033
  return true;
63795
64034
  },
63796
- [clearPromptHint, syncPromptLayoutRows, setPromptDraftOverride, showPromptHint]
64035
+ [
64036
+ clearPromptHint,
64037
+ syncPromptLayoutRows,
64038
+ setPromptDraftOverride,
64039
+ showPromptHint,
64040
+ installPastedImages,
64041
+ installPastedTexts
64042
+ ]
63797
64043
  );
63798
64044
  const openMessageSelector = useCallback20(() => {
63799
64045
  const items = store.getState?.().items ?? state.items;
@@ -63813,7 +64059,7 @@ function useMessageSelector({
63813
64059
  own.close();
63814
64060
  let restored;
63815
64061
  try {
63816
- restored = store.rewindToItem?.(value);
64062
+ restored = store.rewindToItem?.(value, { restoreFiles: false });
63817
64063
  } catch (error) {
63818
64064
  showPromptHint(`rewind failed: ${error?.message || error}`, "error");
63819
64065
  return;
@@ -63907,13 +64153,13 @@ import { useCallback as useCallback21, useEffect as useEffect27, useLayoutEffect
63907
64153
 
63908
64154
  // src/tui/session/transcript-spill.mjs
63909
64155
  import { randomBytes as randomBytes4 } from "node:crypto";
63910
- import { readFileSync as readFileSync16 } from "node:fs";
64156
+ import { readFileSync as readFileSync15 } from "node:fs";
63911
64157
  import { join as join16 } from "node:path";
63912
64158
  import { Worker as Worker2 } from "node:worker_threads";
63913
64159
 
63914
64160
  // src/tui/session/transcript-spill/spill-dir.mjs
63915
64161
  import { randomUUID as randomUUID2 } from "node:crypto";
63916
- import { mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync15, rmSync as rmSync5, statSync as statSync6, writeFileSync as writeFileSync6 } from "node:fs";
64162
+ import { mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync14, rmSync as rmSync5, statSync as statSync7, writeFileSync as writeFileSync4 } from "node:fs";
63917
64163
  import { tmpdir as tmpdir3 } from "node:os";
63918
64164
  import { join as join15 } from "node:path";
63919
64165
  var STALE_MS = 24 * 60 * 60 * 1e3;
@@ -65724,7 +65970,9 @@ function App2({ store, initialStatusLine = "", forceOnboarding = false, onboardi
65724
65970
  setPromptDraftOverride,
65725
65971
  syncPromptLayoutRows,
65726
65972
  showPromptHint,
65727
- clearPromptHint
65973
+ clearPromptHint,
65974
+ installPastedImages,
65975
+ installPastedTexts
65728
65976
  });
65729
65977
  const { handlePromptPaste, handlePromptHistoryNavigate, handlePromptEscape, handlePromptInterrupt } = usePromptHandlers({
65730
65978
  store,
@@ -66364,16 +66612,16 @@ function drainStdin(stdin = process.stdin) {
66364
66612
  let fd = -1;
66365
66613
  try {
66366
66614
  if (!wasRaw) tty3.setRawMode?.(true);
66367
- fd = openSync3("/dev/tty", fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
66615
+ fd = openSync4("/dev/tty", fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
66368
66616
  const buf = Buffer.alloc(1024);
66369
66617
  for (let i = 0; i < 64; i++) {
66370
- if (readSync(fd, buf, 0, buf.length, null) <= 0) break;
66618
+ if (readSync2(fd, buf, 0, buf.length, null) <= 0) break;
66371
66619
  }
66372
66620
  } catch {
66373
66621
  } finally {
66374
66622
  if (fd >= 0) {
66375
66623
  try {
66376
- closeSync3(fd);
66624
+ closeSync4(fd);
66377
66625
  } catch {
66378
66626
  }
66379
66627
  }
@@ -66391,7 +66639,7 @@ function installTuiStderrGuard() {
66391
66639
  const originalWrite = process.stderr.write.bind(process.stderr);
66392
66640
  const logPath = resolveTuiStderrLogPath();
66393
66641
  try {
66394
- mkdirSync8(dirname12(logPath), { recursive: true });
66642
+ mkdirSync8(dirname11(logPath), { recursive: true });
66395
66643
  } catch {
66396
66644
  }
66397
66645
  (0, import_mixdog_debug.rotateBoundedLog)(logPath, import_mixdog_debug.PLUGIN_LOG_MAX_BYTES, import_mixdog_debug.PLUGIN_LOG_KEEP_BYTES);