mixdog 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (417) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-shipped-history.mjs +15 -5
  3. package/scripts/gate-local.mjs +65 -19
  4. package/scripts/graph-parity.mjs +8 -3
  5. package/scripts/lib/gate-scheduler.mjs +2 -1
  6. package/scripts/lib/run-node-tests.mjs +17 -5
  7. package/scripts/lib/test-failure-summary.mjs +1 -8
  8. package/scripts/lib/test-spawn-ledger.mjs +33 -2
  9. package/scripts/post-release-check.mjs +26 -16
  10. package/scripts/provider-pingpong-live.mjs +1 -1
  11. package/scripts/prune-embedding-runtime.mjs +3 -1
  12. package/scripts/release-paths.mjs +7 -1
  13. package/src/defaults/shipped-history.json +20 -0
  14. package/src/defaults/skills/setup/SKILL.md +2 -2
  15. package/src/defaults/skills/setup/references/actions.md +16 -3
  16. package/src/defaults/skills/setup/references/surfaces.md +3 -0
  17. package/src/defaults/skills/setup-guide/SKILL.md +6 -1
  18. package/src/lib/instruction-text.cjs +36 -0
  19. package/src/lib/rules-builder.cjs +34 -20
  20. package/src/orchestration/balanced.md +23 -0
  21. package/src/orchestration/focused.md +22 -0
  22. package/src/orchestration/none.md +5 -0
  23. package/src/orchestration/swarm.md +24 -0
  24. package/src/rules/agent-mode/AGENT_MODE.md +3 -1
  25. package/src/rules/agent-mode/MIXY.md +2 -2
  26. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -2
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +0 -9
  30. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
  31. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
  32. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -4
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -1
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +199 -46
  36. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +3 -5
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +9 -3
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -5
  39. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
  41. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
  42. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
  43. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
  46. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
  49. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
  51. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
  52. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +10 -1
  53. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
  54. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +30 -0
  55. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
  56. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
  57. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
  58. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
  59. package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
  60. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
  61. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
  66. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
  67. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
  68. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
  69. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
  70. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -48
  71. package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
  72. package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
  73. package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +9 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
  79. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
  80. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
  83. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
  88. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
  92. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
  93. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +1 -2
  94. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
  97. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
  99. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
  100. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
  101. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
  102. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
  104. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +6 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  108. package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
  109. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +57 -26
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
  113. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
  114. package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
  115. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
  116. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
  117. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
  118. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
  120. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  121. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
  122. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
  123. package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
  126. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +19 -10
  128. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +31 -0
  131. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +42 -6
  133. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
  134. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +11 -1
  135. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
  139. package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
  140. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
  143. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
  145. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
  146. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
  148. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
  149. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
  150. package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
  151. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
  152. package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
  153. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +14 -5
  154. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +2 -2
  155. package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
  156. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
  157. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
  158. package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
  159. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
  160. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
  162. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
  163. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
  167. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +7 -3
  168. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +27 -9
  169. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
  173. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
  175. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
  176. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
  177. package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
  179. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
  180. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
  181. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
  182. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +2 -2
  183. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -4
  185. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
  186. package/src/runtime/attachments/attachment-reference.mjs +1 -1
  187. package/src/runtime/attachments/pdf-extract.mjs +1 -1
  188. package/src/runtime/attachments/store.d.mts +1 -0
  189. package/src/runtime/attachments/store.mjs +2 -2
  190. package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
  191. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
  192. package/src/runtime/channels/lib/scheduler.mjs +33 -0
  193. package/src/runtime/channels/lib/schedules-db.mjs +11 -4
  194. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
  195. package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
  196. package/src/runtime/channels/lib/whisper-server.mjs +12 -12
  197. package/src/runtime/channels/lib/worker-main.mjs +9 -31
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
  199. package/src/runtime/connectors/connector-accounts-mcp.mjs +169 -33
  200. package/src/runtime/connectors/gmail-app-password.mjs +2 -2
  201. package/src/runtime/connectors/google-mcp.mjs +1 -1
  202. package/src/runtime/effort-judge/judge-client.mjs +2 -2
  203. package/src/runtime/effort-judge/judge-input.mjs +4 -4
  204. package/src/runtime/explore/explorer.mjs +0 -5
  205. package/src/runtime/explore/tool.mjs +3 -3
  206. package/src/runtime/local-provider/hardware.mjs +1 -1
  207. package/src/runtime/media/adapters/codex-image.mjs +3 -3
  208. package/src/runtime/media/adapters/gemini-video.mjs +1 -1
  209. package/src/runtime/media/media-usage.mjs +1 -1
  210. package/src/runtime/media/store.mjs +1 -1
  211. package/src/runtime/memory/index.mjs +11 -1
  212. package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
  213. package/src/runtime/memory/lib/memory-embed.mjs +83 -14
  214. package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
  215. package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
  216. package/src/runtime/memory/lib/memory.mjs +17 -0
  217. package/src/runtime/memory/lib/pg/process.mjs +1 -1
  218. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
  219. package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
  220. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  221. package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
  222. package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
  223. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  224. package/src/runtime/office/capabilities.mjs +1 -1
  225. package/src/runtime/office/core/office-core.mjs +1 -1
  226. package/src/runtime/office/design/content-model.mjs +3 -3
  227. package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
  228. package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
  229. package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
  230. package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
  231. package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
  232. package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
  233. package/src/runtime/office/portable/portable-soffice.mjs +8 -6
  234. package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
  235. package/src/runtime/shared/atomic-file.d.mts +8 -0
  236. package/src/runtime/shared/background-tasks.mjs +22 -4
  237. package/src/runtime/shared/background-wait.mjs +66 -0
  238. package/src/runtime/shared/child-guardian.mjs +17 -7
  239. package/src/runtime/shared/config.mjs +313 -16
  240. package/src/runtime/shared/connector-catalog.d.mts +4 -5
  241. package/src/runtime/shared/connector-catalog.mjs +11 -1
  242. package/src/runtime/shared/file-lock.mjs +8 -3
  243. package/src/runtime/shared/git-runner.mjs +7 -2
  244. package/src/runtime/shared/idle-gc.mjs +30 -7
  245. package/src/runtime/shared/llm/http-agent.mjs +8 -5
  246. package/src/runtime/shared/llm/system-proxy.mjs +2 -1
  247. package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
  248. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
  249. package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
  250. package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
  251. package/src/runtime/shared/maintenance-schedule.mjs +109 -0
  252. package/src/runtime/shared/process-runner.mjs +8 -4
  253. package/src/runtime/shared/routine-trigger.d.mts +13 -0
  254. package/src/runtime/shared/routine-trigger.mjs +34 -0
  255. package/src/runtime/shared/run-child.d.mts +2 -1
  256. package/src/runtime/shared/run-child.mjs +70 -18
  257. package/src/runtime/shared/session-scratch.mjs +14 -2
  258. package/src/runtime/shared/shipped-definitions.mjs +7 -1
  259. package/src/runtime/shared/tool-card-model.golden.json +3 -12
  260. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  261. package/src/runtime/shared/tool-work-units.mjs +31 -6
  262. package/src/runtime/shared/turn-snapshot.mjs +26 -4
  263. package/src/runtime/shared/turn-worktree-snapshot.mjs +6 -11
  264. package/src/runtime/shared/worker-requests.mjs +17 -3
  265. package/src/runtime/shared/worktrees/branch-management.mjs +1 -5
  266. package/src/runtime/shared/worktrees/contract.mjs +16 -6
  267. package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
  268. package/src/runtime/shared/worktrees/disk.mjs +79 -2
  269. package/src/runtime/shared/worktrees/git.mjs +44 -11
  270. package/src/runtime/shared/worktrees/include.mjs +6 -4
  271. package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
  272. package/src/runtime/shared/worktrees/manager.mjs +772 -75
  273. package/src/runtime/shared/worktrees/settings.mjs +33 -7
  274. package/src/runtime/shared/worktrees/test-support.mjs +5 -1
  275. package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
  276. package/src/runtime/tidy/process.mjs +2 -1
  277. package/src/runtime/web-search/index.mjs +3 -22
  278. package/src/runtime/web-search/lib/web-tools.mjs +2 -2
  279. package/src/runtime/web-search/tool-defs.mjs +17 -26
  280. package/src/session-runtime/agent-profile-changes.mjs +55 -4
  281. package/src/session-runtime/boot/apis.mjs +27 -4
  282. package/src/session-runtime/boot/begin.mjs +2 -0
  283. package/src/session-runtime/boot/config.mjs +10 -1
  284. package/src/session-runtime/boot/facade.mjs +1 -0
  285. package/src/session-runtime/boot/providers.mjs +70 -3
  286. package/src/session-runtime/boot/session.mjs +2 -0
  287. package/src/session-runtime/boot/tools.mjs +69 -25
  288. package/src/session-runtime/channel-config-api.mjs +1 -0
  289. package/src/session-runtime/config-change-effects.mjs +179 -0
  290. package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
  291. package/src/session-runtime/config-lifecycle.mjs +160 -24
  292. package/src/session-runtime/conversation-import/discovery.mjs +86 -7
  293. package/src/session-runtime/conversation-import/service.mjs +3 -2
  294. package/src/session-runtime/goal-facade-api.mjs +49 -3
  295. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
  296. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
  297. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +3 -11
  298. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +18 -4
  299. package/src/session-runtime/internal-tool-executor.mjs +12 -19
  300. package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
  301. package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
  302. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
  303. package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
  304. package/src/session-runtime/lifecycle/teardown/session-close.mjs +4 -4
  305. package/src/session-runtime/lifecycle-api.mjs +1 -0
  306. package/src/session-runtime/mcp-glue/server-catalog.mjs +14 -3
  307. package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
  308. package/src/session-runtime/model-route/set-route.mjs +40 -7
  309. package/src/session-runtime/native-web-search/routes.mjs +3 -3
  310. package/src/session-runtime/provider-auth/refresh.mjs +6 -3
  311. package/src/session-runtime/provider-auth/setup.mjs +1 -1
  312. package/src/session-runtime/provider-readiness.mjs +53 -4
  313. package/src/session-runtime/resource-api.mjs +1 -1
  314. package/src/session-runtime/resource-extension-sync.mjs +4 -8
  315. package/src/session-runtime/resource-mcp-connector-accounts.mjs +5 -1
  316. package/src/session-runtime/resource-mcp-secrets.mjs +3 -3
  317. package/src/session-runtime/resource-plugins-api.mjs +4 -0
  318. package/src/session-runtime/routine-executor.mjs +152 -0
  319. package/src/session-runtime/routine-home-turn.mjs +46 -5
  320. package/src/session-runtime/runtime-facade.mjs +5 -0
  321. package/src/session-runtime/runtime-feature-gates.mjs +2 -11
  322. package/src/session-runtime/schedule-session-run.mjs +1 -0
  323. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
  324. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
  325. package/src/session-runtime/services/agent-tool/send-flow.mjs +110 -6
  326. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
  327. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
  328. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +24 -6
  329. package/src/session-runtime/services/agent-tool.mjs +51 -1
  330. package/src/session-runtime/services/channel-admin.mjs +18 -0
  331. package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
  332. package/src/session-runtime/services/plugin-admin.mjs +4 -0
  333. package/src/session-runtime/services/seeds.mjs +7 -0
  334. package/src/session-runtime/services/usage-stats-model.mjs +32 -11
  335. package/src/session-runtime/session-create/materialize.mjs +4 -2
  336. package/src/session-runtime/session-create/session-options.mjs +2 -0
  337. package/src/session-runtime/settings-builtin-tools-api.mjs +1 -9
  338. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +1 -1
  339. package/src/session-runtime/setup-tool/executor.mjs +8 -1
  340. package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
  341. package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
  342. package/src/session-runtime/setup-tool/tool-defs.mjs +26 -1
  343. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
  344. package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
  345. package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
  346. package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
  347. package/src/session-runtime/tool-policy-refresh.mjs +182 -5
  348. package/src/session-runtime/tool-surface.mjs +38 -1
  349. package/src/session-runtime/turn/agent-ops.mjs +10 -2
  350. package/src/session-runtime/turn/session-ops.mjs +2 -0
  351. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
  352. package/src/session-runtime/turn/turn-run.mjs +1 -1
  353. package/src/session-runtime/webhook-session-run.mjs +2 -0
  354. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +1 -4
  355. package/src/session-runtime/workflow-agents-api/agent-route.mjs +3 -10
  356. package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
  357. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
  358. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
  359. package/src/session-runtime/workflow-agents-api.mjs +2 -0
  360. package/src/session-runtime/workspace/project-branches.mjs +1 -1
  361. package/src/session-runtime/workspace/session-view.mjs +1 -1
  362. package/src/session-runtime/workspace/transitions.mjs +20 -4
  363. package/src/session-runtime/workspace/worktree-maintenance.mjs +126 -6
  364. package/src/session-runtime/workspace/worktree-setup.mjs +13 -1
  365. package/src/session-runtime/workspace/worktree-tool.mjs +16 -0
  366. package/src/standalone/daemon-agent-control.mjs +25 -8
  367. package/src/standalone/daemon-desktop-runtime.mjs +6 -2
  368. package/src/standalone/daemon-lag-profiler.mjs +7 -7
  369. package/src/standalone/daemon.mjs +54 -2
  370. package/src/standalone/local-session-runtime.mjs +16 -0
  371. package/src/standalone/session-client/daemon-unavailable.mjs +1 -1
  372. package/src/standalone/session-protocol.mjs +11 -0
  373. package/src/standalone/session-service/agent-route-follow.mjs +23 -20
  374. package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
  375. package/src/standalone/session-service/quota-resume.mjs +9 -0
  376. package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
  377. package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
  378. package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
  379. package/src/standalone/session-service/session-calls.mjs +2 -1
  380. package/src/tui/App.jsx +8 -0
  381. package/src/tui/app/clipboard.mjs +1 -1
  382. package/src/tui/app/create-app-pickers.mjs +2 -0
  383. package/src/tui/app/doctor.mjs +2 -2
  384. package/src/tui/app/panel-signature.mjs +1 -1
  385. package/src/tui/app/settings-picker.mjs +53 -8
  386. package/src/tui/app/shell-layout/row-budgets.mjs +2 -2
  387. package/src/tui/app/slash-commands.mjs +1 -1
  388. package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
  389. package/src/tui/app/slash-dispatch/session-commands.mjs +1 -1
  390. package/src/tui/app/use-prompt-queue-history.mjs +2 -2
  391. package/src/tui/app/use-transcript-activity.mjs +1 -1
  392. package/src/tui/app/use-transcript-window/geometry.mjs +1 -1
  393. package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
  394. package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
  395. package/src/tui/components/question-panel-model.mjs +4 -4
  396. package/src/tui/components/tool-execution/standard-card.jsx +3 -4
  397. package/src/tui/components/user-message-skills.mjs +1 -1
  398. package/src/tui/dist/index.mjs +309 -30
  399. package/src/tui/markdown/render-ansi.mjs +1 -1
  400. package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
  401. package/src/tui/session/agent-job-feed.mjs +2 -0
  402. package/src/tui/session/context-state.mjs +3 -0
  403. package/src/tui/session/quota-resume.mjs +1 -1
  404. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  405. package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
  406. package/src/tui/session/session-api/routes-agent-definitions.mjs +5 -0
  407. package/src/tui/session/session-api/worktree.mjs +13 -1
  408. package/src/tui/session/session-flow/drain.mjs +11 -14
  409. package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
  410. package/src/tui/session/turn-quiet.mjs +1 -1
  411. package/src/tui/session/turn.mjs +3 -0
  412. package/src/tui/session/worktree-setup-notice.mjs +1 -1
  413. package/src/tui/spinner-meta.mjs +3 -3
  414. package/src/tui/spinner-verbs.mjs +2 -2
  415. package/src/defaults/skills/goal-management/SKILL.md +0 -169
  416. package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
  417. package/src/session-runtime/goal-tasks.mjs +0 -1
