mixdog 1.3.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/LICENSES/oh-my-pi-MIT.txt +23 -0
  2. package/NOTICE.md +17 -2
  3. package/README.md +11 -0
  4. package/package.json +3 -11
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/embed-google-oauth-client.mjs +49 -0
  7. package/scripts/gate-local.mjs +4 -2
  8. package/scripts/lib/test-spawn-ledger.mjs +46 -1
  9. package/scripts/native-release-status.mjs +2 -2
  10. package/scripts/recall-history-cases.json +1 -1
  11. package/scripts/suite-health.mjs +10 -1
  12. package/scripts/test.mjs +12 -11
  13. package/src/agents/reviewer/AGENT.md +7 -11
  14. package/src/agents/reviewer/agent.json +1 -1
  15. package/src/defaults/shipped-history.json +25 -1
  16. package/src/defaults/skills/browser-use/SKILL.md +77 -17
  17. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  18. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  19. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  20. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  21. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  22. package/src/defaults/skills/setup/SKILL.md +29 -2
  23. package/src/defaults/skills/setup/references/actions.md +117 -28
  24. package/src/defaults/skills/setup/references/surfaces.md +77 -35
  25. package/src/defaults/skills/setup-guide/SKILL.md +3 -3
  26. package/src/defaults/skills/visualize/SKILL.md +2 -1
  27. package/src/lib/keychain-cjs.cjs +359 -29
  28. package/src/lib/rules-builder.cjs +10 -8
  29. package/src/rules/agent/AGENT.md +6 -6
  30. package/src/rules/agent-mode/AGENT_MODE.md +10 -2
  31. package/src/rules/agent-mode/MIXY.md +12 -0
  32. package/src/rules/lead/LEAD.md +18 -20
  33. package/src/rules/shared/05-tool-calls.md +5 -6
  34. package/src/rules/shared/10-tool-workflow.md +2 -0
  35. package/src/rules/shared/60-verification.md +7 -5
  36. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  37. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
  38. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  39. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/config.mjs +34 -0
  41. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  42. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  43. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  44. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  45. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
  46. package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
  49. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
  50. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
  51. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
  53. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
  54. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
  55. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
  56. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  60. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
  61. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  63. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
  65. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
  66. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
  67. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
  68. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
  69. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
  70. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  71. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
  73. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
  74. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  75. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
  76. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
  77. package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
  78. package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
  79. package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
  81. package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
  82. package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
  83. package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
  84. package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
  85. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
  86. package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
  87. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
  88. package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
  89. package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
  90. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
  91. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
  92. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
  93. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
  94. package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
  95. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
  97. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  98. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
  99. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  100. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  101. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  109. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  113. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
  114. package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
  115. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  116. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  117. package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
  118. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  119. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  120. package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
  121. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  122. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
  127. package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
  128. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  131. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  132. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
  133. package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
  134. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
  135. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
  136. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
  137. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  138. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
  139. package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
  140. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  141. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
  142. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  143. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  144. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
  145. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
  146. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
  147. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  148. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
  149. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  151. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  152. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
  153. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
  154. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
  155. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  156. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
  157. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  158. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
  159. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  163. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
  164. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
  165. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  166. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  167. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
  168. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  172. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  173. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  174. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  175. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  176. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  177. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  178. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  179. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  180. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
  182. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  183. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  184. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  185. package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
  186. package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
  187. package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
  188. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  189. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  191. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
  192. package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
  193. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  203. package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  207. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  208. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  209. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
  214. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
  216. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  217. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  218. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  219. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  225. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  226. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  227. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  228. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  229. package/src/runtime/browser-bridge/client.mjs +73 -8
  230. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  231. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  232. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  233. package/src/runtime/channels/lib/config.mjs +6 -61
  234. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  235. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  236. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  237. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  238. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  239. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  240. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  241. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  242. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  243. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  244. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  245. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  246. package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
  247. package/src/runtime/channels/lib/scheduler.mjs +135 -154
  248. package/src/runtime/channels/lib/schedules-db.mjs +83 -11
  249. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  250. package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
  251. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  252. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  253. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  254. package/src/runtime/channels/lib/worker-main.mjs +19 -34
  255. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  256. package/src/runtime/computer-bridge/actions.mjs +9 -1
  257. package/src/runtime/computer-bridge/client.mjs +180 -8
  258. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  259. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  260. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  261. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  262. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  263. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  264. package/src/runtime/connectors/google-mcp.mjs +10 -12
  265. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  266. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  268. package/src/runtime/office/com/com-adapter.mjs +17 -1
  269. package/src/runtime/office/com/office-com-host.ps1 +51 -1
  270. package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
  271. package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
  272. package/src/runtime/office/pdf/document-preview.mjs +326 -8
  273. package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
  274. package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
  275. package/src/runtime/office/portable/portable-soffice.mjs +45 -5
  276. package/src/runtime/session-search/session-search-db.mjs +73 -9
  277. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  278. package/src/runtime/shared/abort-race.mjs +89 -0
  279. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  280. package/src/runtime/shared/agent-kind.mjs +4 -3
  281. package/src/runtime/shared/bot-hops.mjs +62 -2
  282. package/src/runtime/shared/bounded-download.mjs +8 -3
  283. package/src/runtime/shared/config.mjs +51 -35
  284. package/src/runtime/shared/connector-accounts.mjs +20 -10
  285. package/src/runtime/shared/connector-catalog.d.mts +33 -2
  286. package/src/runtime/shared/connector-catalog.mjs +166 -8
  287. package/src/runtime/shared/connector-triggers.mjs +22 -19
  288. package/src/runtime/shared/defaults-separation.mjs +2 -14
  289. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  290. package/src/runtime/shared/developer-options.mjs +32 -1
  291. package/src/runtime/shared/err-text.mjs +24 -15
  292. package/src/runtime/shared/file-permissions.d.mts +0 -8
  293. package/src/runtime/shared/file-permissions.mjs +0 -11
  294. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  295. package/src/runtime/shared/google-oauth-client.generated.json +4 -0
  296. package/src/runtime/shared/google-oauth-client.mjs +34 -9
  297. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  298. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  299. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  300. package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
  301. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
  302. package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
  303. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  304. package/src/runtime/shared/llm/well-formed.mjs +66 -0
  305. package/src/runtime/shared/mixy-presence.mjs +22 -0
  306. package/src/runtime/shared/orchestration.d.mts +2 -0
  307. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  308. package/src/runtime/shared/profile-config.cjs +1 -1
  309. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  310. package/src/runtime/shared/prompt-origin.mjs +21 -0
  311. package/src/runtime/shared/provider-accounts.mjs +231 -59
  312. package/src/runtime/shared/provider-api-key.mjs +19 -8
  313. package/src/runtime/shared/provider-display-name.mjs +29 -0
  314. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  315. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  316. package/src/runtime/shared/routine-trigger.mjs +52 -0
  317. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  318. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  319. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  320. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  321. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  322. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  323. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  324. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  325. package/src/runtime/shared/transcript-metadata.mjs +38 -0
  326. package/src/runtime/shared/user-data-guard.mjs +30 -2
  327. package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
  328. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  329. package/src/runtime/terminal-bridge/client.mjs +10 -1
  330. package/src/runtime/visualize-bridge/client.mjs +27 -8
  331. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  332. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  333. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  334. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  335. package/src/session-runtime/agent-home-delivery.mjs +486 -179
  336. package/src/session-runtime/agent-room-delivery.mjs +28 -1
  337. package/src/session-runtime/agent-task-sessions.mjs +561 -199
  338. package/src/session-runtime/boot/apis.mjs +9 -0
  339. package/src/session-runtime/boot/begin.mjs +3 -2
  340. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  341. package/src/session-runtime/boot/providers.mjs +3 -0
  342. package/src/session-runtime/boot/tools.mjs +8 -1
  343. package/src/session-runtime/bot-chain.mjs +32 -0
  344. package/src/session-runtime/bot-status.mjs +7 -4
  345. package/src/session-runtime/channel-config-api.mjs +63 -17
  346. package/src/session-runtime/config-lifecycle.mjs +6 -0
  347. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  348. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  349. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  350. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
  351. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
  352. package/src/session-runtime/internal-tool-executor.mjs +4 -0
  353. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  354. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  355. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  356. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  357. package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
  358. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  359. package/src/session-runtime/mixy-heartbeat.mjs +927 -0
  360. package/src/session-runtime/model-route/route-tuning.mjs +10 -6
  361. package/src/session-runtime/notification-bus.mjs +31 -117
  362. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  363. package/src/session-runtime/notification-listeners.mjs +76 -0
  364. package/src/session-runtime/owned-automation-run.mjs +35 -13
  365. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  366. package/src/session-runtime/provider-auth/login.mjs +7 -0
  367. package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
  368. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  369. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  370. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  371. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  372. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  373. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  374. package/src/session-runtime/routine-home-turn.mjs +33 -6
  375. package/src/session-runtime/routine-owner.mjs +40 -9
  376. package/src/session-runtime/schedule-session-run.mjs +7 -3
  377. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  378. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
  379. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  380. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  381. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  382. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  383. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  384. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  385. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  386. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  387. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  388. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  389. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  390. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  391. package/src/session-runtime/services/channel-admin.mjs +73 -31
  392. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  393. package/src/session-runtime/services/channel-worker.mjs +1 -1
  394. package/src/session-runtime/services/provider-admin.mjs +128 -25
  395. package/src/session-runtime/services/schedule-access.mjs +14 -0
  396. package/src/session-runtime/services/seeds.mjs +0 -28
  397. package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
  398. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  399. package/src/session-runtime/session-create/session-options.mjs +8 -4
  400. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  401. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  402. package/src/session-runtime/settings-system-api.mjs +28 -1
  403. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  404. package/src/session-runtime/setup-tool/executor.mjs +187 -29
  405. package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
  406. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  407. package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
  408. package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
  409. package/src/session-runtime/task-session-route.mjs +99 -0
  410. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  411. package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
  412. package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
  413. package/src/session-runtime/tool-surface.mjs +4 -0
  414. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  415. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
  416. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  417. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
  418. package/src/session-runtime/workspace/transitions.mjs +10 -2
  419. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  420. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  421. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  422. package/src/standalone/channel-transport.mjs +1 -1
  423. package/src/standalone/daemon-channels-loader.mjs +10 -8
  424. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  425. package/src/standalone/daemon-session-control/request.mjs +51 -0
  426. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  427. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  428. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  429. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  430. package/src/standalone/daemon-session-control.mjs +161 -157
  431. package/src/standalone/daemon.mjs +33 -2
  432. package/src/standalone/session-client.mjs +7 -3
  433. package/src/standalone/session-protocol.mjs +10 -0
  434. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
  435. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  436. package/src/standalone/session-service/quota-resume.mjs +3 -0
  437. package/src/standalone/session-service/retention/busy.mjs +65 -1
  438. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  439. package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
  440. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  441. package/src/standalone/session-service.mjs +13 -0
  442. package/src/standalone/session-tool-output.mjs +21 -10
  443. package/src/tui/app/model-options.mjs +4 -27
  444. package/src/tui/dist/index.mjs +81 -65
  445. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  446. package/src/tui/session/initial-state.mjs +3 -0
  447. package/src/tui/session/notice-surface.mjs +23 -1
  448. package/src/tui/session/queue-helpers.mjs +3 -42
  449. package/src/tui/session/session-api/integrations.mjs +1 -0
  450. package/src/tui/session/session-api/lifecycle.mjs +8 -0
  451. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  452. package/src/tui/session/session-api/routes-write.mjs +5 -1
  453. package/src/tui/session/session-api/routes.mjs +1 -0
  454. package/src/tui/session/session-api/settings.mjs +30 -2
  455. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  456. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  457. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  458. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  459. package/src/tui/session/session-flow/drain.mjs +118 -149
  460. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  461. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  462. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  463. package/src/tui/session/tool-approval-request.mjs +39 -0
  464. package/src/tui/session/tool-approval-settle.mjs +62 -0
  465. package/src/tui/session/tool-approval.mjs +69 -115
  466. package/src/tui/session/tui-steering-persist.mjs +8 -1
  467. package/src/tui/session/turn-stream.mjs +36 -3
  468. package/src/tui/session/turn.mjs +23 -2
  469. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  470. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  471. package/scripts/code-graph-description-contract.mjs +0 -188
  472. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -1,4 +1,4 @@
