mixdog 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/NOTICE.md +52 -9
  2. package/README.md +8 -4
  3. package/package.json +2 -2
  4. package/scripts/native-release-status.mjs +238 -0
  5. package/src/defaults/shipped-history.json +8 -1
  6. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  7. package/src/defaults/skills/setup/SKILL.md +23 -1
  8. package/src/defaults/skills/setup/references/actions.md +54 -4
  9. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  10. package/src/defaults/skills/setup-guide/SKILL.md +17 -10
  11. package/src/defaults/skills/visualize/SKILL.md +9 -6
  12. package/src/defaults/skills/visualize/references/craft.md +8 -1
  13. package/src/help.mjs +1 -1
  14. package/src/lib/keychain-cjs.cjs +4 -1
  15. package/src/lib/rules-builder.cjs +14 -7
  16. package/src/output-styles/common.md +13 -8
  17. package/src/rules/agent-mode/AGENT_MODE.md +9 -6
  18. package/src/rules/agent-mode/MIXY.md +11 -6
  19. package/src/rules/lead/LEAD.md +2 -2
  20. package/src/rules/shared/10-tool-workflow.md +4 -0
  21. package/src/rules/shared/20-research.md +2 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  23. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  25. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  26. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  27. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  28. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  29. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  31. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  32. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +81 -16
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -4
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  36. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  37. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  38. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  41. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
  43. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  44. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +34 -16
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  48. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  49. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -1
  50. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  51. package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
  52. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  53. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  54. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  56. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  57. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  58. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
  59. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +10 -2
  60. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  62. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  64. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  65. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
  68. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  69. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  72. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  73. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +5 -5
  74. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +17 -6
  78. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +41 -10
  79. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +6 -4
  80. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  81. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  83. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  85. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +21 -2
  86. package/src/runtime/agent/orchestrator/session/agent-room.mjs +149 -45
  87. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  88. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  89. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  90. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  91. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  92. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  93. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  94. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  95. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  96. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  97. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  99. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  100. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +156 -25
  101. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  102. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  103. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  104. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  105. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  106. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  108. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
  109. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  110. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +111 -9
  111. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -14
  112. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  113. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  114. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  115. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  116. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +46 -4
  119. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  120. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  124. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  125. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  126. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  128. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  130. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
  131. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  132. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  134. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +62 -47
  135. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +6 -8
  136. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
  139. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  140. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  142. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +5 -2
  143. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  144. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  145. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +35 -4
  146. package/src/runtime/agent/orchestrator/session/routine-result.mjs +179 -16
  147. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  148. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  149. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  150. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  151. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  152. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  153. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  154. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  155. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  156. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  157. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  158. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  159. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  160. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  161. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +49 -14
  162. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +134 -17
  163. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  164. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  167. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  168. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  169. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  170. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  171. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  175. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  176. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  177. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  178. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  179. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  180. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  181. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  182. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  183. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  184. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  185. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  186. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  187. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  188. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  189. package/src/runtime/attachments/limits.mjs +5 -1
  190. package/src/runtime/attachments/office-extract.mjs +3 -6
  191. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  192. package/src/runtime/attachments/store.mjs +55 -30
  193. package/src/runtime/browser-bridge/client.mjs +73 -20
  194. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  195. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  196. package/src/runtime/channels/lib/state-file.mjs +15 -15
  197. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  198. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  199. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  200. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  201. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  202. package/src/runtime/effort-judge/judge-client.mjs +6 -3
  203. package/src/runtime/effort-judge/model-install.mjs +16 -8
  204. package/src/runtime/effort-judge/model-manifest.json +7 -7
  205. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  206. package/src/runtime/local-provider/catalog.mjs +5 -2
  207. package/src/runtime/local-provider/registered-models.mjs +64 -27
  208. package/src/runtime/local-provider/server.mjs +2 -3
  209. package/src/runtime/media/adapters/image-api.mjs +89 -0
  210. package/src/runtime/media/auth.mjs +64 -1
  211. package/src/runtime/media/catalog.mjs +66 -4
  212. package/src/runtime/media/jobs.mjs +4 -0
  213. package/src/runtime/media/lanes.mjs +46 -5
  214. package/src/runtime/media/media-usage.mjs +17 -0
  215. package/src/runtime/media/renditions.mjs +15 -15
  216. package/src/runtime/media/store.mjs +36 -35
  217. package/src/runtime/media/tool.mjs +8 -1
  218. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  219. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  220. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  221. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  222. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  223. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  224. package/src/runtime/office/core/journal.mjs +7 -14
  225. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  226. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  227. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  228. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  229. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  230. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  231. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  232. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  233. package/src/runtime/shared/agent-kind.mjs +152 -0
  234. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  235. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  236. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  237. package/src/runtime/shared/agent-route-config.d.mts +1 -0
  238. package/src/runtime/shared/agent-route-config.mjs +22 -8
  239. package/src/runtime/shared/agent-store.mjs +149 -0
  240. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  241. package/src/runtime/shared/atomic-file.d.mts +50 -0
  242. package/src/runtime/shared/atomic-file.mjs +394 -132
  243. package/src/runtime/shared/background-tasks.mjs +7 -4
  244. package/src/runtime/shared/bot-hops.mjs +30 -0
  245. package/src/runtime/shared/bot-split-migration.mjs +457 -0
  246. package/src/runtime/shared/bounded-download.mjs +4 -2
  247. package/src/runtime/shared/config-patch.mjs +20 -1
  248. package/src/runtime/shared/config.mjs +39 -9
  249. package/src/runtime/shared/connector-catalog.d.mts +1 -2
  250. package/src/runtime/shared/connector-catalog.mjs +2 -4
  251. package/src/runtime/shared/connector-triggers.mjs +2 -0
  252. package/src/runtime/shared/defaults-separation.mjs +6 -2
  253. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  254. package/src/runtime/shared/err-text.mjs +7 -1
  255. package/src/runtime/shared/error-presentation.mjs +113 -10
  256. package/src/runtime/shared/file-has-data.mjs +28 -0
  257. package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
  258. package/src/runtime/shared/handoff-artifacts.mjs +61 -0
  259. package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
  260. package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
  261. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  262. package/src/runtime/shared/model-route-fallback.mjs +79 -7
  263. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  264. package/src/runtime/shared/plain-preview.mjs +22 -17
  265. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  266. package/src/runtime/shared/projects.mjs +50 -41
  267. package/src/runtime/shared/provider-accounts.mjs +17 -12
  268. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  269. package/src/runtime/shared/routine-trigger.mjs +14 -6
  270. package/src/runtime/shared/run-child.d.mts +3 -0
  271. package/src/runtime/shared/run-child.mjs +24 -3
  272. package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
  273. package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
  274. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  275. package/src/runtime/shared/singleton-owner.mjs +7 -6
  276. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  277. package/src/runtime/shared/staged-update.mjs +4 -2
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  279. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  280. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  281. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  282. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  283. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  284. package/src/runtime/shared/usage-readout-totals.mjs +15 -13
  285. package/src/runtime/shared/user-cwd.mjs +9 -4
  286. package/src/runtime/shared/user-data-guard.mjs +46 -44
  287. package/src/runtime/shared/utf8-cut.mjs +10 -0
  288. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  289. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  290. package/src/runtime/tidy/core-install.mjs +26 -4
  291. package/src/runtime/tidy/install.mjs +91 -23
  292. package/src/runtime/web-search/lib/config.mjs +4 -16
  293. package/src/session-runtime/agent-home-delivery.mjs +859 -136
  294. package/src/session-runtime/agent-room-delivery.mjs +167 -19
  295. package/src/session-runtime/agent-task-sessions.mjs +831 -69
  296. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  297. package/src/session-runtime/boot/apis.mjs +58 -24
  298. package/src/session-runtime/boot/config.mjs +1 -0
  299. package/src/session-runtime/boot/facade.mjs +11 -1
  300. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  301. package/src/session-runtime/boot/providers.mjs +16 -6
  302. package/src/session-runtime/boot/shared.mjs +12 -6
  303. package/src/session-runtime/boot/tools.mjs +2 -2
  304. package/src/session-runtime/bot-status.mjs +432 -0
  305. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  306. package/src/session-runtime/config-lifecycle.mjs +78 -32
  307. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  308. package/src/session-runtime/handoff-attachments.mjs +593 -0
  309. package/src/session-runtime/held-requests.mjs +36 -33
  310. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  311. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  312. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +45 -6
  313. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  314. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  315. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  316. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  317. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  318. package/src/session-runtime/media-api.mjs +5 -2
  319. package/src/session-runtime/model-route/route-persist.mjs +2 -2
  320. package/src/session-runtime/model-route/route-tuning.mjs +11 -2
  321. package/src/session-runtime/model-route/set-route.mjs +82 -2
  322. package/src/session-runtime/native-web-search.mjs +2 -2
  323. package/src/session-runtime/new-session-config.mjs +6 -1
  324. package/src/session-runtime/owned-automation-run.mjs +8 -1
  325. package/src/session-runtime/room-member-gate.mjs +16 -3
  326. package/src/session-runtime/route-resolution.mjs +21 -19
  327. package/src/session-runtime/route-state.mjs +1 -59
  328. package/src/session-runtime/routine-home-turn.mjs +5 -3
  329. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  330. package/src/session-runtime/runtime-paths.mjs +11 -5
  331. package/src/session-runtime/runtime-tunables.mjs +6 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  336. package/src/session-runtime/services/agent-tool/helpers.mjs +24 -5
  337. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  338. package/src/session-runtime/services/agent-tool/send-flow.mjs +36 -24
  339. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  340. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  341. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +12 -6
  342. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
  343. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  344. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  345. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  346. package/src/session-runtime/services/agent-tool.mjs +6 -0
  347. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  348. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  349. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  350. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  351. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  352. package/src/session-runtime/services/provider-admin.mjs +14 -4
  353. package/src/session-runtime/services/schedule-access.mjs +33 -4
  354. package/src/session-runtime/services/seeds.mjs +4 -3
  355. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  356. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  357. package/src/session-runtime/session-create/materialize.mjs +2 -3
  358. package/src/session-runtime/session-create/session-options.mjs +15 -2
  359. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  360. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  361. package/src/session-runtime/setup-tool/executor.mjs +20 -3
  362. package/src/session-runtime/setup-tool/extended-actions.mjs +19 -7
  363. package/src/session-runtime/setup-tool/settings-contract.mjs +37 -1
  364. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  365. package/src/session-runtime/skills-api.mjs +12 -5
  366. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  367. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  368. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  369. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  370. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  371. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  372. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  373. package/src/session-runtime/tool-policy-refresh.mjs +47 -358
  374. package/src/session-runtime/tool-surface.mjs +5 -3
  375. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +99 -81
  376. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  377. package/src/session-runtime/turn/turn-run.mjs +2 -1
  378. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  379. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +578 -301
  380. package/src/session-runtime/workflow-agents-api/agent-route.mjs +113 -53
  381. package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
  382. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  383. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  384. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  385. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  386. package/src/standalone/daemon-agent-control.mjs +4 -1
  387. package/src/standalone/daemon-session-control.mjs +60 -11
  388. package/src/standalone/daemon.mjs +34 -4
  389. package/src/standalone/local-session-runtime.mjs +22 -14
  390. package/src/standalone/session-client.mjs +7 -2
  391. package/src/standalone/session-protocol.mjs +7 -0
  392. package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
  393. package/src/standalone/session-service/agent-tree.mjs +4 -3
  394. package/src/standalone/session-service/session-calls/session-turns.mjs +19 -0
  395. package/src/standalone/session-service.mjs +11 -0
  396. package/src/standalone/session-state-projection.mjs +19 -9
  397. package/src/standalone/session-tool-output.mjs +35 -9
  398. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  399. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  400. package/src/tui/app/slash-commands.mjs +1 -1
  401. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  402. package/src/tui/dist/index.mjs +599 -342
  403. package/src/tui/prompt-history-store.mjs +16 -21
  404. package/src/tui/session/context-state.mjs +4 -0
  405. package/src/tui/session/notice-surface.mjs +3 -4
  406. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  407. package/src/tui/session/session-api/media.mjs +2 -1
  408. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  409. package/src/tui/session/session-api/routes-write.mjs +203 -0
  410. package/src/tui/session/session-api/routes.mjs +23 -236
  411. package/src/tui/session/session-api/settings.mjs +14 -1
  412. package/src/tui/session/session-flow/drain.mjs +28 -3
  413. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  414. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  415. package/src/tui/session/session-flow/steering.mjs +8 -3
  416. package/src/tui/session/tool-approval.mjs +185 -20
  417. package/src/tui/session/transcript-intake.mjs +4 -0
  418. package/src/tui/session/turn-live-call.mjs +11 -3
  419. package/src/tui/session/turn.mjs +36 -17
  420. package/src/tui/transcript-tool-failures.mjs +8 -2
  421. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  422. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  423. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -0,0 +1,54 @@