@@ -18,7 +18,14 @@ import {
18
18
  withDefaultPresets,
19
19
  } from '../runtime/agent/orchestrator/config-presets.mjs';
20
20
  import { syncSessionCompaction } from './session-compaction-sync.mjs';
21
- import { createConfigWriters } from './config-lifecycle/config-writers.mjs';
21
+ import {
22
+ configChangeDomains,
23
+ configChangeIsEmpty,
24
+ configChangeSnapshot,
25
+ diffConfigSnapshots,
26
+ withConfigDomainPending,
27
+ } from './config-change-effects.mjs';
28
+ import { configWithOutputStyle, createConfigWriters } from './config-lifecycle/config-writers.mjs';
22
29
  import { createLeadRouteSync } from './config-lifecycle/lead-route-sync.mjs';
23
30
  import { createOutputStyleStatusCache } from './config-lifecycle/output-style-cache.mjs';
24
31
 
@@ -45,9 +52,19 @@ function seedChanges(config, changes) {
45
52
  .map((key) => ({ path: [key], value: structuredClone(config[key]), ifAbsent: true }));
46
53
  }
47
54
 
55
+ // The agent config load carries no root `outputStyle`; the stored root value
56
+ // is laid on it (absent: the default style applies).
57
+ function withStoredOutputStyle(config, storedOutputStyle) {
58
+ return typeof storedOutputStyle === 'string' && storedOutputStyle.trim()
59
+ ? { ...config, outputStyle: storedOutputStyle.trim() }
60
+ : config;
61
+ }
62
+
48
63
  // The config just read from disk with this runtime's still-pending edits laid