1
- import { join } from 'node:path';
1
+ import { dirname, isAbsolute, join, relative, sep } from 'node:path';
2
2
  import { createHash } from 'node:crypto';
3
3
  import {
4
4
  accessSync,
@@ -7,18 +7,19 @@ import {
7
7
  mkdirSync,
8
8
  readFileSync,
9
9
  readdirSync,
10
+ realpathSync,
10
11
  renameSync,
11
12
  rmSync,
12
13
  statSync,
13
14
  } from 'node:fs';
14
15
  import {
15
16
  holdsNoData,
16
- parseJsonText,
17
17
  readJsonStateSync,
18
18
  writeFileAtomicSync,
19
19
  writeJsonAtomicSync,
20
20
  } from '../../runtime/shared/atomic-file.mjs';
21
21
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
22
+ import { agentEntryFile, agentEntryPath, parseManifestText } from '../../runtime/agent/orchestrator/runtime-core/workflow/agents.mjs';
22
23
  import { hasOwn } from '../../runtime/shared/object.mjs';
23
24
  import { serializeFrontmatterDoc } from '../../runtime/shared/markdown-frontmatter.mjs';
24
25
  import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.mjs';
@@ -79,9 +80,43 @@ import {
79
80
 
80
81
  const isFixedAgent = (id) => FIXED_AGENT_SLOTS.some((agent) => agent.id === id);
81
82
  const userDir = userDefinitionDir;
82
- const hasUserAgentFile = (dataDir, id) => existsSync(join(userDir(dataDir, id), 'AGENT.md'));
83
- // An archived (deleted) agent's AGENT.md, moved aside beside the tombstone:
84
- // no loader reads it, and restoring moves it back.
83
+ // The entry file of the agent folder `dir`, exactly as the loader reads it
84
+ // (agent.json `entry`, AGENT.md by default).
85
+ const entryIn = (dir) => agentEntryPath(dir, readManifest(dir));
86
+ const userEntryPath = (dataDir, id) => entryIn(userDir(dataDir, id));
87
+ // An agent exists where its folder has an AGENT.md, as discovery lists it.
88
+ const hasEntryFile = (dir) => existsSync(join(dir, 'AGENT.md'));
89
+ const hasUserAgentFile = (dataDir, id) => hasEntryFile(userDir(dataDir, id));
90
+ const leavesFolder = (rel) => rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);
91
+ // The file delete moves aside and restore moves back: the agent's entry when
92
+ // it is named inside `dir` and its folder resolves inside too (a symlink as the
93
+ // last part moves as the link alone), else HEAD's AGENT.md. An entry outside the
94
+ // folder (`..`, or through a linked folder) is shared and never moved.
95
+ const archivableEntry = (dir, entry) => {
96
+ try {
97
+ const rel = relative(dir, entry);
98
+ if (rel && !leavesFolder(rel) && !leavesFolder(relative(realpathSync(dir), realpathSync(dirname(entry))))) {
99
+ return entry;
100
+ }
101
+ } catch {
102
+ // an unresolvable folder holds no entry to move
103
+ }
104
+ return join(dir, 'AGENT.md');
105
+ };
106
+ // The entry file the editor may hand out to edit in place: it resolves (symlinks
107
+ // followed) to a file inside its own agent folder, else null. The loader still
108
+ // reads an entry outside; only the editable path is contained.
109
+ const editablePathIn = (dir, path) => {
110
+ try {
111
+ return leavesFolder(relative(realpathSync(dir), realpathSync(path))) ? null : path;
112
+ } catch {
113
+ return null;
114
+ }
115
+ };
116
+ // An archived (deleted) agent's entry file, moved aside beside the tombstone
117
+ // as `<entry>.archived`: no loader reads it, and restoring moves it back.
118
+ const archivedEntryPath = (entryPath) => `${entryPath}.archived`;
119
+ // The default entry's archive (all that HEAD wrote); a save clears it.
85
120
  const ARCHIVED_AGENT_FILE = 'AGENT.md.archived';
86
121
 
87
122
  // Frontmatter the editor does not show (e.g. `permission`), taken from the
@@ -123,15 +158,19 @@ const writeManifest = (file, manifest) => writeJsonAtomicSync(file, manifest, DE
123
158
  // no data (empty, or NUL-filled by a crash) or is not a JSON object.
124
159
  function readManifestStrict(file) {
125
160
  try {
126
- const text = readFileSync(file, 'utf8');
127
- if (holdsNoData(text)) return null;
128
- const value = parseJsonText(text);
129
- return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
161
+ return readManifestFile(file);
130
162
  } catch {
131
163
  return null;
132
164
  }
133
165
  }
134
166
 
167
+ // The same, but a file that cannot be read throws its error.
168
+ function readManifestFile(file) {
169
+ const text = readFileSync(file, 'utf8');
170
+ if (holdsNoData(text)) return null;
171
+ return parseManifestText(text);
172
+ }
173
+
135
174
  // A read-only agent.json is a standing refusal, not a transient lock: the
136
175
  // atomic write's rename onto it fails with EPERM and is retried for seconds,
137
176
  // synchronously on the host's event loop. Refuse it up front (one stat, no wait),
@@ -559,10 +598,16 @@ function definitionPresence({ dataDir, rootDir, stat }, agentIds) {
559
598
  // A definition folder's revision: a hash of the three files an editor writes.
560
599
  function definitionDirRevision(dir) {
561
600
  const hash = createHash('sha256');
562
- for (const name of ['agent.json', 'AGENT.md', PERSONA_FILE]) {
601
+ const manifest = readManifest(dir);
602
+ const files = [
603
+ ['agent.json', join(dir, 'agent.json')],
604
+ [agentEntryFile(manifest), agentEntryPath(dir, manifest)],
605
+ [PERSONA_FILE, join(dir, PERSONA_FILE)],
606
+ ];
607
+ for (const [name, file] of files) {
563
608
  hash.update(`${name}\0`);
564
609
  try {
565
- hash.update(readFileSync(join(dir, name)));
610
+ hash.update(readFileSync(file));
566
611
  } catch {
567
612
  hash.update('\u0001absent');
568
613
  }
@@ -660,13 +705,27 @@ function createAgentArchiveApi({
660
705
  await announceBotChange(dataDir);
661
706
  return { id, deleted: true, revertedToBuiltIn: Boolean(sourceFallback) };
662
707
  }
663
- // A custom agent is archived: its AGENT.md (what makes it an agent
664
- // anywhere) moves aside, the tombstone keeps the packaged copy of a
665
- // starter from coming back, and the rest (agent.json: name and face,
666
- // persona, memory, its model and on/off) stays for its archived chat —
667
- // until restoreAgentDefinition brings it back or purgeAgentDefinition.
708
+ // A custom agent is archived: its entry file (AGENT.md, or the one its
709
+ // agent.json names; what makes it an agent anywhere) moves aside, the
710
+ // tombstone keeps the packaged copy of a starter from coming back, and the
711
+ // rest (agent.json: name and face, persona, memory, its model and on/off)
712
+ // stays for its archived chat — until restoreAgentDefinition brings it back
713
+ // or purgeAgentDefinition.
714
+ // AGENT.md always moves aside; an AGENT.md over an existing archive is
715
+ // refused, since the move would overwrite it. Another selected entry
716
+ // moves too when its own archive is free, and otherwise stays in place
717
+ // under the tombstone.
718
+ const agentFile = join(dir, 'AGENT.md');
719
+ const agentArchive = archivedEntryPath(agentFile);
720
+ if (existsSync(agentFile) && existsSync(agentArchive)) {
721
+ throw new Error(`agent "${id}" cannot be deleted: ${agentArchive} already exists; move or remove it first`);
722
+ }
723
+ const entry = archivableEntry(dir, entryIn(dir));
668
724
  mkdirSync(dir, { recursive: true });
669
- if (existsSync(join(dir, 'AGENT.md'))) renameSync(join(dir, 'AGENT.md'), join(dir, ARCHIVED_AGENT_FILE));
725
+ if (existsSync(agentFile)) renameSync(agentFile, agentArchive);
726
+ if (entry !== agentFile && existsSync(entry) && !existsSync(archivedEntryPath(entry))) {
727
+ renameSync(entry, archivedEntryPath(entry));
728
+ }
670
729
  writeFileAtomicSync(join(dir, AGENT_DELETED_MARKER), 'deleted\n');
671
730
  clearAgentDefinitionCache(id);
672
731
  // The Leads listing it drop it from their roster from their next turn.
@@ -683,11 +742,22 @@ function createAgentArchiveApi({
683
742
  const dataDir = dataDirOf();
684
743
  if (!isArchivedAgent(dataDir, id)) throw new Error(`agent "${id}" is not deleted`);
685
744
  const dir = userDir(dataDir, id);
686
- const kept = join(dir, ARCHIVED_AGENT_FILE);
687
- if (!existsSync(kept) && !existsSync(join(shippedDir(id), 'AGENT.md'))) {
745
+ // AGENT.md.archived back to AGENT.md, and the selected entry's archive back
746
+ // to that entry. Nothing is overwritten: an archive whose file exists again
747
+ // stays as it is, and when every kept archive collides the restore is refused.
748
+ const agentFile = join(dir, 'AGENT.md');
749
+ const entry = archivableEntry(dir, entryIn(dir));
750
+ const moves = [{ from: join(dir, ARCHIVED_AGENT_FILE), to: agentFile }];
751
+ if (entry !== agentFile) moves.push({ from: archivedEntryPath(entry), to: entry });
752
+ const kept = moves.filter((move) => existsSync(move.from));
753
+ if (kept.length === 0 && !hasEntryFile(shippedDir(id))) {
688
754
  throw new Error(`agent "${id}" was deleted before deleted agents were kept, so it cannot be restored`);
689
755
  }
690
- if (existsSync(kept)) renameSync(kept, join(dir, 'AGENT.md'));
756
+ const free = kept.filter((move) => !existsSync(move.to));
757
+ if (kept.length > 0 && free.length === 0) {
758
+ throw new Error(`agent "${id}" cannot be restored: ${kept[0].to} already exists; move or remove it first`);
759
+ }
760
+ for (const move of free) renameSync(move.from, move.to);
691
761
  rmSync(join(dir, AGENT_DELETED_MARKER), { force: true });
692
762
  clearAgentDefinitionCache(id);
693
763
  // Back in the roster of the Leads that list it, from their next turn.
@@ -795,13 +865,20 @@ export function createAgentEditorApi(deps) {
795
865
  const dataDir = resolveDataDir(deps);
796
866
  const definition = loadAgentDefinition(dataDir, id);
797
867
  if (!definition) throw new Error(`agent "${agentId}" not found`);
868
+ const userOverride = hasUserAgentFile(dataDir, id);
798
869
  return {
799
870
  id,
800
871
  name: definition.name,
801
872
  description: definition.description,
802
873
  body: definition.body,
803
874
  custom: !isFixedAgent(id),
804
- userOverride: hasUserAgentFile(dataDir, id),
875
+ userOverride,
876
+ // The user copy's entry file, editable in place; a shipped-only agent has
877
+ // none (an install-dir edit would be lost on update), nor has an entry
878
+ // that resolves outside its folder (the body is still returned).
879
+ ...(userOverride && editablePathIn(userDir(dataDir, id), userEntryPath(dataDir, id))
880
+ ? { path: userEntryPath(dataDir, id) }
881
+ : {}),
805
882
  route: routeFor(getConfig(), id),
806
883
  routeConfigured: routeConfigured(getConfig(), id),
807
884
  kind: kindOf(definition),
@@ -64,6 +64,10 @@ export function createStyleAndModeApi(deps) {
64
64
  listOutputStyles: () => getOutputStyleStatusCached(),
65
65
  setOutputStyle,
66
66
  getOrchestrationMode: () => configuredOrchestrationMode(displayConfig()),
67
+ // The default new sessions inherit. The runtime facade's own
68
+ // getOrchestrationMode answers for the ACTIVE session when one exists, so a
69
+ // New Task composer reads this one: it never depends on which session is open.
70
+ getConfiguredOrchestrationMode: () => configuredOrchestrationMode(displayConfig()),
67
71
  setOrchestrationMode,
68
72
  };
69
73
  }
@@ -77,13 +77,18 @@ export function createWorkflowPacksApi(deps) {
77
77
  const dataDir = resolveDataDir(deps);
78
78
  const pack = loadWorkflowPack(dataDir, id);
79
79
  if (!pack || pack.id !== id) throw new Error(`workflow "${workflowId}" not found`);
80
+ // The user copy's file, so a part of a long body can be edited in place;
81
+ // a shipped-only pack has none (its install copy is replaced on update).
82
+ const file = join(dataDir, 'workflows', id, 'WORKFLOW.md');
83
+ const userOverride = existsSync(file);
80
84
  return {
81
85
  id: pack.id,
82
86
  name: pack.name,
83
87
  description: pack.description,
84
88
  source: pack.source,
85
89
  body: pack.body,
86
- userOverride: existsSync(join(dataDir, 'workflows', id, 'WORKFLOW.md')),
90
+ userOverride,
91
+ ...(userOverride ? { path: file } : {}),
87
92
  };
88
93
  }
89
94
 
@@ -426,7 +426,7 @@ export function createWorkspaceTransitions({
426
426
  * refuses while a session or worker runs in an affected folder, or a
427
427
  * background command does; `removing` refuses while any session still uses
428
428
  * one. Then holds the folders — Mixdog checkouts only; an external checkout
429
- * is never lock-blocked — so new Mixdog work there waits until `run`
429
+ * takes no lease from this profile — so new Mixdog work there waits until `run`
430
430
  * settles. `actor`: the session acting on its own checkout, whose own turn
431
431
  * and workers its caller already checked (assertLeadIdle).
432
432
  */
@@ -985,7 +985,15 @@ export function createWorkspaceTransitions({
985
985
  * `ownTurn` (internal: the Lead's own tool call) tolerates the calling turn.
986
986
  */
987
987
  async function requestWorktree(
988
- { name = null, confirmLarge = false, provision = false, carryChanges, prompt = '', txId = '', expectedRevision } = {},
988
+ {
989
+ name = null,
990
+ confirmLarge = false,
991
+ provision = false,
992
+ carryChanges,
993
+ prompt = '',
994
+ txId = '',
995
+ expectedRevision,
996
+ } = {},
989
997
  { ownTurn = false } = {}
990
998
  ) {
991
999
  const nameHint = validName(name);
@@ -0,0 +1,65 @@
1
+ // The live client records of the channel transport: pid-liveness reads and
2
+ // pruning, the one removal primitive every path uses, and the UI control
3
+ // pointer lookup. `replays` and `grace` are the registry's replay records and
4
+ // self-shutdown grace timer.
5
+ import { isPidAlive } from '../../../runtime/shared/pid-liveness.mjs';
6
+
7
+ export function createClientRecords({ state, log, publishRemoteState, replays, grace }) {
8
+ const { clients } = state;
9
+
10
+ // pid death is the authoritative signal for client liveness: a client whose
11
+ // SSE stream closed may still reconnect, one whose lead pid is gone cannot.
12
+ function pruneDeadClients() {
13
+ for (const [token, c] of clients) {
14
+ if (!isPidAlive(c.leadPid)) dropClient(token, 'pid dead');
15
+ }
16
+ }
17
+
18
+ function liveClients() {
19
+ const out = [];
20
+ for (const [token, c] of clients) {
21
+ if (isPidAlive(c.leadPid)) out.push([token, c]);
22
+ }
23
+ return out;
24
+ }
25
+
26
+ // Every removal path uses this primitive, including replacement's deliberate
27
+ // no-failover retirement, so replay records/timers never target absent tokens.
28
+ function removeClientRecord(token) {
29
+ const c = clients.get(token);
30
+ if (!c) return null;
31
+ clients.delete(token);
32
+ replays.forgetFor(token);
33
+ try {
34
+ c.sse?.end?.();
35
+ } catch {
36
+ // The stream may already be closed by the peer.
37
+ }
38
+ return c;
39
+ }
40
+
41
+ function dropClient(token, reason) {
42
+ const c = removeClientRecord(token);
43
+ if (!c) return;
44
+ if (state.pointerToken === token) state.pointerToken = null;
45
+ log(`client ${token} (lead=${c.leadPid}) removed: ${reason}`);
46
+ // Client presence is not channel authority. The durable session pin stays
47
+ // active until that same session explicitly turns Remote OFF.
48
+ publishRemoteState();
49
+ grace.maybeArm('client removed');
50
+ }
51
+
52
+ // Resolve the active UI pointer for state delivery only. Session routing
53
+ // never depends on this client.
54
+ function resolveTarget() {
55
+ if (state.pointerToken) {
56
+ const c = clients.get(state.pointerToken);
57
+ if (c && isPidAlive(c.leadPid)) return c;
58
+ if (c) dropClient(state.pointerToken, 'pid dead (notify-time)');
59
+ else state.pointerToken = null;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ return { pruneDeadClients, liveClients, removeClientRecord, dropClient, resolveTarget };
65
+ }
@@ -0,0 +1,118 @@
1
+ // Client registration for the channel transport: a fresh record per attach,
2
+ // a passive reconnect's response-loss replay, the replaced token's retirement
3
+ // with its token-scoped state carried over, and the replacement's
4
+ // cancellation. `records` is the registry's client records (client-records).
5
+ import { randomUUID } from 'node:crypto';
6
+ import { parsePid } from '../../../runtime/shared/pid-liveness.mjs';
7
+ import { replayMatchesCancellation, replayMatchesRegistration } from './registration-replays.mjs';
8
+
9
+ export function createClientRegistration({ state, log, onClientRegistered, replays, grace, records }) {
10
+ const { clients } = state;
11
+
12
+ function cancelReplacementRegistration(request) {
13
+ const replay = replays.lookup(request.registrationId);
14
+ if (!replay) return 'missing';
15
+ if (!replayMatchesCancellation(replay, request)) return 'forbidden';
16
+ records.dropClient(replay.token, 'replacement deregister');
17
+ return 'cancelled';
18
+ }
19
+
20
+ /** A passive reconnect retrying under a known registration id gets the fresh
21
+ * token it already created; a different identity under that id is a 409. */
22
+ function replayedToken(replayId, identity) {
23
+ const existing = replays.lookup(replayId);
24
+ if (!existing) return null;
25
+ if (replayMatchesRegistration(existing, identity) && clients.has(existing.token)) {
26
+ replays.arm(replayId, existing);
27
+ log(`client reconnect replay token=${existing.token} lead=${identity.pid}`);
28
+ return existing.token;
29
+ }
30
+ if (clients.has(existing.token)) {
31
+ const err = new Error('registration replay identity mismatch');
32
+ err.statusCode = 409;
33
+ throw err;
34
+ }
35
+ replays.remove(replayId, existing);
36
+ return null;
37
+ }
38
+
39
+ /** A reconnect names the exact token it replaces. Token-scoped state belongs
40
+ * to the logical client, so the fresh record inherits the buffered frame,
41
+ * remote session and pointer; the old token is retired even when it is not
42
+ * the pointer — retaining it would let it later call or accumulate a
43
+ * buffered frame after the fresh client has gone away. */
44
+ function adoptReplacedClient(fresh, replaced) {
45
+ const replacedWasPointer = state.pointerToken === replaced.token;
46
+ if (replaced.pendingRemoteStateFrame) {
47
+ fresh.pendingRemoteStateFrame = replaced.pendingRemoteStateFrame;
48
+ replaced.pendingRemoteStateFrame = null;
49
+ }
50
+ if (replaced.remoteSessionId) fresh.remoteSessionId = replaced.remoteSessionId;
51
+ if (replaced.restoreSessionId) fresh.restoreSessionId = replaced.restoreSessionId;
52
+ if (replacedWasPointer) state.pointerToken = fresh.token;
53
+ records.removeClientRecord(replaced.token);
54
+ log(`client reconnect replaced token=${replaced.token} -> ${fresh.token} lead=${fresh.leadPid}`);
55
+ }
56
+
57
+ function registerClient({
58
+ leadPid,
59
+ cwd,
60
+ passive = false,
61
+ replaceToken = null,
62
+ registrationId = null,
63
+ restoreSessionId = null,
64
+ }) {
65
+ const pid = parsePid(leadPid) ?? 0;
66
+ const restoreId = /^[A-Za-z0-9_-]+$/.test(String(restoreSessionId || '')) ? String(restoreSessionId) : null;
67
+ const replacementToken = replaceToken ? String(replaceToken) : null;
68
+ const replayId = passive && registrationId ? replays.replayIdOf(registrationId) : null;
69
+ const replayed = replayedToken(replayId, { pid, cwd, replacementToken, restoreId });
70
+ if (replayed) return replayed;
71
+ const token = randomUUID();
72
+ const fresh = {
73
+ token,
74
+ leadPid: pid,
75
+ cwd: cwd || null,
76
+ sse: null,
77
+ pendingRemoteStateFrame: null,
78
+ lastSeen: Date.now(),
79
+ registeredAt: Date.now(),
80
+ restoreSessionId: restoreId,
81
+ };
82
+ clients.set(token, fresh);
83
+ if (state.remoteAcquired && restoreId && restoreId === state.pinnedSessionId) fresh.remoteSessionId = restoreId;
84
+ state.everHadClient = true;
85
+ grace.cancel();
86
+ grace.resetBackoff();
87
+ grace.startSweep();
88
+ log(`client registered token=${token} lead=${pid} cwd=${cwd || '-'}`);
89
+ // The unified daemon starts the channels runtime (automation, webhooks)
90
+ // only once a CHANNELS client is actually present — a
91
+ // session runtime-only daemon must not run tunnels nobody asked for.
92
+ if (typeof onClientRegistered === 'function') {
93
+ try {
94
+ onClientRegistered({ token, leadPid: pid, cwd: cwd || null });
95
+ } catch {
96
+ // The registered-client hook must not fail the registration itself.
97
+ }
98
+ }
99
+ // Token replacement never crosses leadPid boundaries. Registration is
100
+ // otherwise transport-only, including same-pid fresh attaches.
101
+ const replaced = passive && replacementToken ? clients.get(replacementToken) : null;
102
+ if (replaced && replaced.leadPid === pid) adoptReplacedClient(fresh, replaced);
103
+ if (replayId) {
104
+ replays.remember(replayId, {
105
+ token,
106
+ leadPid: pid,
107
+ cwd: cwd || null,
108
+ replaceToken: replacementToken,
109
+ restoreSessionId: restoreId,
110
+ responseFinished: false,
111
+ timer: null,
112
+ });
113
+ }
114
+ return token;
115
+ }
116
+
117
+ return { registerClient, cancelReplacementRegistration };
118
+ }
@@ -3,6 +3,8 @@
3
3
  * transport: the live client records, registration with reconnect
4
4
  * replacement, pid-liveness pruning, and the UI control pointer lookup. The
5
5
  * supporting state machines live under ./client-registry/:
6
+ * client-records — live records, removal, pruning, pointer lookup
7
+ * client-registration — registration, replay and reconnect replacement
6
8
  * registration-replays — response-loss replay records and their TTL
7
9
  * client-grace — self-shutdown grace timer + dead-pid sweep
8
10
  * sse-attach — binding a client's notify stream
@@ -11,14 +13,10 @@
11
13
  * pointerToken, everHadClient, closed, remoteAcquired, pinnedSessionId,
12
14
  * stickyRemoteFrame.
13
15
  */
14
- import { randomUUID } from 'node:crypto';
15
- import { isPidAlive, parsePid } from '../../runtime/shared/pid-liveness.mjs';
16
16
  import { createClientGrace } from './client-registry/client-grace.mjs';
17
- import {
18
- createRegistrationReplays,
19
- replayMatchesCancellation,
20
- replayMatchesRegistration,
21
- } from './client-registry/registration-replays.mjs';
17
+ import { createClientRecords } from './client-registry/client-records.mjs';
18
+ import { createClientRegistration } from './client-registry/client-registration.mjs';
19
+ import { createRegistrationReplays } from './client-registry/registration-replays.mjs';
22
20
  import { attachClientStream } from './client-registry/sse-attach.mjs';
23
21
 
24
22
  export function createClientRegistry({
@@ -37,162 +35,25 @@ export function createClientRegistry({
37
35
  ttlMs: registrationReplayTtlMs,
38
36
  onExpiredUnflushed: (token) => dropClient(token, 'unflushed registration replay expired'),
39
37
  });
40
- const grace = createClientGrace({ state, clients, log, clientGraceMs, sweepMs, onClientsEmpty, pruneDeadClients });
41
-
42
- // pid death is the authoritative signal for client liveness: a client whose
43
- // SSE stream closed may still reconnect, one whose lead pid is gone cannot.
44
- function pruneDeadClients() {
45
- for (const [token, c] of clients) {
46
- if (!isPidAlive(c.leadPid)) dropClient(token, 'pid dead');
47
- }
48
- }
49
-
50
- function liveClients() {
51
- const out = [];
52
- for (const [token, c] of clients) {
53
- if (isPidAlive(c.leadPid)) out.push([token, c]);
54
- }
55
- return out;
56
- }
57
-
58
- // Every removal path uses this primitive, including replacement's deliberate
59
- // no-failover retirement, so replay records/timers never target absent tokens.
60
- function removeClientRecord(token) {
61
- const c = clients.get(token);
62
- if (!c) return null;
63
- clients.delete(token);
64
- replays.forgetFor(token);
65
- try {
66
- c.sse?.end?.();
67
- } catch {}
68
- return c;
69
- }
70
-
71
- function dropClient(token, reason) {
72
- const c = removeClientRecord(token);
73
- if (!c) return;
74
- if (state.pointerToken === token) state.pointerToken = null;
75
- log(`client ${token} (lead=${c.leadPid}) removed: ${reason}`);
76
- // Client presence is not channel authority. The durable session pin stays
77
- // active until that same session explicitly turns Remote OFF.
78
- publishRemoteState();
79
- grace.maybeArm('client removed');
80
- }
81
-
82
- function cancelReplacementRegistration(request) {
83
- const replay = replays.lookup(request.registrationId);
84
- if (!replay) return 'missing';
85
- if (!replayMatchesCancellation(replay, request)) return 'forbidden';
86
- dropClient(replay.token, 'replacement deregister');
87
- return 'cancelled';
88
- }
89
-
90
- // Resolve the active UI pointer for state delivery only. Session routing
91
- // never depends on this client.
92
- function resolveTarget() {
93
- if (state.pointerToken) {
94
- const c = clients.get(state.pointerToken);
95
- if (c && isPidAlive(c.leadPid)) return c;
96
- if (c) dropClient(state.pointerToken, 'pid dead (notify-time)');
97
- else state.pointerToken = null;
98
- }
99
- return null;
100
- }
101
-
102
- /** A passive reconnect retrying under a known registration id gets the fresh
103
- * token it already created; a different identity under that id is a 409. */
104
- function replayedToken(replayId, identity) {
105
- const existing = replays.lookup(replayId);
106
- if (!existing) return null;
107
- if (replayMatchesRegistration(existing, identity) && clients.has(existing.token)) {
108
- replays.arm(replayId, existing);
109
- log(`client reconnect replay token=${existing.token} lead=${identity.pid}`);
110
- return existing.token;
111
- }
112
- if (clients.has(existing.token)) {
113
- const err = new Error('registration replay identity mismatch');
114
- err.statusCode = 409;
115
- throw err;
116
- }
117
- replays.remove(replayId, existing);
118
- return null;
119
- }
120
-
121
- /** A reconnect names the exact token it replaces. Token-scoped state belongs
122
- * to the logical client, so the fresh record inherits the buffered frame,
123
- * remote session and pointer; the old token is retired even when it is not
124
- * the pointer — retaining it would let it later call or accumulate a
125
- * buffered frame after the fresh client has gone away. */
126
- function adoptReplacedClient(fresh, replaced) {
127
- const replacedWasPointer = state.pointerToken === replaced.token;
128
- if (replaced.pendingRemoteStateFrame) {
129
- fresh.pendingRemoteStateFrame = replaced.pendingRemoteStateFrame;
130
- replaced.pendingRemoteStateFrame = null;
131
- }
132
- if (replaced.remoteSessionId) fresh.remoteSessionId = replaced.remoteSessionId;
133
- if (replaced.restoreSessionId) fresh.restoreSessionId = replaced.restoreSessionId;
134
- if (replacedWasPointer) state.pointerToken = fresh.token;
135
- removeClientRecord(replaced.token);
136
- log(`client reconnect replaced token=${replaced.token} -> ${fresh.token} lead=${fresh.leadPid}`);
137
- }
138
-
139
- function registerClient({
140
- leadPid,
141
- cwd,
142
- passive = false,
143
- replaceToken = null,
144
- registrationId = null,
145
- restoreSessionId = null,
146
- }) {
147
- const pid = parsePid(leadPid) ?? 0;
148
- const restoreId = /^[A-Za-z0-9_-]+$/.test(String(restoreSessionId || '')) ? String(restoreSessionId) : null;
149
- const replacementToken = replaceToken ? String(replaceToken) : null;
150
- const replayId = passive && registrationId ? replays.replayIdOf(registrationId) : null;
151
- const replayed = replayedToken(replayId, { pid, cwd, replacementToken, restoreId });
152
- if (replayed) return replayed;
153
- const token = randomUUID();
154
- const fresh = {
155
- token,
156
- leadPid: pid,
157
- cwd: cwd || null,
158
- sse: null,
159
- pendingRemoteStateFrame: null,
160
- lastSeen: Date.now(),
161
- registeredAt: Date.now(),
162
- restoreSessionId: restoreId,
163
- };
164
- clients.set(token, fresh);
165
- if (state.remoteAcquired && restoreId && restoreId === state.pinnedSessionId) fresh.remoteSessionId = restoreId;
166
- state.everHadClient = true;
167
- grace.cancel();
168
- grace.resetBackoff();
169
- grace.startSweep();
170
- log(`client registered token=${token} lead=${pid} cwd=${cwd || '-'}`);
171
- // The unified daemon starts the channels runtime (automation, webhooks,
172
- // messaging provider) only once a CHANNELS client is actually present — a
173
- // session runtime-only daemon must not run tunnels nobody asked for.
174
- if (typeof onClientRegistered === 'function') {
175
- try {
176
- onClientRegistered({ token, leadPid: pid, cwd: cwd || null });
177
- } catch {}
178
- }
179
- // Token replacement never crosses leadPid boundaries. Registration is
180
- // otherwise transport-only, including same-pid fresh attaches.
181
- const replaced = passive && replacementToken ? clients.get(replacementToken) : null;
182
- if (replaced && replaced.leadPid === pid) adoptReplacedClient(fresh, replaced);
183
- if (replayId) {
184
- replays.remember(replayId, {
185
- token,
186
- leadPid: pid,
187
- cwd: cwd || null,
188
- replaceToken: replacementToken,
189
- restoreSessionId: restoreId,
190
- responseFinished: false,
191
- timer: null,
192
- });
193
- }
194
- return token;
195
- }
38
+ const grace = createClientGrace({
39
+ state,
40
+ clients,
41
+ log,
42
+ clientGraceMs,
43
+ sweepMs,
44
+ onClientsEmpty,
45
+ pruneDeadClients: () => records.pruneDeadClients(),
46
+ });
47
+ const records = createClientRecords({ state, log, publishRemoteState, replays, grace });
48
+ const { liveClients, dropClient, resolveTarget } = records;
49
+ const { registerClient, cancelReplacementRegistration } = createClientRegistration({
50
+ state,
51
+ log,
52
+ onClientRegistered,
53
+ replays,
54
+ grace,
55
+ records,
56
+ });
196
57
 
197
58
  function attachSse(token, res) {
198
59
  const c = clients.get(token);
@@ -10,7 +10,7 @@
10
10
  // This module owns ONLY the transport (sockets, client registry, notify
11
11
  // routing and lifecycle). The channels runtime is injected via `handleCall`
12
12
  // so the same transport is exercised by the real daemon entry AND the smoke
13
- // harness (stub runtime, no Discord token).
13
+ // harness (stub runtime).
14
14
  //
15
15
  // Composition root: the shared transport state below is read and written by
16
16
  // the sub-services in channel-transport/ (remote binding, client registry,