1
+ // Which Lead sessions still wait on agent work they handed off: the rule the
2
+ // desktop turn notifier applies to the agent pool (apps/desktop
3
+ // shared/agent-activity.ts isActiveDesktopAgentEntry /
4
+ // isCancelUnconfirmedDesktopAgentEntry, main/push-turn-events.ts
5
+ // sessionsWithPendingWork), ported for the runtime so a bot's task-session
6
+ // report waits for the same final answer the desktop notifies. Pool rows are
7
+ // listStoredAgentWorkersAsync rows (`sessionId`, root `ownerSessionId`,
8
+ // `status`, `stage`); nested descendants keep the Lead as their owner.
9
+
10
+ const TERMINAL_STATUS = /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/i;
11
+ const ACTIVE_STATUS =
12
+ /^(?:connecting|requesting|streaming|tool[-_\s]?running|running|queued|pending|starting|resource_wait)$/i;
13
+ const CANCELLED_STATUS = /cancel|killed|aborted|interrupted/i;
14
+ const CANCEL_UNCONFIRMED_STATUS = /cancel[-_\s]?(?:unconfirmed|pending)|cancell?ing/i;
15
+
16
+ const record = (value) => (value && typeof value === 'object' && !Array.isArray(value) ? value : {});
17
+
18
+ function statusValues(value) {
19
+ const entry = record(value);
20
+ return [entry.stage, entry.status]
21
+ .map((status) => String(status || '').trim())
22
+ .filter((status, index, values) => Boolean(status) && values.indexOf(status) === index);
23
+ }
24
+
25
+ /** A running or queued row: `status` decides, `stage` only for a row without one. */
26
+ function isActiveAgentRow(value) {
27
+ const statuses = statusValues(value);
28
+ if (statuses.length === 0 || statuses.some((status) => CANCELLED_STATUS.test(status))) return false;
29
+ const lifecycle = String(record(value).status || '').trim() || statuses[0];
30
+ return !TERMINAL_STATUS.test(lifecycle) && ACTIVE_STATUS.test(lifecycle);
31
+ }
32
+
33
+ /** A cancel whose stop is not proven yet. */
34
+ function isCancelUnconfirmedAgentRow(value) {
35
+ return statusValues(value).some((status) => CANCEL_UNCONFIRMED_STATUS.test(status));
36
+ }
37
+
38
+ /** Agent work still in flight on one pool row. */
39
+ export function hasPendingAgentWork(value) {
40
+ return isActiveAgentRow(value) || isCancelUnconfirmedAgentRow(value);
41
+ }
42
+
43
+ /** The owner (Lead) session ids with a child row still in flight. The Lead's
44
+ * own row is skipped: its turn state comes from the session itself. */
45
+ export function sessionsWithPendingWork(rows) {
46
+ const pending = new Set();
47
+ for (const row of Array.isArray(rows) ? rows : []) {
48
+ const sessionId = String(row?.sessionId || '');
49
+ const ownerSessionId = String(row?.ownerSessionId || '') || sessionId;
50
+ if (!ownerSessionId || sessionId === ownerSessionId) continue;
51
+ if (hasPendingAgentWork(row)) pending.add(ownerSessionId);
52
+ }
53
+ return pending;
54
+ }
@@ -5,23 +5,28 @@
5
5
  /** `text` without markdown syntax, whitespace folded to single spaces. */