49
64
  // back on it (the config and skills writers hold the only copy of them).
50
- function withPendingEdits(loaded, writers) {
65
+ // The agent config load carries no root `outputStyle`: the stored root value
66
+ // (`storedOutputStyle`) is laid on it, and this runtime's pending pick wins.
67
+ function withPendingEdits(loaded, writers, storedOutputStyle) {
51
68
  let next = loaded;
52
69
  if (writers.hasPendingConfigChanges()) {
53
70
  // Preserve only our pending edits. Peer changes and fresh secret overlays
@@ -67,6 +84,9 @@ function withPendingEdits(loaded, writers) {
67
84
  if (pendingSkills !== null) {
68
85
  next = { ...next, skills: { ...(next.skills || {}), disabled: pendingSkills } };
69
86
  }
87
+ const pendingOutputStyle = writers.pendingOutputStyle();
88
+ if (pendingOutputStyle !== null) next = configWithOutputStyle(next, pendingOutputStyle);
89
+ else next = withStoredOutputStyle(next, storedOutputStyle);
70
90
  return next;
71
91
  }
72
92
 
@@ -85,6 +105,7 @@ function withPendingEdits(loaded, writers) {
85
105
  export function resolveInitialConfigState({
86
106
  initialConfig,
87
107
  loadConfig,
108
+ readStoredOutputStyle = null,
88
109
  resolveRoute,
89
110
  provider,
90
111
  model,
@@ -97,8 +118,12 @@ export function resolveInitialConfigState({
97
118
  selectedContextWindow,
98
119
  agentOwned = false,
99
120
  }) {
121
+ // An injected config is complete as given; a loaded one takes the stored
122
+ // root output style (`readStoredOutputStyle`) as every reload does.
100
123
  const config = withGrandfatheredBuiltins(
101
- initialConfig && typeof initialConfig === 'object' ? initialConfig : loadConfig()
124
+ initialConfig && typeof initialConfig === 'object'
125
+ ? initialConfig
126
+ : withStoredOutputStyle(loadConfig(), readStoredOutputStyle?.())
102
127
  );
103
128
  // A requested percent replaces a saved token window; requested tokens
104
129
  // replace both (resolveRouteContextState lets tokens win).
@@ -135,10 +160,12 @@ export function createConfigLifecycle({
135
160
  getConfigHasSecrets,
136
161
  setConfigHasSecrets,
137
162
  getRoute,
138
- // The live session follows the reloaded global compaction settings, unless
139
- // the runtime runs on an injected (headless) config.
163
+ // The live session follows the reloaded global compaction settings.
140
164
  getSession,
141
- fixedSessionCompaction = false,
165
+ // A headless caller injects a complete, deliberately isolated config: it
166
+ // never reloads from disk (no change subscription either), and its sessions
167
+ // keep the compaction they were created with.
168
+ injectedConfig = false,
142
169
  // Main route sync on reload (both optional: a lifecycle without them never syncs)
143
170
  setRouteState,
144
171
  resolveRoute,
@@ -153,9 +180,14 @@ export function createConfigLifecycle({
153
180
  clean,
154
181
  resolve,
155
182
  performanceNow = () => performance.now(),
156
- onConfigReloaded,
183
+ // The runtime's change-driven effects (config-change-effects.mjs), given the
184
+ // diff against the snapshot this runtime last applied.
185
+ onConfigChanged,
186
+ // The backoff of a failed effect's retries; the last delay repeats.
187
+ effectRetryDelaysMs = [1000, 4000, 15000, 60000],
157
188
  STANDALONE_DATA_DIR,
158
189
  }) {
190
+ let disposed = false;
159
191
  const outputStyleCache = createOutputStyleStatusCache({
160
192
  cfgMod,
161
193
  STANDALONE_DATA_DIR,
@@ -217,25 +249,125 @@ export function createConfigLifecycle({
217
249
  }
218
250
 
219
251
  // --- reload / ensure --------------------------------------------------------
220
- function reloadFullConfig() {
221
- // A pending debounced write holds the only copy of the latest change.
222
- // Flush it before re-reading from disk so loadConfig() observes (and the
223
- // subsequent adopt preserves) that change instead of reverting to a stale
224
- // on-disk snapshot.
252
+ const changeSnapshot = (config) =>
253
+ configChangeSnapshot({
254
+ config,
255
+ root: sharedCfgMod?.readConfig?.(),
256
+ secretTokens: sharedCfgMod?.secretChangeTokens?.(),
257
+ });
258
+ // What this runtime's effects last ran against, starting from the boot
259
+ // config (the snapshot does not depend on whether a load read secrets).
260
+ let appliedSnapshot = changeSnapshot(getConfig());
261
+ // The stored settings the current config object was taken from (never
262
+ // marked pending): the object is replaced only when they changed, so a
263
+ // retry of a failed effect alone keeps it.
264
+ let loadedSnapshot = appliedSnapshot;
265
+
266
+ // A pending debounced write holds the only copy of the latest change.
267
+ // Flush it before re-reading from disk so loadConfig() observes (and the
268
+ // subsequent adopt preserves) that change instead of reverting to a stale
269
+ // on-disk snapshot.
270
+ function loadFullConfig() {
225
271
  writers.flushConfigSave();
226
- const next = withPendingEdits(cfgMod.loadConfig(), writers);
272
+ if (injectedConfig) return null;
273
+ return withPendingEdits(cfgMod.loadConfig(), writers, sharedCfgMod?.readConfig?.()?.outputStyle);
274
+ }
275
+
276
+ function reloadFullConfig() {
277
+ return adoptFullConfig(loadFullConfig());
278
+ }
279
+
280
+ function adoptFullConfig(next) {
281
+ if (!next) return getConfig();
227
282
  const adopted = adoptConfig(next, { hasSecrets: true });
228
283
  configSaveBaseline = structuredClone(adopted);
229
284
  leadRoute.syncEmptyMainRoute(adopted);
230
- if (!fixedSessionCompaction) syncSessionCompaction(getSession?.(), adopted);
285
+ syncSessionCompaction(getSession?.(), adopted);
231
286
  return adopted;
232
287
  }
233
288
 
234
289
  function ensureFullConfig() {
235
- if (getConfigHasSecrets()) return getConfig();
290
+ if (injectedConfig || getConfigHasSecrets()) return getConfig();
236
291
  return reloadFullConfig();
237
292
  }
238
293
 
294
+ /** The change step: reload, then run the effects keyed by what differs from
295
+ * the last applied snapshot. A writer that runs it right after its own write
296
+ * has applied the change, so the notification that follows finds nothing.
297
+ * The config object is replaced only when the stored settings changed (a
298
+ * retry of a failed effect alone keeps it): consumers key turn-start
299
+ * reconciles on its identity. */
300
+ function applyConfigChange() {
301
+ // A load that throws changes nothing: pending domains stay pending.
302
+ const loaded = loadFullConfig();
303
+ // Every pending domain is in this step's diff, so it runs again here (and
304
+ // a scheduled retry is this step).
305
+ pendingDomains = new Set();
306
+ clearTimeout(retryTimer);
307
+ retryTimer = null;
308
+ outputStyleCache.invalidateOutputStyleStatusCache();
309
+ const next = changeSnapshot(loaded ?? getConfig());
310
+ const change = diffConfigSnapshots(appliedSnapshot, next);
311
+ appliedSnapshot = next;
312
+ const settingsChanged = !configChangeIsEmpty(diffConfigSnapshots(loadedSnapshot, next));
313
+ loadedSnapshot = next;
314
+ const config = settingsChanged ? adoptFullConfig(loaded) : getConfig();
315
+ let outcome;
316
+ try {
317
+ outcome = onConfigChanged?.(change, config);
318
+ } catch {
319
+ outcome = configChangeDomains(change);
320
+ }
321
+ void Promise.resolve(outcome).then(
322
+ (failedDomains) => settleConfigEffects(failedDomains),
323
+ () => settleConfigEffects(configChangeDomains(change))
324
+ );
325
+ return change;
326
+ }
327
+
328
+ // A domain whose effect failed is marked not applied, so the next change
329
+ // step (any notification, a writer's own step, or the retry below) runs it
330
+ // again; the applied snapshot otherwise advances as the step runs, which
331
+ // keeps a step overlapping an in-flight effect from repeating it. The retry
332
+ // backs off to the last delay and keeps going until the domain succeeds or
333
+ // the sync is disposed.
334
+ let pendingDomains = new Set();
335
+ let retryTimer = null;
336
+ let retryAttempt = 0;
337
+ function settleConfigEffects(failedDomains) {
338
+ for (const domain of Array.isArray(failedDomains) ? failedDomains : []) {
339
+ appliedSnapshot = withConfigDomainPending(appliedSnapshot, domain);
340
+ pendingDomains.add(domain);
341
+ }
342
+ if (!pendingDomains.size) {
343
+ retryAttempt = 0;
344
+ return;
345
+ }
346
+ if (retryTimer || disposed) return;
347
+ const delay = effectRetryDelaysMs[Math.min(retryAttempt, effectRetryDelaysMs.length - 1)];
348
+ retryTimer = setTimeout(() => {
349
+ retryTimer = null;
350
+ retryAttempt += 1;
351
+ try {
352
+ applyConfigChange();
353
+ } catch {
354
+ // The reload failed (e.g. a config mid-write): still pending, retry later.
355
+ settleConfigEffects([]);
356
+ }
357
+ }, delay);
358
+ retryTimer.unref?.();
359
+ }
360
+
361
+ // The output style this runtime selected but has not yet persisted wins over
362
+ // a status read from disk, so a concurrent write never shows the old style.
363
+ function getOutputStyleStatusCached(options) {
364
+ const status = outputStyleCache.getOutputStyleStatusCached(options);
365
+ const pending = writers.pendingOutputStyle();
366
+ if (pending === null || status?.configured === pending) return status;
367
+ const current = (status?.styles || []).find((style) => style.id === pending) || status?.current;
368
+ return { ...status, configured: pending, current };
369
+ }
370
+
239
371
  function ensureConfigForRouteProvider() {
240
372
  const config = getConfig();
241
373
  const providerId = clean(getRoute().provider);
@@ -246,19 +378,22 @@ export function createConfigLifecycle({
246
378
  return ensureFullConfig();
247
379
  }
248
380
 
249
- // Settings persisted by any other runtime (or process) reach this runtime's
250
- // in-memory config. reloadFullConfig keeps our own pending edits, and the
251
- // shared layer coalesces bursts into one pass per subscriber.
252
- const unsubscribeConfigChange = sharedCfgMod?.subscribeConfigChange?.(() => {
253
- reloadFullConfig();
254
- outputStyleCache.invalidateOutputStyleStatusCache();
255
- onConfigReloaded?.();
256
- });
381
+ // Settings and secrets persisted by this or any other runtime (or process)
382
+ // reach this runtime's config and effects. reloadFullConfig keeps our own
383
+ // pending edits, and the shared layer coalesces bursts into one pass per
384
+ // subscriber. An injected config never follows the disk.
385
+ const unsubscribeConfigChange = injectedConfig ? null : sharedCfgMod?.subscribeConfigChange?.(applyConfigChange);
257
386
 
258
387
  return {
259
- disposeConfigSync: () => unsubscribeConfigChange?.(),
388
+ disposeConfigSync: () => {
389
+ disposed = true;
390
+ clearTimeout(retryTimer);
391
+ retryTimer = null;
392
+ unsubscribeConfigChange?.();
393
+ },
260
394
  // output-style cache
261
395
  ...outputStyleCache,
396
+ getOutputStyleStatusCached,
262
397
  // adopt / save
263
398
  adoptConfig,
264
399
  saveConfigAndAdopt,
@@ -270,6 +405,7 @@ export function createConfigLifecycle({
270
405
  flushAllConfigSavesAsync: writers.flushAllConfigSavesAsync,
271
406
  // reload / ensure
272
407
  reloadFullConfig,
408
+ applyConfigChange,
273
409
  ensureFullConfig,
274
410
  displayConfig: () => getConfig(),
275
411
  ensureConfigForRouteProvider,
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * codex $CODEX_HOME/sessions/**\/rollout-*.jsonl[.zst] (default ~/.codex)
5
5
  * claude $CLAUDE_CONFIG_DIR/projects/*\/*.jsonl (default ~/.claude)
6
+ * cowork %APPDATA%\Claude\local-agent-mode-sessions (Claude Desktop; Windows only)
6
7
  *
7
8
  * Enumeration is stat-only; per-file metadata (foreign id, cwd, title, and
8
9
  * the message count when the whole file fit the read) comes from a bounded
@@ -11,7 +12,7 @@
11
12
  * shares one cache, one candidate registry and one set of listing views.
12
13
  */
13
14
  import { createHash, randomUUID } from 'node:crypto';
14
- import { open, readdir, realpath, stat } from 'node:fs/promises';
15
+ import { open, readdir, readFile, realpath, stat } from 'node:fs/promises';
15
16
  import { homedir } from 'node:os';
16
17
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
17
18
  import { pipeline } from 'node:stream';
@@ -77,6 +78,11 @@ const envDir = (value) => (typeof value === 'string' && value.trim() ? resolve(v
77
78
  export function sourceRoot(source, env = process.env) {
78
79
  if (source === 'codex') return join(envDir(env.CODEX_HOME) || join(homedir(), '.codex'), 'sessions');
79
80
  if (source === 'claude') return join(envDir(env.CLAUDE_CONFIG_DIR) || join(homedir(), '.claude'), 'projects');
81
+ // Only Windows sets APPDATA; elsewhere Claude Desktop has no Cowork folder to search.
82
+ if (source === 'cowork') {
83
+ const appData = envDir(env.APPDATA);
84
+ return appData ? join(appData, 'Claude', 'local-agent-mode-sessions') : null;
85
+ }
80
86
  throw importError('unsupported');
81
87
  }
82
88
 
@@ -91,8 +97,8 @@ export function loadStoredTitles(source, env = process.env) {
91
97
  }
92
98
 
93
99
  /** The app's stored title of a conversation, else the one derived from its messages. */
94
- export function displayTitle(meta, titles) {
95
- return (meta?.foreignSessionId && titles?.get(meta.foreignSessionId)) || meta?.title || null;
100
+ export function displayTitle(meta, titles, manifestTitle = null) {
101
+ return manifestTitle || (meta?.foreignSessionId && titles?.get(meta.foreignSessionId)) || meta?.title || null;
96
102
  }
97
103
 
98
104
  const caseFold = (value) => (process.platform === 'win32' ? value.toLowerCase() : value);
@@ -215,6 +221,68 @@ async function walkClaude(rootReal, files) {
215
221
  }
216
222
  }
217
223
 
224
+ // Cowork keeps one `local_<id>.json` manifest per conversation (the account/org folders hold
225
+ // `local_*.json` directly, the agent variant under `agent/`), next to a `local_<id>/` folder whose
226
+ // `.claude/projects/<project>/<cliSessionId>.jsonl` is the Claude Code transcript. The manifest also
227
+ // holds the system prompt and account details, so only title / isStarred / cliSessionId are read.
228
+ // The agent variant (`local_ditto_*`, sessionType "agent") is the user's own Claude desktop chat
229
+ // (its initialMessage is user-typed), so it is listed like the others.
230
+ const COWORK_MANIFEST_MAX_BYTES = 8 * 1024 * 1024;
231
+ const COWORK_TITLE_MAX_CHARS = 200;
232
+ const COWORK_SESSION_ID_RE = /^[\w-]+$/;
233
+
234
+ async function coworkManifestPaths(rootReal) {
235
+ const manifests = [];
236
+ for (const account of await entriesOf(rootReal)) {
237
+ if (!account.isDirectory()) continue;
238
+ for (const org of await entriesOf(join(rootReal, account.name))) {
239
+ if (!org.isDirectory()) continue;
240
+ for (const dir of [join(rootReal, account.name, org.name), join(rootReal, account.name, org.name, 'agent')]) {
241
+ for (const file of await entriesOf(dir)) {
242
+ if (file.isFile() && /^local_.+\.json$/.test(file.name)) manifests.push(join(dir, file.name));
243
+ }
244
+ }
245
+ }
246
+ }
247
+ return manifests;
248
+ }
249
+
250
+ // The transcript a manifest names, with the manifest's title and star; null when it has none.
251
+ async function coworkTranscript(rootReal, manifestPath) {
252
+ const real = await containedRealpath(rootReal, manifestPath);
253
+ const info = real ? await stat(real).catch(() => null) : null;
254
+ if (!info?.isFile() || info.size > COWORK_MANIFEST_MAX_BYTES) return null;
255
+ let manifest;
256
+ try {
257
+ manifest = JSON.parse(await readFile(real, 'utf8'));
258
+ } catch {
259
+ return null;
260
+ }
261
+ const cliSessionId = manifest?.cliSessionId;
262
+ if (typeof cliSessionId !== 'string' || !COWORK_SESSION_ID_RE.test(cliSessionId)) return null;
263
+ const projects = join(real.slice(0, -'.json'.length), '.claude', 'projects');
264
+ for (const project of await entriesOf(projects)) {
265
+ if (!project.isDirectory()) continue;
266
+ const path = join(projects, project.name, `${cliSessionId}.jsonl`);
267
+ if (await containedRealpath(rootReal, path)) {
268
+ const title = typeof manifest.title === 'string' ? manifest.title.trim().slice(0, COWORK_TITLE_MAX_CHARS) : '';
269
+ return { path, title: title || null, starred: manifest.isStarred === true };
270
+ }
271
+ }
272
+ return null;
273
+ }
274
+
275
+ async function walkCowork(rootReal, files, extras) {
276
+ const found = await mapBounded(await coworkManifestPaths(rootReal), STAT_CONCURRENCY, (path) =>
277
+ coworkTranscript(rootReal, path)
278
+ );
279
+ for (const entry of found) {
280
+ if (!entry) continue;
281
+ files.push(entry.path);
282
+ extras.set(entry.path, { title: entry.title, starred: entry.starred });
283
+ }
284
+ }
285
+
218
286
  async function mapBounded(items, limit, fn) {
219
287
  const out = new Array(items.length);
220
288
  let next = 0;
@@ -231,6 +299,7 @@ async function mapBounded(items, limit, fn) {
231
299
  /** Regular files under the source root, canonical and contained, as candidates. */
232
300
  export async function enumerateCandidates(source, env = process.env) {
233
301
  const root = sourceRoot(source, env);
302
+ if (!root) return { root: '', candidates: [] };
234
303
  let rootReal;
235
304
  try {
236
305
  rootReal = await realpath(root);
@@ -238,13 +307,17 @@ export async function enumerateCandidates(source, env = process.env) {
238
307
  return { root, candidates: [] };
239
308
  }
240
309
  const paths = [];
310
+ const extras = new Map(); // transcript path → manifest title / star (Cowork)
241
311
  if (source === 'codex') await walkCodex(rootReal, rootReal, paths, new Set([caseFold(rootReal)]));
312
+ else if (source === 'cowork') await walkCowork(rootReal, paths, extras);
242
313
  else await walkClaude(rootReal, paths);
243
314
  const stamped = await mapBounded(paths, STAT_CONCURRENCY, async (path) => {
244
315
  const real = await containedRealpath(rootReal, path);
245
316
  if (!real) return null;
246
317
  const info = await stat(real).catch(() => null);
247
- return info?.isFile() ? { path: real, size: info.size, mtimeMs: info.mtimeMs, fileId: fileIdentity(info) } : null;
318
+ return info?.isFile()
319
+ ? { path: real, size: info.size, mtimeMs: info.mtimeMs, fileId: fileIdentity(info), ...extras.get(path) }
320
+ : null;
248
321
  });
249
322
  const unique = new Map();
250
323
  for (const file of stamped) {
@@ -261,7 +334,9 @@ function compareCodeUnits(left, right) {
261
334
  return left < right ? -1 : 1;
262
335
  }
263
336
 
337
+ // Starred conversations (Cowork) come first, then newest first.
264
338
  function byModifiedThenId(left, right) {
339
+ if (!left.starred !== !right.starred) return left.starred ? -1 : 1;
265
340
  if (right.mtimeMs !== left.mtimeMs) return right.mtimeMs - left.mtimeMs;
266
341
  return compareCodeUnits(left.id, right.id);
267
342
  }
@@ -418,7 +493,7 @@ async function readPrefixMeta(candidate) {
418
493
  // nothing but sidechain messages has been seen the scan streams on, line by
419
494
  // line, to EOF or to the import file-size limit. A file over that limit stays
420
495
  // unclassified (listed); importing it fails as tooLarge.
421
- const claude = candidate.source === 'claude';
496
+ const claude = candidate.source === 'claude' || candidate.source === 'cowork';
422
497
  let read;
423
498
  try {
424
499
  read = await streamJsonlLines(handle, (line) => converter.pushLine(line), {
@@ -793,7 +868,8 @@ function rowFor(candidate, meta, cwdInfo, imported, titles) {
793
868
  return {
794
869
  id: candidate.id,
795
870
  source: candidate.source,
796
- title: displayTitle(meta, titles),
871
+ title: displayTitle(meta, titles, candidate.title),
872
+ starred: candidate.starred === true,
797
873
  cwd: cwdInfo?.path ?? null,
798
874
  modifiedAt: candidate.mtimeMs,
799
875
  messageCount: meta?.messageCount ?? null,
@@ -946,7 +1022,10 @@ async function filterCandidates(candidates, filters, imported, titles) {
946
1022
  const cwdInfo = meta.cwd ? await resolveSourceCwd(meta.cwd, cwdCache) : null;
947
1023
  if (
948
1024
  needle &&
949
- !(foldText(displayTitle(meta, titles)).includes(needle) || foldText(cwdInfo?.path).includes(needle))
1025
+ !(
1026
+ foldText(displayTitle(meta, titles, candidate.title)).includes(needle) ||
1027
+ foldText(cwdInfo?.path).includes(needle)
1028
+ )
950
1029
  ) {
951
1030
  continue;
952
1031
  }
@@ -143,7 +143,7 @@ const titlesFor = (candidate, context) => (candidate.source === 'codex' ? contex
143
143
  function describeCandidate(candidateId, context) {
144
144
  const candidate = getCandidate(candidateId);
145
145
  return {
146
- title: candidate ? displayTitle(cachedMeta(candidate), titlesFor(candidate, context)) : null,
146
+ title: candidate ? displayTitle(cachedMeta(candidate), titlesFor(candidate, context), candidate.title) : null,
147
147
  sourceName: candidate ? basename(candidate.path) : null,
148
148
  };
149
149
  }
@@ -196,7 +196,8 @@ async function importOne(candidateId, context) {
196
196
  }
197
197
  const title = displayTitle(
198
198
  { foreignSessionId: conversion.foreignSessionId, title: listedTitle || conversion.title },
199
- titlesFor(candidate, context)
199
+ titlesFor(candidate, context),
200
+ candidate.title
200
201
  );
201
202
  if (title) conversion = { ...conversion, title };
202
203
  recordConversion(candidate, conversion);
@@ -1,6 +1,15 @@
1
1
  import { markPendingGoalReminder } from '../runtime/agent/orchestrator/runtime-core/goal-reminder.mjs';
2
+ import { adoptLegacyGoalTasks, todoContextBlock } from '../runtime/agent/orchestrator/runtime-core/todo-list.mjs';
3
+ import { SAVE_OUTCOME_SAVED } from '../runtime/agent/orchestrator/session/store/live-state.mjs';
2
4
 
3
- export function createGoalFacadeApi({ agentStatusState, createCurrentSession, getSession, getSessionId, goalRuntime }) {
5
+ export function createGoalFacadeApi({
6
+ agentStatusState,
7
+ createCurrentSession,
8
+ getSession,
9
+ getSessionId,
10
+ goalRuntime,
11
+ persistSession,
12
+ }) {
4
13
  const markGoalReminder = (reason = '') => {
5
14
  try {
6
15
  const pending = markPendingGoalReminder(getSession(), reason);
@@ -14,10 +23,39 @@ export function createGoalFacadeApi({ agentStatusState, createCurrentSession, ge
14
23
  return null;
15
24
  }
16
25
  };
26
+ // A Goal record written before the todo list still carries its tasks: the
27
+ // session's list adopts them, and only once that session is saved does the
28
+ // record let them go. A failed save keeps them on the record, so the next
29
+ // load adopts again (items already listed are skipped, never duplicated).
30
+ const adopting = new Map();
31
+ const adoptLegacyTasks = (sessionId) => {
32
+ if (adopting.has(sessionId)) return adopting.get(sessionId);
33
+ const tasks = goalRuntime.legacyTasks?.(sessionId) || [];
34
+ if (!tasks.length) return null;
35
+ const session = getSession();
36
+ if (session?.id !== sessionId) return null;
37
+ adoptLegacyGoalTasks(session, tasks);
38
+ const adoption = (async () => {
39
+ try {
40
+ // Only an explicit committed write lets the record go: a dropped or
41
+ // stale save resolves too, without the list on disk.
42
+ if ((await persistSession(session)) !== SAVE_OUTCOME_SAVED) return;
43
+ await goalRuntime.clearLegacyTasks(sessionId);
44
+ } catch {
45
+ // Kept on the record; retried on the next load.
46
+ } finally {
47
+ adopting.delete(sessionId);
48
+ }
49
+ })();
50
+ adopting.set(sessionId, adoption);
51
+ return adoption;
52
+ };
17
53
  return {
18
54
  goalStatus() {
19
55
  const sessionId = getSessionId();
20
- return sessionId ? goalRuntime.snapshot(sessionId) : null;
56
+ if (!sessionId) return null;
57
+ adoptLegacyTasks(sessionId);
58
+ return goalRuntime.snapshot(sessionId);
21
59
  },
22
60
  async goalControl(args = {}) {
23
61
  let sessionId = getSessionId();
@@ -31,10 +69,18 @@ export function createGoalFacadeApi({ agentStatusState, createCurrentSession, ge
31
69
  return result;
32
70
  },
33
71
  markGoalReminder,
72
+ /** Settles once a legacy Goal record's tasks are adopted and saved (or kept after a failed save). */
73
+ adoptLegacyGoalTasks(sessionId = getSessionId()) {
74
+ return Promise.resolve(sessionId ? adoptLegacyTasks(sessionId) : null);
75
+ },
34
76
  goalContinuation() {
35
77
  const sessionId = getSessionId();
36
78
  if (!sessionId) return { run: false, reason: 'missing-session', goal: null };
37
- return goalRuntime.continuation(sessionId, { agentStatus: agentStatusState() });
79
+ adoptLegacyTasks(sessionId);
80
+ return goalRuntime.continuation(sessionId, {
81
+ agentStatus: agentStatusState(),
82
+ todo: todoContextBlock(getSession(), 'Todo list for this Goal (exact wordings):', { whole: true }),
83
+ });
38
84
  },
39
85
  goalTurnStarted() {
40
86
  const sessionId = getSessionId();
@@ -8,6 +8,7 @@ import {
8
8
  ASK_USER_MAX_TEXT,
9
9
  } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
10
10
  import { createCallerContextResolvers } from './caller-context.mjs';
11
+ import { noteTodoUserWait } from '../../runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs';
11
12
  import { runAbortable } from '../../runtime/shared/abort-race.mjs';
12
13
  import {
13
14
  HANDOFF_OUTCOMES as AGENT_OUTCOMES,
@@ -309,6 +310,8 @@ export function createAskUserToolHandlers({ rt }) {
309
310
  normalizeAskUserAnswers(result.answers, questions),
310
311
  normalizeAskUserReply(result.reply)
311
312
  );
313
+ // A question left unanswered: the turn may end waiting on the user.
314
+ noteTodoUserWait(sessionIdFor(callerCtx));
312
315
  return DISMISSED;
313
316
  },
314
317
  };
@@ -68,8 +68,9 @@ export function createBridgeToolHandlers({ rt }) {
68
68
  if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_VISUALIZE')) {
69
69
  throw new Error('the visualize tool is disabled in this environment');
70
70
  }
71
- // A conversation keeps its frozen tool list, so honor the Settings toggle
72
- // at execution time too (an env override wins, as in builtinFeatureActive).
71
+ // The Settings toggle at execution time too: it reaches a turn already
72
+ // running and callers outside the session's deny set (an env override
73
+ // wins, as in builtinFeatureActive).
73
74
  if (!(featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ?? moduleEnabled(rt.config, 'visualize', true))) {
74
75
  return {
75
76
  content: [
@@ -1,19 +1,13 @@
1
- // Optional feature tools gated by settings for model-initiated calls: office,
2
- // media and tidy (each loaded on first use), and the setup tool.
1
+ // Optional feature tools: office, media and tidy (each loaded on first use;
2
+ // their settings gates apply in the executor), and the setup tool.
3
3
  import { standaloneDataDir } from '../runtime-paths.mjs';
4
4
  import { createCallerContextResolvers } from './caller-context.mjs';
5
5
 
6
- export function createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, mediaToolEnabled, tidyToolEnabled }) {
6
+ export function createFeatureToolHandlers({ rt, setupTool }) {
7
7
  const { sessionIdFor, signalFor } = createCallerContextResolvers(rt);
8
- const requireEnabled = (callerCtx, enabled, label) => {
9
- if (callerCtx?.invocationSource === 'model-tool' && !enabled()) {
10
- throw new Error(`${label} is disabled in settings; start a new session to refresh the tool list`);
11
- }
12
- };
13
8
 
14
9
  return {
15
10
  office: async (args, { callerCtx, callerCwd }) => {
16
- requireEnabled(callerCtx, officeToolsEnabled, 'office');
17
11
  const { executeOfficeTool } = await import('../../runtime/office/index.mjs');
18
12
  return await executeOfficeTool(args, {
19
13
  cwd: callerCwd,
@@ -25,7 +19,6 @@ export function createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, m
25
19
  });
26
20
  },
27
21
  media: async (args, { callerCtx, callerCwd }) => {
28
- requireEnabled(callerCtx, mediaToolEnabled, 'media');
29
22
  const { executeMediaTool } = await import('../../runtime/media/tool.mjs');
30
23
  const { getSession } = await import('../../runtime/agent/orchestrator/session/manager/session-crud.mjs');
31
24
  // Generation spend is attributed to the calling session in the usage ledger.
@@ -38,7 +31,6 @@ export function createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, m
38
31
  });
39
32
  },
40
33
  tidy: async (args, { callerCtx, callerCwd }) => {
41
- requireEnabled(callerCtx, tidyToolEnabled, 'tidy');
42
34
  const { executeTidyTool } = await import('../../runtime/tidy/tool.mjs');
43
35
  return await executeTidyTool(args, {
44
36
  cwd: callerCwd,
@@ -17,6 +17,7 @@ import { scheduleCallerScope } from '../services/schedule-access.mjs';
17
17
  import { worktreeCallerScope } from '../services/worktree-access.mjs';
18
18
  import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
19
19
  import { delegationChainOf } from '../bot-chain.mjs';
20
+ import { openTodoItems } from '../../runtime/agent/orchestrator/runtime-core/todo-list.mjs';
20
21
  import { BOT_TARGET_ACTIONS, resolveBotTarget } from '../bot-target.mjs';
21
22
 
22
23
  const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
@@ -322,9 +323,21 @@ export function createWorkspaceToolHandlers({
322
323
  goal: async (args, { name, callerCtx }) => {
323
324
  // A Goal keeps working in its session across turns: a heartbeat's would
324
325
  // run on unseen in the hidden session.
325
- if (['create', 'resume'].includes(clean(args?.action).toLowerCase()) && callerIsHeartbeat(callerCtx)) {
326
+ const action = clean(args?.action).toLowerCase();
327
+ if (action === 'create' && callerIsHeartbeat(callerCtx)) {
326
328
  throw new Error(`goal: ${HEARTBEAT_NO_HANDOFF}`);
327
329
  }
330
+ // A completion claim with steps still open in the session's own list
331
+ // has not finished the work it recorded.
332
+ const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
333
+ if (action === 'complete' && rt.session?.id === callerSessionId) {
334
+ const open = openTodoItems(rt.session);
335
+ if (open.length) {
336
+ throw new Error(
337
+ `goal: cannot complete with ${open.length} open todo item(s) (${open.map((task) => task.content).join('; ')}); finish them, or drop or block each with its reason`
338
+ );
339
+ }
340
+ }
328
341
  return await goalRuntime.executeTool(name, args || {}, {
329
342
  callerSessionId: callerCtx?.callerSessionId || rt.session?.id || rt.reservedSessionId || null,
330
343
  });
@@ -335,9 +348,10 @@ export function createWorkspaceToolHandlers({
335
348
  if (['spawn', 'send'].includes((clean(args?.type) || 'spawn').toLowerCase()) && callerIsHeartbeat(callerCtx)) {
336
349
  throw new Error(`agent: ${HEARTBEAT_NO_HANDOFF}`);
337
350
  }
338
- // `attachmentParts` is never the model's to set: only the resolved
339
- // `attachments` reach the agent's turn.
340
- const { attachments, attachmentParts: _ignored, ...rest } = args || {};
351
+ // `attachmentParts` and `skillSelection` are never the model's to set:
352
+ // only the resolved `attachments` reach the agent's turn, and its skills
353
+ // are selected from its whole prompt.
354
+ const { attachments, attachmentParts: _ignored, skillSelection: _selection, ...rest } = args || {};
341
355
  const attachmentParts = callerAttachmentParts(rt, { attachments }, callerCwd, 'agent');
342
356
  return await agentTool.execute(attachmentParts.length ? { ...rest, attachmentParts } : rest, {
343
357
  callerCwd,