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
@@ -62,9 +62,9 @@ const identityKey = (entry) => `${entry.pid}:${entry.token ?? ''}`;
62
62
  * leave the process group). `observe()` takes one strict table read and
63
63
  * answers 'running', 'gone' or 'unknown':
64
64
  * - the root is alive (same identity) → running
65
- * - no complete pre-exit capture exists → unknown
66
65
  * - a tracked identity, a descendant still linked to one,
67
66
  * or (POSIX) a member of the command's group is alive → running
67
+ * - no complete pre-exit capture exists → unknown
68
68
  * - a tracked process without a creation token is present → unknown
69
69
  * A failed table read rejects; callers treat it as unknown. */
70
70
  export function createProcessTreeTracker({ pid, members = [], exitObserved = false } = {}) {
@@ -137,9 +137,9 @@ export function createProcessTreeTracker({ pid, members = [], exitObserved = fal
137
137
  /** Pre-termination snapshot; call while the root still runs. */
138
138
  async capture({ rootExited = false } = {}) {
139
139
  if (rootPid <= 0) return false;
140
- let table = await readProcessTable({ shared: true });
141
- // A shared read may have begun before this root was spawned.
142
- if (!table.tokenOf.has(rootPid)) table = await readProcessTable();
140
+ // Even if an in-flight read saw the root, children may have appeared
141
+ // since it began. Join the next fresh read before parent links break.
142
+ const table = await readProcessTable();
143
143
  absorb(table, rootIsAlive(table, rootExited));
144
144
  return complete;
145
145
  },
@@ -159,7 +159,6 @@ export function createProcessTreeTracker({ pid, members = [], exitObserved = fal
159
159
  live = [{ pid: rootPid, token: rootToken, name: table.nameOf.get(rootPid) || '' }];
160
160
  return 'running';
161
161
  }
162
- if (!complete) return 'unknown';
163
162
  let unknown = false;
164
163
  for (const entry of tracked.values()) {
165
164
  const state = present(table, entry);
@@ -167,7 +166,9 @@ export function createProcessTreeTracker({ pid, members = [], exitObserved = fal
167
166
  else if (state === null) unknown = true;
168
167
  }
169
168
  if (live.length > 0) return 'running';
170
- return unknown ? 'unknown' : 'gone';
169
+ // Known survivors can still be swept after an incomplete capture, but
170
+ // their absence cannot prove that an unobserved descendant is gone.
171
+ return !complete || unknown ? 'unknown' : 'gone';
171
172
  },
172
173
  };
173
174
  }
@@ -196,21 +197,13 @@ function sweepLiveIdentities(tracker) {
196
197
  * `sweep` (a killed tree): a pass that finds captured identities alive
197
198
  * terminates them and observes again in the same pass, so a cancellation
198
199
  * ends with the tree gone instead of a held folder over live processes.
199
- *
200
- * `ownerClosed`: once the owning session has closed and the root has
201
- * exited, a tree without a complete pre-kill snapshot is observed after
202
- * exit (what is still linked to the root) instead of answering 'unknown'
203
- * forever — no one is left to release it, and the close must end in a
204
- * definitive state. `onSurvivors(pids)` reports the pids a pass that is
205
- * not 'gone' found alive, whenever that set changes, so out-of-process
206
- * readers can tell a live hold from a stale one. */
200
+ * A missing pre-kill capture stays unknown even if the owning session
201
+ * closes: its lifetime is not evidence that every descendant has exited. */
207
202
  export function watchTermination({
208
203
  tracker,
209
204
  rootExited = () => true,
210
205
  onTerminated,
211
206
  onUnconfirmed = null,
212
- onSurvivors = null,
213
- ownerClosed = null,
214
207
  releaseRequested = null,
215
208
  intervalMs = TERMINATION_RECHECK_MS,
216
209
  sweep = false,
@@ -218,7 +211,6 @@ export function watchTermination({
218
211
  let timer = null;
219
212
  let stopped = false;
220
213
  let reportedUnconfirmed = false;
221
- let survivorKey = null;
222
214
  const stop = () => {
223
215
  stopped = true;
224
216
  if (timer) clearTimeout(timer);
@@ -234,7 +226,6 @@ export function watchTermination({
234
226
  if (releaseRequested?.()) state = 'gone';
235
227
  else
236
228
  try {
237
- if (!tracker.complete && rootExited() && ownerClosed?.()) tracker.acceptExitObservation();
238
229
  observation = tracker.observe({ rootExited: rootExited() });
239
230
  observation.catch(() => {});
240
231
  // A pass that has not answered by the next cadence tick is unknown.
@@ -262,18 +253,6 @@ export function watchTermination({
262
253
  /* presentation only */
263
254
  }
264
255
  }
265
- if (state !== 'gone' && onSurvivors) {
266
- const pids = tracker.live.map((entry) => entry.pid);
267
- const key = pids.join(',');
268
- if (key !== survivorKey) {
269
- survivorKey = key;
270
- try {
271
- onSurvivors(pids);
272
- } catch {
273
- /* presentation only */
274
- }
275
- }
276
- }
277
256
  if (state === 'gone') {
278
257
  try {
279
258
  await onTerminated();
@@ -8,8 +8,6 @@ import { unregisterOwnedShellTree } from './owned-shell-trees.mjs';
8
8
  import {
9
9
  adoptShellWorkspaceAdmission,
10
10
  clearTerminationHold,
11
- isShellOwnerSessionClosed,
12
- noteShellJobSurvivors,
13
11
  registerTerminationHold,
14
12
  takeReleaseRequest,
15
13
  } from '../builtin/lib/shell-job-records.mjs';
@@ -28,13 +26,24 @@ import {
28
26
  // it follows confirmed termination only (see below).
29
27
  const UNCONFIRMED_KILL_CLEANUP_CEILING_MS = 60_000;
30
28
 
29
+ /** Settles once a held run's whole tree is confirmed gone (or its hold was
30
+ * force-released): what its result left in use defers to this. */
31
+ function treeSettled(run) {
32
+ if (!run.treeSettled) {
33
+ run.treeSettled = new Promise((resolve) => {
34
+ run.resolveTreeSettled = resolve;
35
+ });
36
+ }
37
+ return run.treeSettled;
38
+ }
39
+
31
40
  /** Lifecycle completion of a run whose result is already reported: release
32
41
  * `run.workspaceAdmission` and retire the live record only once the root AND
33
42
  * every descendant identity captured before the kill are confirmed gone. One
34
43
  * observation loop at the termination cadence; running, unknown (incomplete
35
44
  * snapshot, failed table read) and a failing release all re-check, and no
36
45
  * timer ever releases on its own. */
37
- export function releaseAdmissionOnConfirmedTermination(run, { markNow = true } = {}) {
46
+ export function releaseAdmissionOnConfirmedTermination(run, { markNow = true, sweep = run.killed === true } = {}) {
38
47
  const { child } = run;
39
48
  if (!child || run.stopTerminationWatch) return;
40
49
  // Visible as "termination unconfirmed" until the lifecycle ends, with an
@@ -42,12 +51,14 @@ export function releaseAdmissionOnConfirmedTermination(run, { markNow = true } =
42
51
  // kill is marked at once; an owner-cancelled tree whose root is already
43
52
  // gone only once its first observation is not 'gone'.
44
53
  if (markNow) keepTerminationUnconfirmedRecord(run);
54
+ treeSettled(run);
45
55
  const release = () => {
46
56
  run.workspaceAdmission?.();
47
57
  run.workspaceAdmission = null;
48
58
  run.stopTerminationWatch?.();
49
59
  clearTerminationHold(run.foregroundRecordId);
50
60
  clearForegroundRecord(run);
61
+ run.resolveTreeSettled();
51
62
  };
52
63
  registerTerminationHold(run.foregroundRecordId, {
53
64
  ownerSessionId: run.foregroundRecordInput?.ownerSessionId || null,
@@ -59,11 +70,9 @@ export function releaseAdmissionOnConfirmedTermination(run, { markNow = true } =
59
70
  tracker: run.terminationTracker,
60
71
  rootExited: () => child.exitCode != null || child.signalCode != null,
61
72
  // A killed run is not over while processes it started still run.
62
- sweep: run.killed === true,
73
+ sweep,
63
74
  onTerminated: release,
64
75
  onUnconfirmed: markNow ? null : () => keepTerminationUnconfirmedRecord(run),
65
- onSurvivors: (pids) => void noteShellJobSurvivors(run.foregroundRecordId, pids),
66
- ownerClosed: () => isShellOwnerSessionClosed(run.foregroundRecordInput?.ownerSessionId),
67
76
  releaseRequested: () => takeReleaseRequest(run.foregroundRecordId),
68
77
  });
69
78
  }
@@ -202,17 +211,19 @@ export function createSettle(run, releasePromotedCapture) {
202
211
  // The test compares captured BYTES, not rendered UTF-16 length: a CJK
203
212
  // head+tail holds ~1/3 the char count of its byte size and under a
204
213
  // length test would delete the very file its own marker names.
214
+ const dropCapture =
215
+ !run.killUnconfirmed && taskOutput.spilled && taskOutput.totalDiskBytes() <= SHELL_OUTPUT_INLINE_CAP;
205
216
  if (run.killUnconfirmed) {
206
217
  // Never confirmed dead: deleting the capture files or handing back the
207
218
  // lease now would release resources a live process still owns. The
208
219
  // spilled paths therefore survive and travel with the result below, and
209
220
  // the workspace admission stays held until termination is confirmed.
210
221
  deferCleanupToChildExit(run);
211
- } else if (taskOutput.spilled && taskOutput.totalDiskBytes() <= SHELL_OUTPUT_INLINE_CAP) {
212
- taskOutput.deleteFiles();
213
- void releaseResourceLease(run);
214
222
  } else {
215
- taskOutput.closeFds();
223
+ const releaseCapture = () => (dropCapture ? taskOutput.deleteFiles() : taskOutput.closeFds());
224
+ // A captured descendant of a killed tree may still write the capture.
225
+ if (confirmKill) void treeSettled(run).then(releaseCapture);
226
+ else releaseCapture();
216
227
  void releaseResourceLease(run);
217
228
  }
218
229
  let descendants = null;
@@ -256,9 +267,9 @@ export function createSettle(run, releasePromotedCapture) {
256
267
  timedOut: run.timedOut,
257
268
  killed: run.killed,
258
269
  killCause: run.killCause,
259
- stdoutPath: taskOutput.spilled ? taskOutput.stdoutPath : null,
270
+ stdoutPath: taskOutput.spilled && !dropCapture ? taskOutput.stdoutPath : null,
260
271
  stdoutFileSize: taskOutput.stdoutFileSize,
261
- stderrPath: taskOutput.spilled ? taskOutput.stderrPath : null,
272
+ stderrPath: taskOutput.spilled && !dropCapture ? taskOutput.stderrPath : null,
262
273
  stderrFileSize: taskOutput.stderrFileSize,
263
274
  taskId: run.taskId,
264
275
  partialOutput: run.partialOutput,
@@ -266,6 +277,7 @@ export function createSettle(run, releasePromotedCapture) {
266
277
  failurePhase: run.failurePhase,
267
278
  failureReason: run.failureReason,
268
279
  descendants,
280
+ settled: run.treeSettled,
269
281
  })
270
282
  );
271
283
  };
@@ -289,8 +289,9 @@ export async function spawnShellChild(input) {
289
289
  if (!detail || !['completed', 'failed', 'cancelled'].includes(detail.nativeStatus)) {
290
290
  throw new Error('The child termination could not be confirmed; workspace admission is retained.');
291
291
  }
292
- input.run.workspaceAdmission?.();
293
- input.run.workspaceAdmission = null;
292
+ // The root is gone, but not necessarily what it started: the
293
+ // reservation follows the captured tree, swept like any killed one.
294
+ releaseAdmissionOnConfirmedTermination(input.run, { markNow: false, sweep: true });
294
295
  } catch (cleanupError) {
295
296
  input.run.killUnconfirmed = true;
296
297
  // No settle will ever run for this run; the reservation is released
@@ -104,8 +104,9 @@ export function createShellRun({ abortSignal, resolve, jobId = null }) {
104
104
  // `settled`: whichever transition wins first wins for good.
105
105
  autoBackgrounded: false,
106
106
  autoBackgroundJobId: null,
107
- // [exitCode, signal] of a 'close' that arrived while promotion was in
108
- // flight; a promotion that then fails settles the run with it.
107
+ // [exitCode, signal] of a 'close' (or an expired drain grace) that arrived
108
+ // while promotion was in flight; a promotion that then fails settles the
109
+ // run with it.
109
110
  promotionClose: null,
110
111
  autoBgTimer: null,
111
112
  killUnconfirmed: false,
@@ -125,6 +126,9 @@ export function createShellRun({ abortSignal, resolve, jobId = null }) {
125
126
  // observation loop that holds the workspace until they are gone.
126
127
  terminationTracker: null,
127
128
  stopTerminationWatch: null,
129
+ // Settles when that loop ends (lib/shell-run-settle.mjs treeSettled).
130
+ treeSettled: null,
131
+ resolveTreeSettled: null,
128
132
  };
129
133
  }
130
134
 
@@ -41,7 +41,8 @@ function quarantinePathFor(fullPath) {
41
41
  const dir = dirname(fullPath);
42
42
  const name = basename(fullPath).slice(0, 80);
43
43
  for (let attempt = 0; attempt < 8; attempt++) {
44
- const candidate = join(dir, `.${name}.mixdog-delete-${randomBytes(8).toString('hex')}`);
44
+ // No leading dot: sync-drive folders (e.g. Naver MYBOX) refuse dot-prefixed names.
45
+ const candidate = join(dir, `${name}.mixdog-delete-${randomBytes(8).toString('hex')}`);
45
46
  try {
46
47
  lstatSync(candidate);
47
48
  } catch (err) {
@@ -1,7 +1,8 @@
1
1
  // Patch replay capture: every apply_patch failure is frozen (args + target
2
2
  // file snapshots) for `npm run patch:replay`. Best-effort throughout — a
3
3
  // capture problem never changes the tool result.
4
- import { readdirSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
4
+ import { readdirSync, mkdirSync, rmSync } from 'node:fs';
5
+ import { writeJsonAtomicSync } from '../../../../shared/atomic-file.mjs';
5
6
  import { resolve as pathResolve, isAbsolute, join as pathJoin } from 'node:path';
6
7
  import {
7
8
  createSnapshotBudget,
@@ -203,7 +204,8 @@ export function maybeCapturePatchReplay(capture, errorText) {
203
204
  ? { file_snapshot_meta: capture.fileSnapshotMeta }
204
205
  : {}),
205
206
  };
206
- writeFileSync(pathJoin(dir, `${id}.json`), JSON.stringify(record, null, 2), { mode: 0o600 });
207
+ // Whole-file publication; `patch:replay` skips a record it cannot parse.
208
+ writeJsonAtomicSync(pathJoin(dir, `${id}.json`), record, { mode: 0o600 });
207
209
  // Retention: keep the newest 40 captures. The id prefix is Date.now() in
208
210
  // base36 (fixed width until ~2059), so a lexicographic sort is
209
211
  // chronological and the oldest records sort first.
@@ -8,7 +8,17 @@
8
8
  // { existed: true, content: null, omitted: true, size, mode }. Omitted is not
9
9
  // empty content — restore never writes it, and only reports it untouched when
10
10
  // the file provably still is.
11
- import { chmodSync, existsSync, statSync, writeFileSync, mkdirSync, rmSync } from 'node:fs';
11
+ import {
12
+ chmodSync,
13
+ closeSync,
14
+ existsSync,
15
+ fsyncSync,
16
+ mkdirSync,
17
+ openSync,
18
+ rmSync,
19
+ statSync,
20
+ writeFileSync,
21
+ } from 'node:fs';
12
22
  import { dirname as pathDirname } from 'node:path';
13
23
  import {
14
24
  createSnapshotBudget,
@@ -83,6 +93,27 @@ function omittedSnapshotIssue(snapshot) {
83
93
  return `${what}; the file changed and cannot be restored`;
84
94
  }
85
95
 
96
+ // Filesystems that refuse fsync answer with one of these; durability is
97
+ // best-effort there and must not fail the restore.
98
+ const FSYNC_TOLERATED = new Set(['EPERM', 'ENOTSUP', 'EINVAL']);
99
+
100
+ // The restore rewrites a file of the user's project in place: flush it through
101
+ // the handle that wrote it, like the write tool does, so a crash cannot leave
102
+ // the restored file with its size committed and every byte NUL.
103
+ function writeRestoredFile(path, content) {
104
+ const fd = openSync(path, 'w');
105
+ try {
106
+ writeFileSync(fd, content);
107
+ try {
108
+ fsyncSync(fd);
109
+ } catch (err) {
110
+ if (!FSYNC_TOLERATED.has(err?.code)) throw err;
111
+ }
112
+ } finally {
113
+ closeSync(fd);
114
+ }
115
+ }
116
+
86
117
  export function restorePatchRollbackState(snapshots, readStateScope) {
87
118
  const errors = [];
88
119
  const paths = [];
@@ -96,7 +127,7 @@ export function restorePatchRollbackState(snapshots, readStateScope) {
96
127
  try {
97
128
  if (snapshot.existed) {
98
129
  mkdirSync(pathDirname(snapshot.fullPath), { recursive: true });
99
- writeFileSync(snapshot.fullPath, snapshot.content);
130
+ writeRestoredFile(snapshot.fullPath, snapshot.content);
100
131
  if (snapshot.mode != null) chmodSync(snapshot.fullPath, snapshot.mode);
101
132
  } else if (existsSync(snapshot.fullPath)) {
102
133
  rmSync(snapshot.fullPath, { force: true });
@@ -1,30 +1,30 @@
1
1
  {
2
- "version": "0.7.33",
2
+ "version": "0.7.34",
3
3
  "_comment": "Rewritten by .github/workflows/patch-release.yml on each tagged release. assets maps platformKey (process.platform-process.arch, e.g. win32-x64, linux-x64, darwin-arm64) to { url, sha256 } of the mixdog-patch binary on the GitHub release. A local cargo build under native/mixdog-patch/target/release always takes precedence; otherwise the binary is fetched per this manifest into the data dir (apply is native-only — no JS apply engine).",
4
4
  "assets": {
5
5
  "darwin-arm64": {
6
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-darwin-arm64",
7
- "sha256": "c2cb3b4a7e519aa9ddf97537578cf31eae48c371453f46b4501f46b16311c0a5"
6
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-darwin-arm64",
7
+ "sha256": "b7e2bd6649ae64801d7f2720fea11fdd8e52ca2fcc21fedb717691cc6f8e6341"
8
8
  },
9
9
  "darwin-x64": {
10
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-darwin-x64",
11
- "sha256": "a4a138765dfc76b39fa3ea20638c0526eb857a19f6d8ea59b0e02f3f552bbaf8"
10
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-darwin-x64",
11
+ "sha256": "d0d11eda8b65ee34a3b510eb9f3a57970e977076cd9c8854bf336ec62e3f6d23"
12
12
  },
13
13
  "linux-arm64": {
14
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-linux-arm64",
15
- "sha256": "8ba081125ab7658a3353ced9dd487d393dc8b252ac98636da3b8745724798e68"
14
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-linux-arm64",
15
+ "sha256": "cb3a530dd68e18edef1cc01025ee89c1b4296f847b76a8f6ea8110710b66912e"
16
16
  },
17
17
  "linux-x64": {
18
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-linux-x64",
19
- "sha256": "bec7bdfc1129d5e99d15c7bc961a2b865868a68e0163a65b506be2aa70ad0bff"
18
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-linux-x64",
19
+ "sha256": "4743bb6b23db574f282df0a2744ca4b5b2a7e5049533cf00bc26b93ce235231c"
20
20
  },
21
21
  "win32-arm64": {
22
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-win32-arm64.exe",
23
- "sha256": "85374d85f5e966f9ba82f52ed58c799339d591a1afa32a4902ae0c8715506cb5"
22
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-win32-arm64.exe",
23
+ "sha256": "626496b0a2f804f769c20f8d1f6d35d9caab89b8ddb38c5e320ba1d146bf84f6"
24
24
  },
25
25
  "win32-x64": {
26
- "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.33/mixdog-patch-win32-x64.exe",
27
- "sha256": "b0826bb2e7462d072fdc2250fc42662d7da80589d0589a4c4781decc1fd5f008"
26
+ "url": "https://github.com/tribgames/mixdog/releases/download/patch-v0.7.34/mixdog-patch-win32-x64.exe",
27
+ "sha256": "03c73fe9895dad6684060e7b0155bb41a8dc96ad75c49c9c9b550cbec27afd1d"
28
28
  }
29
29
  }
30
30
  }
@@ -421,15 +421,17 @@ function attachChildExit(run, releasePromotedCapture) {
421
421
  child.once('exit', (code, signal) => {
422
422
  if (!run.rootExitAtMs) run.rootExitAtMs = Date.now();
423
423
  const grace = setTimeout(() => {
424
- if (run.settled) return;
424
+ if (run.settled || run.promotionClose) return;
425
+ run.partialOutput = true;
425
426
  if (run.autoBackgrounded) {
426
427
  // A promoted child whose 'close' never arrives (grandchild holding
427
428
  // the stdio) would otherwise keep its spill descriptors forever:
428
- // 'exit' is the only terminal event this path gets.
429
+ // 'exit' is the only terminal event this path gets. A promotion that
430
+ // is still pending and then fails settles the run with it.
431
+ run.promotionClose = [code == null ? 1 : code, signal];
429
432
  releasePromotedCapture();
430
433
  return;
431
434
  }
432
- run.partialOutput = true;
433
435
  run.settle(code == null ? 1 : code, signal);
434
436
  }, 3000);
435
437
  if (grace.unref) grace.unref();
@@ -637,7 +637,9 @@ export class ExecResult {
637
637
  this.backgrounded = opts.backgrounded === true;
638
638
  this.jobId = opts.jobId || null;
639
639
  // A command queued behind a saturated shell lane is no shell job; this
640
- // promise settles when the queued run finishes.
640
+ // promise settles when the queued run finishes. A foreground result
641
+ // reported while its tree may still run (a kill, unverified descendants)
642
+ // carries the promise that settles once that tree is gone.
641
643
  this.settled = opts.settled || null;
642
644
  this.backgroundMessage = opts.backgroundMessage || null;
643
645
  // Survivors observed AFTER the shell process exited (process group on
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Attachments sent by reference instead of inline.
3
+ *
4
+ * A PDF or image the user attached already lives in the content-addressed
5
+ * store on this machine. When inlining it would crowd the context window, the
6
+ * message carries a short text part instead: the file's name, size, page count
7
+ * and text-layer status, and the path the model reads it from — so the model
8
+ * pulls in only the pages the request needs (read with `pages`). The part keeps
9
+ * the attachment's `attachmentRef` (nested, so it is not itself a stored-text
10
+ * reference) and the store's census keeps the bytes as long as the message
11
+ * names them.
12
+ */
13
+ import { inlineFileKind } from '../shared/inline-file-kind.mjs';
14
+ import { attachmentFilePath, isAttachmentReference, storeInlineDocumentPart, storeInlineImagePart } from './store.mjs';
15
+
16
+ /** Up to this many pages per read call; the read tool enforces the same cap. */
17
+ export const READ_PDF_MAX_PAGES_PER_CALL = 20;
18
+
19
+ function sizeLabel(bytes) {
20
+ const value = Number(bytes) || 0;
21
+ if (value >= 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(1)} MB`;
22
+ if (value >= 1024) return `${Math.max(1, Math.round(value / 1024))} KB`;
23
+ return `${value} bytes`;
24
+ }
25
+
26
+ function quotedName(name, fallback) {
27
+ const base = String(name || '')
28
+ .split(/[\\/]/)
29
+ .pop()
30
+ .replace(/["\r\n]/g, '')
31
+ .trim();
32
+ return `"${base || fallback}"`;
33
+ }
34
+
35
+ /** How much of a PDF reads as text, in words the model can act on. */
36
+ export function textLayerLabel(pageCount, textPages) {
37
+ const pages = Number(pageCount);
38
+ const withText = Number(textPages);
39
+ if (!(pages > 0) || !Number.isFinite(withText) || withText < 0) return 'text layer not checked';
40
+ if (withText === 0) return 'no text layer (scanned pages)';
41
+ if (withText >= pages) return 'text layer on every page';
42
+ return `text layer on ${withText} of ${pages} pages; the rest are scanned`;
43
+ }
44
+
45
+ function pdfReferenceText(info, path) {
46
+ const plural = info.pageCount === 1 ? '' : 's';
47
+ const pages = info.pageCount > 0 ? `${info.pageCount} page${plural}, ` : '';
48
+ return [
49
+ `[Attachment kept on disk, not inlined: ${quotedName(info.filename, 'document.pdf')} ` +
50
+ `(PDF, ${pages}${textLayerLabel(info.pageCount, info.textPages)}, ${sizeLabel(info.sizeBytes)})`,
51
+ `Path: ${path}`,
52
+ `Read only what the request needs: read this path with pages, e.g. pages="1-5" (up to ` +
53
+ `${READ_PDF_MAX_PAGES_PER_CALL} pages per call). Pages with a text layer come back as text; ` +
54
+ 'scanned pages come back as page images, or as OCR text when the model is text-only.]',
55
+ ].join('\n');
56
+ }
57
+
58
+ function imageReferenceText(info, path) {
59
+ return [
60
+ `[Image kept on disk, not inlined: ${quotedName(info.filename, 'image')} ` +
61
+ `(${info.mimeType || 'image'}, ${sizeLabel(info.sizeBytes)})`,
62
+ `Path: ${path}`,
63
+ 'View it with read on this path when the request needs it.]',
64
+ ].join('\n');
65
+ }
66
+
67
+ // The stored form of a media part that can travel by reference, or null. An
68
+ // inline payload is written to the store first so it gets a path too.
69
+ function storedMediaPart(part) {
70
+ if (!part || typeof part !== 'object') return null;
71
+ if (part.type === 'image') {
72
+ if (isAttachmentReference(part)) return part;
73
+ return storeInlineImagePart(part);
74
+ }
75
+ if (part.type === 'file' || part.type === 'document') {
76
+ const stored = isAttachmentReference(part) ? part : storeInlineDocumentPart(part);
77
+ if (!stored) return null;
78
+ const mimeType = String(stored.mimeType || stored.mediaType || '');
79
+ return inlineFileKind(mimeType, '') === 'pdf' ? stored : null;
80
+ }
81
+ return null;
82
+ }
83
+
84
+ /** Whether `part` is a media part this module can turn into a reference. */
85
+ export function isReferencableMediaPart(part) {
86
+ if (!part || typeof part !== 'object') return false;
87
+ if (part.type === 'image') {
88
+ return isAttachmentReference(part) || typeof part.data === 'string' || part.source?.type === 'base64';
89
+ }
90
+ if (part.type === 'document') return part.source?.type === 'base64';
91
+ if (part.type !== 'file') return false;
92
+ const mimeType = String(part.mimeType || part.mediaType || 'application/pdf');
93
+ return inlineFileKind(mimeType, isAttachmentReference(part) ? '' : part.data || '', part.filename) === 'pdf';
94
+ }
95
+
96
+ /** Whether `part` is a reference this module produced. */
97
+ export function isMediaReferencePart(part) {
98
+ return Boolean(part && part.type === 'text' && part.attachmentReference && typeof part.text === 'string');
99
+ }
100
+
101
+ /**
102
+ * The reference text part standing for one media part, or null when the part
103
+ * cannot travel by reference (a URL image, a non-PDF file, a store failure).
104
+ */
105
+ export function mediaPartToReference(part) {
106
+ let stored;
107
+ try {
108
+ stored = storedMediaPart(part);
109
+ } catch {
110
+ return null;
111
+ }
112
+ if (!stored) return null;
113
+ const kind = stored.type === 'image' ? 'image' : 'pdf';
114
+ const pageCount = Math.floor(Number(part.pageCount ?? stored.pageCount)) || 0;
115
+ const textPagesRaw = part.textPages ?? stored.textPages;
116
+ const info = {
117
+ attachmentRef: stored.attachmentRef,
118
+ kind,
119
+ filename: String(part.filename || part.title || part.name || stored.filename || ''),
120
+ mimeType: String(stored.mimeType || stored.mediaType || (kind === 'pdf' ? 'application/pdf' : 'image/png')),
121
+ sizeBytes: Number(stored.sizeBytes) || 0,
122
+ ...(pageCount > 0 ? { pageCount } : {}),
123
+ ...(Number.isFinite(Number(textPagesRaw)) && textPagesRaw !== null ? { textPages: Number(textPagesRaw) } : {}),
124
+ };
125
+ const path = attachmentFilePath(info.attachmentRef);
126
+ return {
127
+ type: 'text',
128
+ text: kind === 'pdf' ? pdfReferenceText(info, path) : imageReferenceText(info, path),
129
+ attachmentReference: info,
130
+ };
131
+ }
132
+
133
+ /**
134
+ * A user `message` with every referencable media part of its own content
135
+ * replaced by its reference text (other roles, tool results and nested content
136
+ * stay as they are).
137
+ * Returns the same message when nothing changed.
138
+ */
139
+ export function referenceMessageMedia(message) {
140
+ if (message?.role !== 'user' || !Array.isArray(message.content)) return message;
141
+ let changed = false;
142
+ const content = message.content.map((part) => {
143
+ if (!isReferencableMediaPart(part)) return part;
144
+ const reference = mediaPartToReference(part);
145
+ if (!reference) return part;
146
+ changed = true;
147
+ return reference;
148
+ });
149
+ return changed ? { ...message, content } : message;
150
+ }
151
+
152
+ /** Whether the message's own content still carries referencable media. */
153
+ export function messageHasReferencableMedia(message) {
154
+ return Array.isArray(message?.content) && message.content.some(isReferencableMediaPart);
155
+ }
@@ -16,5 +16,9 @@ const PDF_MAGIC = Buffer.from('%PDF-', 'latin1');
16
16
 
17
17
  /** True when the bytes start with the %PDF- header. */
18
18
  export function isPdfBuffer(buffer) {
19
- return Buffer.isBuffer(buffer) && buffer.length >= PDF_MAGIC.length && buffer.subarray(0, PDF_MAGIC.length).equals(PDF_MAGIC);
19
+ return (
20
+ Buffer.isBuffer(buffer) &&
21
+ buffer.length >= PDF_MAGIC.length &&
22
+ buffer.subarray(0, PDF_MAGIC.length).equals(PDF_MAGIC)
23
+ );
20
24
  }
@@ -6,6 +6,7 @@
6
6
  // No external dependencies.
7
7
  import { readFile, stat } from 'node:fs/promises';
8
8
  import { inflateRawSync } from 'node:zlib';
9
+ import { stripTrailingReplacementChars } from '../shared/utf8-cut.mjs';
9
10
 
10
11
  // Longest run of attributes inside one tag that a pattern will cross. Bounded
11
12
  // so a tag with no closing bracket cannot make a pattern rescan the part.
@@ -675,12 +676,8 @@ function sheetFigures(buf, entries, sheetPart) {
675
676
  function capOutput(text, maxOutputBytes) {
676
677
  const buf = Buffer.from(text, 'utf8');
677
678
  if (buf.length <= maxOutputBytes) return text;
678
- let cut = buf.subarray(0, maxOutputBytes).toString('utf8');
679
- // Drop the replacement characters a split multi-byte sequence leaves (a loop:
680
- // `/\uFFFD+$/` is quadratic on a long run of them).
681
- let end = cut.length;
682
- while (end > 0 && cut[end - 1] === '\uFFFD') end -= 1;
683
- cut = cut.slice(0, end);
679
+ // Drop the replacement characters a split multi-byte sequence leaves.
680
+ const cut = stripTrailingReplacementChars(buf.subarray(0, maxOutputBytes).toString('utf8'));
684
681
  return `${cut}\n... [office text truncated at ${maxOutputBytes} bytes]`;
685
682
  }
686
683