6
6
  export function plainPreviewText(text) {
7
7
  // HTML comments never show in markdown (a greeting's suggestions ride one).
8
- return String(text ?? '')
9
- .replace(/<!--[\s\S]*?(?:-->|$)/g, ' ')
10
- .replace(/^[ \t]*(`{3,}|~{3,})[^\n]*$/gm, ' ')
11
- .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
12
- .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
13
- .replace(/^[ \t]{0,3}#{1,6}[ \t]+/gm, '')
14
- .replace(/^[ \t]*>[ \t]?/gm, '')
15
- .replace(/^[ \t]*(?:[-*+]|\d+[.)])[ \t]+(?:\[[ xX]\][ \t]+)?/gm, '')
16
- .replace(/^[ \t]*(?:[-*_][ \t]*){3,}$/gm, ' ')
17
- .replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2')
18
- .replace(/(^|[^\w*])\*(?=\S)([^*\n]*?\S)\*(?!\w)/g, '$1$2')
19
- .replace(/(^|[^\w_])_(?=\S)([^_\n]*?\S)_(?!\w)/g, '$1$2')
20
- .replace(/~~(?=\S)([\s\S]*?\S)~~/g, '$1')
21
- .replace(/`+([^`\n]*)`+/g, '$1')
22
- .replace(/\s+/g, ' ')
23
- .trim()
24
- .slice(0, PREVIEW_CAP);
8
+ return (
9
+ String(text ?? '')
10
+ .replace(/<!--[\s\S]*?(?:-->|$)/g, ' ')
11
+ .replace(/^[ \t]*(`{3,}|~{3,})[^\n]*$/gm, ' ')
12
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1')
13
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
14
+ .replace(/^[ \t]{0,3}#{1,6}[ \t]+/gm, '')
15
+ .replace(/^[ \t]*>[ \t]?/gm, '')
16
+ .replace(/^[ \t]*(?:[-*+]|\d+[.)])[ \t]+(?:\[[ xX]\][ \t]+)?/gm, '')
17
+ .replace(/^[ \t]*(?:[-*_][ \t]*){3,}$/gm, ' ')
18
+ .replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, '$2')
19
+ .replace(/(^|[^\w*])\*(?=\S)([^*\n]*?\S)\*(?!\w)/g, '$1$2')
20
+ .replace(/(^|[^\w_])_(?=\S)([^_\n]*?\S)_(?!\w)/g, '$1$2')
21
+ .replace(/~~(?=\S)([\s\S]*?\S)~~/g, '$1')
22
+ .replace(/`+([^`\n]*)`+/g, '$1')
23
+ .replace(/\s+/g, ' ')
24
+ // A summary stored before newlines were kept already folded its list: a
25
+ // dash or bullet right after a sentence end is a marker, not a hyphen.
26
+ .replace(/([.!?。…::])\s[-*+•]\s+(?=\S)/g, '$1 ')
27
+ .trim()
28
+ .slice(0, PREVIEW_CAP)
29
+ );
25
30
  }
26
31
 
27
32
  /** A safety cap only: the row clamps the shown text to two lines. */
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  closeSync,
3
3
  existsSync,
4
- fsyncSync,
5
4
  mkdirSync,
6
5
  openSync,
7
6
  readFileSync,
@@ -15,7 +14,7 @@ import {
15
14
  import { join } from 'node:path';
16
15
  import { execFile } from 'node:child_process';
17
16
  import { isDeepStrictEqual, promisify } from 'node:util';
18
- import { withFileLock, withFileLockSync } from './atomic-file.mjs';
17
+ import { withFileLock, withFileLockSync, writeJsonAtomicSync } from './atomic-file.mjs';
19
18
  import { resolvePluginData } from './plugin-paths.mjs';
20
19
 
21
20
  const execFileAsync = promisify(execFile);
@@ -338,24 +337,21 @@ function markerOwned(active) {
338
337
  function writeMarker(active) {
339
338
  try {
340
339
  mkdirSync(active.markerDir, { recursive: true, mode: 0o700 });
341
- writeFileSync(
340
+ // Flushed through the handle that wrote it: reopening the file read-only to
341
+ // fsync it is refused on Windows (EPERM), which reported a failure after
342
+ // the marker was already written.
343
+ writeJsonAtomicSync(
342
344
  active.markerPath,
343
- `${JSON.stringify({
345
+ {
344
346
  version: 1,
345
347
  timestamp: new Date().toISOString(),
346
348
  pid: process.pid,
347
349
  ppid: process.ppid,
348
350
  token: active.token,
349
351
  processIdentity: active.processIdentity,
350
- })}\n`,
351
- { encoding: 'utf8', mode: 0o600 }
352
+ },
353
+ { compact: true, mode: 0o600 }
352
354
  );
353
- const fd = openSync(active.markerPath, 'r');
354
- try {
355
- fsyncSync(fd);
356
- } finally {
357
- closeSync(fd);
358
- }
359
355
  return true;
360
356
  } catch {
361
357
  return false;
@@ -8,9 +8,9 @@
8
8
  * Data shape: { projects: [{ name, path, addedAt, lastSelectedAt? }] }
9
9
  */
10
10
  import { homedir } from 'node:os';
11
- import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
11
+ import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
12
12
  import { basename, isAbsolute, join, resolve } from 'node:path';
13
- import { withFileLockSync, writeJsonAtomicSync } from './atomic-file.mjs';
13
+ import { holdsNoData, readJsonStateSync, updateJsonAtomicSync, writeFileAtomicSync } from './atomic-file.mjs';
14
14
 
15
15
  const MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
16
16
  const PROJECTS_FILE = process.env.MIXDOG_PROJECTS_FILE
@@ -30,47 +30,57 @@ function normalizeKey(absPath) {
30
30
  return process.platform === 'win32' ? text.replace(/[\\/]+$/, '').toLowerCase() : text.replace(/\/+$/, '');
31
31
  }
32
32
 
33
+ // The registry document as a store. A strict read (permission checks and
34
+ // mutations) drops entries without an absolute path: they can never grant
35
+ // anything, and the replaced file survives as projects.json.bak.
36
+ function normalizeStore(parsed, strict) {
37
+ const projects = Array.isArray(parsed?.projects) ? parsed.projects : [];
38
+ return {
39
+ projects: projects
40
+ .filter(
41
+ (entry) => entry && typeof entry.path === 'string' && entry.path.trim() && (!strict || isAbsolute(entry.path))
42
+ )
43
+ .map((entry) => ({
44
+ name: String(entry.name || basename(entry.path) || entry.path),
45
+ path: String(entry.path),
46
+ addedAt: Number(entry.addedAt) || 0,
47
+ ...(Number(entry.lastSelectedAt) > 0 ? { lastSelectedAt: Number(entry.lastSelectedAt) } : {}),
48
+ })),
49
+ };
50
+ }
51
+
52
+ // A damaged registry file (NUL, truncated, malformed) is kept aside as
53
+ // projects.json.corrupt-<ts> and the last good projects.json.bak is restored
54
+ // when there is one, else the registry starts empty: a damaged file never
55
+ // blocks adding, removing or selecting a project. Strict callers still see a
56
+ // real I/O failure (EACCES, EIO) as an error — an unreadable registry is not
57
+ // an empty one; the tolerant listing reads it as empty.
33
58
  function readStore(strict = false) {
34
59
  try {
35
- if (!strict && !existsSync(PROJECTS_FILE)) return { projects: [] };
36
- const raw = readFileSync(PROJECTS_FILE, 'utf8');
37
- const parsed = JSON.parse(raw);
38
- if (
39
- strict &&
40
- (!Array.isArray(parsed?.projects) ||
41
- parsed.projects.some(
42
- (entry) => !entry || typeof entry.path !== 'string' || !entry.path.trim() || !isAbsolute(entry.path)
43
- ))
44
- )
45
- throw new TypeError('Project registry is invalid.');
46
- const projects = Array.isArray(parsed?.projects) ? parsed.projects : [];
47
- return {
48
- projects: projects
49
- .filter((entry) => entry && typeof entry.path === 'string' && entry.path.trim())
50
- .map((entry) => ({
51
- name: String(entry.name || basename(entry.path) || entry.path),
52
- path: String(entry.path),
53
- addedAt: Number(entry.addedAt) || 0,
54
- ...(Number(entry.lastSelectedAt) > 0 ? { lastSelectedAt: Number(entry.lastSelectedAt) } : {}),
55
- })),
56
- };
60
+ return normalizeStore(readJsonStateSync(PROJECTS_FILE), strict);
57
61
  } catch (error) {
58
- if (strict && error?.code !== 'ENOENT') throw error;
62
+ if (strict) throw error;
59
63
  return { projects: [] };
60
64
  }
61
65
  }
62
66
 
63
67
  // Serialize the entire read/modify/write across cooperating processes, not
64
- // just publication. Unlike the generic JSON updater, a corrupt registry must
65
- // never be repaired into an empty document. Preserve synchronous return values
66
- // and no-op behavior; failed reads, lock acquisition and writes propagate.
68
+ // just publication: the file is read under the registry lock. Preserve
69
+ // synchronous return values and no-op behavior (an unchanged store is not
70
+ // written); failed reads, lock acquisition and writes propagate. Each write
71
+ // keeps the replaced version as projects.json.bak.
67
72
  function mutateStore(mutator) {
68
- return withFileLockSync(`${PROJECTS_FILE}.lock`, () => {
69
- const store = readStore(true);
70
- const { value, changed } = mutator(store);
71
- if (changed) writeJsonAtomicSync(PROJECTS_FILE, store, { lock: false });
72
- return value;
73
- });
73
+ let outcome;
74
+ updateJsonAtomicSync(
75
+ PROJECTS_FILE,
76
+ (current) => {
77
+ const store = normalizeStore(current, true);
78
+ outcome = mutator(store);
79
+ return outcome.changed ? store : undefined;
80
+ },
81
+ { backup: true }
82
+ );
83
+ return outcome.value;
74
84
  }
75
85
 
76
86
  /**
@@ -78,7 +88,8 @@ function mutateStore(mutator) {
78
88
  * selection store and memory project_id classification (resolveProjectId)
79
89
  * agree. Rules:
80
90
  * - Never overwrite an existing marker (user may have set 'common' or a
81
- * custom id — respect it).
91
+ * custom id — respect it). A marker holding no data (empty or NUL, what a
92
+ * crash leaves) is no id: it is rewritten.
82
93
  * - Only act when absPath is a real directory on disk; never create the
83
94
  * project folder itself (addProject does NOT create the directory).
84
95
  * - Marker value = provided name, else basename(absPath). Skip when the
@@ -97,14 +108,12 @@ function ensureProjectIdMarker(absPath, name) {
97
108
  return; // missing / unstattable → silently skip
98
109
  }
99
110
  const markerPath = join(absPath, '.mixdog', 'project.id');
100
- if (existsSync(markerPath)) return; // preserve existing value
111
+ const present = existsSync(markerPath);
112
+ if (present && !holdsNoData(readFileSync(markerPath))) return; // preserve existing value
101
113
  const value = String(name || basename(absPath) || '').trim();
102
114
  if (!value || value.toLowerCase() === 'common') return;
103
- const markerDir = join(absPath, '.mixdog');
104
- mkdirSync(markerDir, { recursive: true });
105
- const tmp = `${markerPath}.${process.pid}.tmp`;
106
- writeFileSync(tmp, `${value}\n`, 'utf8');
107
- renameSync(tmp, markerPath);
115
+ // A missing marker is created only if still missing (a concurrent creator's wins).
116
+ writeFileAtomicSync(markerPath, `${value}\n`, { createOnly: !present });
108
117
  } catch {
109
118
  /* best-effort marker; never throw */
110
119
  }
@@ -1,7 +1,6 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
- import { readFileSync } from 'node:fs';
3
2
  import { join } from 'node:path';
4
- import { parseJsonText, updateJsonAtomic } from './atomic-file.mjs';
3
+ import { readJsonStateSync, unsupportedState, updateJsonAtomic } from './atomic-file.mjs';
5
4
  import { resolvePluginData } from './plugin-paths.mjs';
6
5
 
7
6
  export const ACCOUNT_PROVIDERS = Object.freeze([
@@ -34,12 +33,19 @@ function requireAccountId(id) {
34
33
 
35
34
  function validate(raw) {
36
35
  if (raw == null) return { version: 1, providers: {} };
36
+ // A newer build's roster is left in place: reads see no accounts and writes
37
+ // refuse, so a downgrade never discards it.
38
+ if (Number.isSafeInteger(raw.version) && raw.version > 1) {
39
+ throw unsupportedState(`Provider account settings version ${raw.version} is newer than this build.`);
40
+ }
37
41
  if (raw.version !== 1 || !raw.providers || typeof raw.providers !== 'object') {
38
42
  throw new Error('Provider account settings are unreadable.');
39
43
  }
40
44
  for (const [provider, pool] of Object.entries(raw.providers)) {
41
- requireAccountProvider(provider);
42
- if (!Array.isArray(pool.accounts) || pool.accounts.length > MAX_ACCOUNTS) {
45
+ // A provider a newer build added is carried as is: this build never reads
46
+ // or writes its pool, and treating it as damage would discard it.
47
+ if (!ACCOUNT_PROVIDERS.includes(provider)) continue;
48
+ if (!Array.isArray(pool?.accounts) || pool.accounts.length > MAX_ACCOUNTS) {
43
49
  throw new Error('Invalid provider account list.');
44
50
  }
45
51
  const ids = pool.accounts.map((row) => requireAccountId(row.id));
@@ -48,13 +54,11 @@ function validate(raw) {
48
54
  return raw;
49
55
  }
50
56
 
57
+ // Every OAuth provider's init reads the roster, so a damaged one (crash
58
+ // leftovers, truncation, a shape this build rejects) must never throw: it is
59
+ // kept aside and the last good copy, which every write keeps, is restored.
51
60
  function read(dataDir) {
52
- try {
53
- return validate(parseJsonText(readFileSync(file(dataDir), 'utf8')));
54
- } catch (error) {
55
- if (error.code === 'ENOENT') return validate(null);
56
- throw error;
57
- }
61
+ return readJsonStateSync(file(dataDir), { validate, fallback: validate(null), secret: true });
58
62
  }
59
63
 
60
64
  // Labels an earlier build assigned automatically. They read as a different
@@ -106,7 +110,8 @@ export function readProviderAccountPool(provider, dataDir = resolvePluginData())
106
110
 
107
111
  async function update(provider, mutate) {
108
112
  requireAccountProvider(provider);
109
- // Read failures must not be interpreted as an empty account roster.
113
+ // A roster that parses yet fails validation is recovered here first; the
114
+ // mutator's validate would otherwise refuse every write. I/O failures throw.
110
115
  read();
111
116
  const result = await updateJsonAtomic(
112
117
  file(),
@@ -117,7 +122,7 @@ async function update(provider, mutate) {
117
122
  state.providers[provider] = pool;
118
123
  return validate(state);
119
124
  },
120
- { mode: 0o600, secret: true, fsyncDir: true }
125
+ { mode: 0o600, secret: true, fsyncDir: true, backup: true }
121
126
  );
122
127
  return normalizePool(result.providers[provider]);
123
128
  }
@@ -4,7 +4,12 @@ export interface RoutineTrigger {
4
4
  event?: string;
5
5
  }
6
6
 
7
- export declare function routineTriggerHeader(input: { kind: string; name: string; project?: string }): string;
7
+ export declare function routineTriggerHeader(input: {
8
+ kind: string;
9
+ name: string;
10
+ project?: string;
11
+ now?: Date | number;
12
+ }): string;
8
13
  export declare function stripRoutineTriggerHeader(text: unknown): string;
9
14
  export declare function normalizeRoutineTrigger(value: unknown): RoutineTrigger | null;
10
15
  export declare function routineTriggerField(value: unknown): { routineTrigger?: RoutineTrigger };
@@ -4,18 +4,26 @@
4
4
  // carries (live, persisted in meta.transcript, restored) so a chat paints it
5
5
  // as a routine marker instead of a user bubble.
6
6
 
7
+ import { localTimestampParts } from './time-format.mjs';
8
+
7
9
  const HEADER_PREFIX = '[Routine trigger]';
8
10
  const KINDS = new Set(['schedule', 'webhook']);
9
11
 
10
- /** The first line of a routine turn's prompt: which routine fired and how to
11
- * treat the turn. Only the turn's content carries it, never the system
12
- * prompt, so the thread's prompt cache is untouched. */
13
- export function routineTriggerHeader({ kind, name, project = '' }) {
12
+ /** The first line of a routine turn's prompt: which routine fired, when (local
13
+ * time and zone: the turn gets no other date), and how to treat the turn.
14
+ * It is the routine's only conduct text. Only the turn's content carries it,
15
+ * never the system prompt, so the thread's prompt cache is untouched. */
16
+ export function routineTriggerHeader({ kind, name, project = '', now = new Date() }) {
14
17
  const what = kind === 'webhook' ? 'webhook' : 'schedule';
18
+ const at = localTimestampParts(now);
15
19
  return [
16
20
  `${HEADER_PREFIX} Your ${what} "${String(name)}" fired.`,
17
- 'Carry out its instructions below now, yourself or by delegating; nobody is watching this turn, so approvals wait for the user.',
18
- 'Reply in this chat with the result.',
21
+ ...(at ? [`Local time: ${at.date} ${at.time.slice(0, 5)} ${at.timeZone} (${at.offset}).`] : []),
22
+ 'Carry out its instructions below now, yourself or by delegating.',
23
+ 'Nobody is watching this turn and nothing asks for approval: act within your permissions.',
24
+ 'Payments and permanent deletes are refused here: report that and suggest doing it from the chat.',
25
+ 'If a sign-in, code or CAPTCHA blocks you, stop and say which site needs the user.',
26
+ 'Reply in this chat in plain words, without tool names; if you handed work off, say it started — its result arrives here later.',
19
27
  ...(project ? [`Project folder: ${project}`] : []),
20
28
  ].join(' ');
21
29
  }
@@ -0,0 +1,3 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+
3
+ export function killChildTree(child: ChildProcess): void;
@@ -8,6 +8,29 @@
8
8
  // maxBytes, or the spawn error itself.
9
9
  import { spawn } from 'node:child_process';
10
10
 
11
+ /** Kills `child` with everything it started. On Windows a plain kill ends only
12
+ * the child itself: `git` there is a launcher whose git.exe (and the alias,
13
+ * hook or filter it runs) would live on, still holding the output pipes. */
14
+ export function killChildTree(child) {
15
+ if (process.platform === 'win32' && child.pid && child.exitCode === null) {
16
+ const taskkill = `${process.env.SystemRoot || 'C:\\Windows'}\\System32\\taskkill.exe`;
17
+ const fallback = () => {
18
+ try {
19
+ child.kill();
20
+ } catch {}
21
+ };
22
+ spawn(taskkill, ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true })
23
+ .once('error', fallback)
24
+ .once('exit', (code) => {
25
+ if (code !== 0) fallback();
26
+ });
27
+ return;
28
+ }
29
+ try {
30
+ child.kill();
31
+ } catch {}
32
+ }
33
+
11
34
  export function runChild({ command, args, cwd, input, env, timeoutMs, maxBytes, stdoutEncoding = 'utf8' }) {
12
35
  return new Promise((resolvePromise, rejectPromise) => {
13
36
  const hasInput = (typeof input === 'string' || ArrayBuffer.isView(input)) && input.length > 0;
@@ -35,9 +58,7 @@ export function runChild({ command, args, cwd, input, env, timeoutMs, maxBytes,
35
58
  ? setTimeout(() => {
36
59
  const error = new Error(`${command} ${args.join(' ')} timed out after ${timeoutMs}ms`);
37
60
  error.code = 'ETIMEDOUT';
38
- try {
39
- child.kill();
40
- } catch {}
61
+ killChildTree(child);
41
62
  finish(error);
42
63
  }, timeoutMs)
43
64
  : null;
@@ -0,0 +1,15 @@
1
+ export interface SensitiveScope {
2
+ root: string[];
3
+ /** Subfolders of the root that stay reachable. */
4
+ except: readonly string[];
5
+ /** Whole tree (a registered private root) or Mixdog's secret subset. */
6
+ whole: boolean;
7
+ }
8
+ export function sensitivePathSegments(path: string): string[];
9
+ export function matchesSensitivePath(
10
+ path: string,
11
+ within?: readonly SensitiveScope[],
12
+ options?: { envTemplates?: boolean }
13
+ ): boolean;
14
+ export function mixdogSensitiveScopes(extraData?: readonly string[]): SensitiveScope[];
15
+ export function isSensitiveLocalPath(path: string, options?: { extraData?: readonly string[] }): boolean;