mixdog 1.2.5 → 1.3.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 (493) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/gate-scheduler.mjs +2 -2
  7. package/scripts/lib/isolated-test-env.mjs +16 -0
  8. package/scripts/lib/run-node-tests.mjs +5 -14
  9. package/scripts/provider-pingpong-live.mjs +9 -9
  10. package/src/agents/advisor/AGENT.md +4 -4
  11. package/src/agents/advisor/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +5 -4
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/defaults/shipped-history.json +17 -0
  15. package/src/defaults/skills/browser-use/SKILL.md +7 -1
  16. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  17. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  18. package/src/defaults/skills/setup/references/actions.md +11 -4
  19. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  20. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  21. package/src/defaults/skills/visualize/SKILL.md +3 -2
  22. package/src/lib/rules-builder.cjs +56 -7
  23. package/src/output-styles/common.md +22 -6
  24. package/src/output-styles/detailed.md +7 -5
  25. package/src/output-styles/minimal.md +7 -3
  26. package/src/output-styles/simple.md +2 -3
  27. package/src/rules/agent/AGENT.md +7 -1
  28. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  29. package/src/rules/agent-mode/MIXY.md +21 -0
  30. package/src/rules/agent-mode/ROUTINE.md +6 -0
  31. package/src/rules/lead/LEAD.md +9 -4
  32. package/src/rules/shared/10-tool-workflow.md +2 -1
  33. package/src/rules/shared/60-verification.md +4 -5
  34. package/src/rules/shared/70-delivery.md +3 -0
  35. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  37. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  39. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  40. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
  41. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  42. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  43. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
  44. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  45. package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
  46. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  49. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  50. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  51. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  53. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  62. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  65. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  66. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  69. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  70. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  84. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  86. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  88. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  92. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  94. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  95. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  96. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  97. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  98. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  99. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  100. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
  101. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  103. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
  104. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  109. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  110. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  111. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  112. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  113. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  114. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  115. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  116. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  117. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  118. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  119. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  120. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  121. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  122. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  123. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  124. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  125. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  126. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  127. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  128. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  129. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  130. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  131. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  132. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  134. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  135. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  140. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  146. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  147. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  148. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  150. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  157. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  158. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  159. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  163. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  166. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  167. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  168. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  169. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  171. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  174. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  175. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  176. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
  177. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
  178. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  179. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  180. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  181. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  182. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  183. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  184. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  185. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  186. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  189. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  191. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  197. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  198. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  199. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  200. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  201. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  203. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  208. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  209. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  210. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  211. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  212. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  213. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  214. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  215. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  216. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  217. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  218. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  219. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  220. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  221. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
  222. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  224. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  225. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  226. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  227. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  228. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  229. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  230. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  231. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  232. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
  233. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
  234. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  235. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
  236. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
  237. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  238. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
  239. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  240. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  241. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  242. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  243. package/src/runtime/attachments/office-extract.mjs +11 -9
  244. package/src/runtime/browser-bridge/client.mjs +24 -0
  245. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  246. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  247. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  248. package/src/runtime/channels/lib/webhook.mjs +0 -1
  249. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  250. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  251. package/src/runtime/computer-bridge/client.mjs +28 -1
  252. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  253. package/src/runtime/connectors/google-mcp.mjs +446 -0
  254. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  255. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  256. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  257. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  258. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  259. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  260. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  261. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  262. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  263. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  264. package/src/runtime/memory/lib/memory.mjs +7 -1
  265. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  266. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  268. package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
  269. package/src/runtime/memory/tool-defs.mjs +5 -0
  270. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  271. package/src/runtime/office/core/tabular.mjs +1 -1
  272. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  273. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  274. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  275. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  276. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  277. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  278. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  279. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  280. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  281. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  282. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  283. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  284. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  285. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  286. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  287. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  288. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  289. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  290. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  291. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  292. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  293. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  294. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  295. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  296. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  297. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  298. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  299. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  300. package/src/runtime/shared/agent-avatar.mjs +320 -0
  301. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  302. package/src/runtime/shared/agent-route-config.mjs +16 -0
  303. package/src/runtime/shared/atomic-file.mjs +36 -14
  304. package/src/runtime/shared/background-tasks.mjs +60 -20
  305. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  306. package/src/runtime/shared/config.mjs +8 -11
  307. package/src/runtime/shared/connector-accounts.mjs +15 -0
  308. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  309. package/src/runtime/shared/connector-catalog.mjs +230 -0
  310. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  311. package/src/runtime/shared/connector-triggers.mjs +43 -0
  312. package/src/runtime/shared/file-lock.mjs +7 -1
  313. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  314. package/src/runtime/shared/file-permissions.d.mts +19 -4
  315. package/src/runtime/shared/file-permissions.mjs +310 -4
  316. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  317. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  318. package/src/runtime/shared/held-request-host.d.mts +12 -0
  319. package/src/runtime/shared/held-request-host.mjs +71 -0
  320. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  321. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  322. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  323. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  324. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  325. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  326. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  327. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  328. package/src/runtime/shared/notify-trace.mjs +8 -1
  329. package/src/runtime/shared/plain-preview.d.mts +1 -0
  330. package/src/runtime/shared/plain-preview.mjs +28 -0
  331. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  332. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  333. package/src/runtime/shared/process-runner.mjs +38 -0
  334. package/src/runtime/shared/provider-accounts.mjs +2 -1
  335. package/src/runtime/shared/provider-api-key.mjs +0 -1
  336. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  337. package/src/runtime/shared/resource-admission.mjs +60 -18
  338. package/src/runtime/shared/routine-limits.d.mts +11 -0
  339. package/src/runtime/shared/routine-limits.mjs +21 -0
  340. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  341. package/src/runtime/shared/routine-trigger.mjs +44 -0
  342. package/src/runtime/shared/run-child.mjs +84 -0
  343. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  344. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  345. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  346. package/src/runtime/shared/singleton-owner.mjs +126 -42
  347. package/src/runtime/shared/slow-span.mjs +4 -12
  348. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  349. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  350. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  351. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  352. package/src/runtime/shared/turn-changes.mjs +0 -8
  353. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  354. package/src/runtime/shared/user-data-guard.mjs +131 -34
  355. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  356. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  357. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  358. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  359. package/src/runtime/tidy/runners/biome.mjs +3 -1
  360. package/src/runtime/tidy/shadow.mjs +2 -1
  361. package/src/runtime/web-search/index.mjs +5 -7
  362. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  363. package/src/runtime/web-search/tool-defs.mjs +6 -1
  364. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  365. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  366. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  367. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  368. package/src/session-runtime/boot/apis.mjs +50 -18
  369. package/src/session-runtime/boot/facade.mjs +5 -8
  370. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  371. package/src/session-runtime/boot/tools.mjs +3 -0
  372. package/src/session-runtime/channel-config-api.mjs +53 -5
  373. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  374. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  375. package/src/session-runtime/held-requests.mjs +228 -0
  376. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  377. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  378. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  379. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  380. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  381. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  382. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  383. package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
  384. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  385. package/src/session-runtime/lifecycle/shared.mjs +25 -2
  386. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  387. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  388. package/src/session-runtime/lifecycle-api.mjs +3 -1
  389. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  390. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  391. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  392. package/src/session-runtime/model-route/set-route.mjs +14 -3
  393. package/src/session-runtime/owned-automation-run.mjs +229 -0
  394. package/src/session-runtime/provider-auth/login.mjs +10 -6
  395. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  396. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  397. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  398. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  399. package/src/session-runtime/provider-models.mjs +76 -10
  400. package/src/session-runtime/provider-readiness.mjs +4 -0
  401. package/src/session-runtime/resource-api.mjs +5 -0
  402. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  403. package/src/session-runtime/resource-skills-api.mjs +12 -0
  404. package/src/session-runtime/room-member-gate.mjs +48 -0
  405. package/src/session-runtime/routine-home-turn.mjs +156 -0
  406. package/src/session-runtime/routine-owner.mjs +48 -0
  407. package/src/session-runtime/schedule-session-run.mjs +30 -64
  408. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  409. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  410. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  411. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  412. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  413. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  414. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  415. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  416. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  417. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  418. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  419. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  420. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  422. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  423. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  424. package/src/session-runtime/services/agent-tool.mjs +20 -0
  425. package/src/session-runtime/services/channel-admin.mjs +93 -24
  426. package/src/session-runtime/services/memory-access.mjs +37 -0
  427. package/src/session-runtime/services/provider-admin.mjs +166 -59
  428. package/src/session-runtime/services/schedule-access.mjs +91 -0
  429. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  430. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  431. package/src/session-runtime/session-create/session-options.mjs +34 -7
  432. package/src/session-runtime/session-hooks.mjs +16 -8
  433. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  434. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  435. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  436. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  437. package/src/session-runtime/skills-api.mjs +16 -1
  438. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  439. package/src/session-runtime/turn/session-ops.mjs +10 -3
  440. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
  441. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  442. package/src/session-runtime/usage-stats-api.mjs +6 -1
  443. package/src/session-runtime/webhook-session-run.mjs +49 -67
  444. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  445. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  446. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  447. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  448. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  449. package/src/standalone/daemon-agent-control.mjs +1 -8
  450. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  451. package/src/standalone/daemon-owner.mjs +117 -25
  452. package/src/standalone/daemon-session-control.mjs +11 -2
  453. package/src/standalone/daemon.mjs +61 -17
  454. package/src/standalone/session-call-cache.mjs +6 -0
  455. package/src/standalone/session-protocol.mjs +28 -1
  456. package/src/standalone/session-service/retention/busy.mjs +7 -2
  457. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  458. package/src/standalone/session-service.mjs +19 -0
  459. package/src/standalone/session-transport/routes.mjs +7 -6
  460. package/src/tui/app/model-options.mjs +39 -2
  461. package/src/tui/app/model-picker.mjs +16 -2
  462. package/src/tui/app/provider-setup-picker.mjs +1 -2
  463. package/src/tui/app/slash-commands.mjs +20 -0
  464. package/src/tui/dist/index.mjs +487 -128
  465. package/src/tui/session/context-state.mjs +6 -1
  466. package/src/tui/session/notice-surface.mjs +35 -4
  467. package/src/tui/session/notice-text.mjs +0 -1
  468. package/src/tui/session/queue-helpers.mjs +2 -1
  469. package/src/tui/session/session-api/extensions.mjs +4 -0
  470. package/src/tui/session/session-api/intake/submission.mjs +12 -13
  471. package/src/tui/session/session-api/integrations.mjs +9 -5
  472. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  473. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  474. package/src/tui/session/session-api/routes.mjs +48 -3
  475. package/src/tui/session/session-flow/drain.mjs +20 -3
  476. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  477. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  478. package/src/tui/session/session-flow/steering.mjs +104 -24
  479. package/src/tui/session/session-flow.mjs +2 -0
  480. package/src/tui/session/tool-approval.mjs +26 -5
  481. package/src/tui/session/transcript-intake.mjs +6 -2
  482. package/src/tui/session/transcript-spill.mjs +5 -1
  483. package/src/tui/session/tui-steering-persist.mjs +65 -42
  484. package/src/tui/session/turn-live-call.mjs +56 -0
  485. package/src/tui/session/turn-stream.mjs +33 -0
  486. package/src/tui/session/turn.mjs +50 -7
  487. package/src/ui/statusline.mjs +1 -6
  488. package/src/ui/streaming-markdown-heal.mjs +24 -7
  489. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  490. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  491. package/src/workflows/default/WORKFLOW.md +9 -13
  492. package/src/session-runtime/automation-agents.mjs +0 -94
  493. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -29,6 +29,12 @@ import {
29
29
  pruneEmptyPendingIdSet,
30
30
  } from './pending-claim-ledger.mjs';
31
31
  import { legacyPendingMessagesPath } from './pending-spool-path.mjs';
32
+ import {
33
+ TASK_WAIT_WAKE_NOTIFICATION,
34
+ TASK_WAIT_WAKE_USER,
35
+ interruptTaskWaitForSession,
36
+ registerTaskWaitSteeringCheck,
37
+ } from '../task-wait-control.mjs';
32
38
  import { pruneCleanupConfirmedLedger } from './pending-delivered-ledger.mjs';
33
39
  // The durable delivered-id ledger on the session record lives in
34
40
  // pending-delivered-ledger.mjs; re-exported so prior importers stay unchanged.
@@ -51,6 +57,8 @@ import {
51
57
  PENDING_MODE_TASK_NOTIFICATION,
52
58
  _groupPendingMessageEntries,
53
59
  _mergePendingMessageEntries,
60
+ completionExecutionId,
61
+ completionIsDelivered,
54
62
  completionWasDelivered,
55
63
  entryLifecycleToken,
56
64
  isCompletionNotificationEntry,
@@ -447,9 +455,52 @@ export function enqueuePendingMessage(sessionId, message) {
447
455
  }
448
456
  q.push(entry);
449
457
  const bufferedDepth = schedulePendingMessagePersist(sessionId, entry);
458
+ const wake = pendingEntryWake(entry);
459
+ if (wake) {
460
+ interruptTaskWaitForSession(
461
+ sessionId,
462
+ wake,
463
+ wake === TASK_WAIT_WAKE_NOTIFICATION ? completionExecutionId(entry) : null
464
+ );
465
+ }
450
466
  return Math.max(q.length, bufferedDepth || 0);
451
467
  }
452
468
 
469
+ // What one queued entry wakes a passive task wait or shell hold for, so it
470
+ // reaches the loop now: steering (a Lead message to its agent) as user input,
471
+ // and an undelivered task completion (agent/shell, any status). A plain
472
+ // channel notice (scheduled or event-rule inject) waits for the turn, as it
473
+ // does in the Lead's queue.
474
+ function pendingEntryWake(entry) {
475
+ if (pendingEntryMode(entry) === PENDING_MODE_PROMPT) return TASK_WAIT_WAKE_USER;
476
+ if (isCompletionNotificationEntry(entry) && !completionIsDelivered(entry)) return TASK_WAIT_WAKE_NOTIFICATION;
477
+ return false;
478
+ }
479
+
480
+ // Anything already queued when a wait or hold begins ends it at once, exactly
481
+ // like what arrives during it. A completion the agent already received is no
482
+ // longer deliverable and never wakes.
483
+ registerTaskWaitSteeringCheck((sessionId) => {
484
+ let wake = false;
485
+ for (const entry of _sessionPendingMessages.get(sessionId) || []) {
486
+ const found = pendingEntryWake(entry);
487
+ if (found === TASK_WAIT_WAKE_USER) return found;
488
+ wake = wake || found;
489
+ }
490
+ return wake;
491
+ });
492
+
493
+ // True when this session's queue holds task notifications and nothing else:
494
+ // the mid-chain boundary may then take the whole queue without bypassing the
495
+ // prompt filters that keep queued prompts for the terminal boundary.
496
+ export function pendingQueueHoldsOnlyNotifications(sessionId) {
497
+ const queued = [
498
+ ...(_sessionPendingMessages.get(sessionId) || []),
499
+ ...(_hydratedPendingMessages.get(sessionId) || []),
500
+ ];
501
+ return queued.length > 0 && queued.every((entry) => pendingEntryMode(entry) === PENDING_MODE_TASK_NOTIFICATION);
502
+ }
503
+
453
504
  // Drain order: enqueue time, then source (hydrated < buffered < memory), then
454
505
  // position within the source.
455
506
  function byEnqueueOrder(a, b) {
@@ -468,19 +519,13 @@ export function drainPendingMessages(sessionId) {
468
519
  // This path is deliberately memory-only: no file lock, stat, parse, or
469
520
  // atomic rename can run in the agent-completion tick.
470
521
  const buffered = takeBufferedPendingMessages(sessionId);
471
- // Deferred completion/task notifications are dropped ONLY from the persisted
472
- // (disk/buffer) path. Those are the entries a later session resume/restart
473
- // would replay out-of-order into a future turn, once the in-memory queue is
474
- // gone — discarding them there is the deliberate owner decision.
475
- // Live in-memory completions (fallback/headless enqueues delivered within
476
- // THIS process, e.g. the idle-resume kick that surfaces the model-visible
477
- // body) are the intended payload and MUST survive the drain — filtering
478
- // them here dropped the notification entirely. On a genuine resume the
479
- // in-memory queue is empty, so keeping it only ever delivers live entries.
480
- // Genuine user/steering messages carry no marker and are kept in order in
481
- // both paths.
482
- // Drain-time belt: drop MARKED in-memory completion entries whose text hash
483
- // was already delivered+ACKed (TUI execution-ui) this process — those would
522
+ // A completion that reached the spool and was never acknowledged was never
523
+ // delivered to the model (delivery acknowledges and removes it), so it
524
+ // survives a restart: the hydrated disk entries drain like live in-memory
525
+ // ones. Dropping them on resume lost a finished (or failed) task's only
526
+ // notification.
527
+ // Drain-time belt: drop MARKED completion entries whose text hash was
528
+ // already delivered+ACKed (TUI execution-ui) this process — those would
484
529
  // double-inject next turn. Only marked completion entries are eligible;
485
530
  // genuine user/steering entries carry no marker and are always kept.
486
531
  const deliveredIds = new Set();
@@ -494,10 +539,11 @@ export function drainPendingMessages(sessionId) {
494
539
  deliveredEntries.push(entry);
495
540
  return false;
496
541
  };
542
+ const hydratedKept = hydrated.filter(keepUndelivered);
497
543
  const memoryKept = memory.filter(keepUndelivered);
498
544
  const bufferedKept = buffered.filter(keepUndelivered);
499
545
  const tagged = [
500
- ...hydrated.map((entry, index) => ({ entry, source: 0, index })),
546
+ ...hydratedKept.map((entry, index) => ({ entry, source: 0, index })),
501
547
  ...bufferedKept.map((entry, index) => ({ entry, source: 1, index })),
502
548
  ...memoryKept.map((entry, index) => ({ entry, source: 2, index })),
503
549
  ];
@@ -512,14 +558,8 @@ export function drainPendingMessages(sessionId) {
512
558
  if (!prior || item.source > prior.source) byId.set(normalized.id, { ...item, entry: normalized });
513
559
  }
514
560
  const ordered = [...byId.values()].sort(byEnqueueOrder);
515
- const dropped = ordered
516
- .filter(({ entry, source }) => source === 0 && isCompletionNotificationEntry(entry))
517
- .map(({ entry }) => entry);
518
- const acknowledged = [...deliveredEntries, ...dropped];
519
- if (acknowledged.length > 0) acknowledgePendingMessages(sessionId, acknowledged);
520
- const visible = modelVisiblePendingMessages(
521
- ordered.filter(({ entry }) => !dropped.includes(entry)).map(({ entry }) => entry)
522
- );
561
+ if (deliveredEntries.length > 0) acknowledgePendingMessages(sessionId, deliveredEntries);
562
+ const visible = modelVisiblePendingMessages(ordered.map(({ entry }) => entry));
523
563
  // Epoch gate. Normalization above rebuilds every entry, but each copy
524
564
  // inherits the IMMUTABLE token the entry was accepted under. An entry from
525
565
  // an older epoch (a cross-process close/detach moved the generation while
@@ -207,29 +207,46 @@ function bp3SliceAt(text, heading) {
207
207
 
208
208
  /**
209
209
  * Replace the workspace-scoped tail of a BP3 core (composeSystemPrompt order:
210
- * …, `# Core Memory`, `# Instruction Files`). The instruction-files slice is
211
- * always last and may itself contain separators, so it is cut by position;
212
- * the core-memory slice is one separator-free part. `undefined` keeps a slice,
213
- * an empty string drops it.
210
+ * …, `# Instruction Files`, `# Core Memory`). Core memory is the last,
211
+ * separator-free part; the instruction-files slice before it may itself
212
+ * contain separators, so both are cut by position. A core from before that
213
+ * order (memory, then instruction files) is read too and written in the
214
+ * current order. `undefined` keeps a slice, an empty string drops it.
214
215
  * @param {string} core
215
216
  * @param {{ coreMemoryContext?: string, instructionFilesContext?: string }} slices
216
217
  */
217
218
  export function replaceWorkspaceCoreSlices(core, { coreMemoryContext, instructionFilesContext } = {}) {
219
+ let { head, instructionFiles, memory } = splitWorkspaceCoreSlices(core);
220
+ if (coreMemoryContext !== undefined) {
221
+ const value = trimmedSlice(coreMemoryContext);
222
+ memory = value ? `${CORE_MEMORY_HEADING}${value}` : '';
223
+ }
224
+ if (instructionFilesContext !== undefined) instructionFiles = trimmedSlice(instructionFilesContext);
225
+ return [head, instructionFiles, memory].filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
226
+ }
227
+
228
+ /** A BP3 core's `{ head, instructionFiles, memory }` (each '' when absent). */
229
+ function splitWorkspaceCoreSlices(core) {
218
230
  let head = String(core || '');
231
+ let memory = '';
232
+ const memoryAt = bp3SliceAt(head, CORE_MEMORY_HEADING);
233
+ if (memoryAt) {
234
+ memory = head.slice(memoryAt.start);
235
+ head = head.slice(0, memoryAt.cut);
236
+ const legacy = memory.indexOf(`${BP3_PART_SEPARATOR}${INSTRUCTION_FILES_HEADING}`);
237
+ if (legacy >= 0) {
238
+ const instructions = memory.slice(legacy + BP3_PART_SEPARATOR.length);
239
+ memory = memory.slice(0, legacy);
240
+ head = head ? `${head}${BP3_PART_SEPARATOR}${instructions}` : instructions;
241
+ }
242
+ }
219
243
  let instructionFiles = '';
220
244
  const instructionAt = bp3SliceAt(head, INSTRUCTION_FILES_HEADING);
221
245
  if (instructionAt) {
222
246
  instructionFiles = head.slice(instructionAt.start);
223
247
  head = head.slice(0, instructionAt.cut);
224
248
  }
225
- let parts = head ? head.split(BP3_PART_SEPARATOR) : [];
226
- if (coreMemoryContext !== undefined) {
227
- parts = parts.filter((part) => !part.startsWith(CORE_MEMORY_HEADING));
228
- const memory = trimmedSlice(coreMemoryContext);
229
- if (memory) parts.push(`${CORE_MEMORY_HEADING}${memory}`);
230
- }
231
- if (instructionFilesContext !== undefined) instructionFiles = trimmedSlice(instructionFilesContext);
232
- return [...parts, instructionFiles].filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
249
+ return { head, instructionFiles, memory };
233
250
  }
234
251
 
235
252
  /**
@@ -286,9 +303,7 @@ function textOf(message) {
286
303
  */
287
304
  function presentedWorkspace(session) {
288
305
  const core = session.bp3EnvSplit === true ? textOf(bp3SystemMessage(session)) : String(session.bp3CoreContext || '');
289
- const instructionAt = bp3SliceAt(core, INSTRUCTION_FILES_HEADING);
290
- const head = instructionAt ? core.slice(0, instructionAt.cut) : core;
291
- const memory = (head ? head.split(BP3_PART_SEPARATOR) : []).find((part) => part.startsWith(CORE_MEMORY_HEADING));
306
+ const { instructionFiles, memory } = splitWorkspaceCoreSlices(core);
292
307
  const tail = typeof session.bp3EnvironmentContext === 'string' ? session.bp3EnvironmentContext : '';
293
308
  const facts = tail.split(BP3_PART_SEPARATOR)[0] || '';
294
309
  const promptText = [textOf(bpEnvSystemMessage(session)), textOf(bp3SystemMessage(session))].join('\n');
@@ -297,10 +312,23 @@ function presentedWorkspace(session) {
297
312
  cwd: cwd ? cwd.trim() : null,
298
313
  environmentContext: facts.startsWith('- ') ? facts.trim() : '',
299
314
  coreMemoryContext: memory ? memory.slice(CORE_MEMORY_HEADING.length).trim() : '',
300
- instructionFilesContext: instructionAt ? core.slice(instructionAt.start).trim() : '',
315
+ instructionFilesContext: instructionFiles.trim(),
301
316
  };
302
317
  }
303
318
 
319
+ /**
320
+ * A rebuilt BP3 core head (composed without workspace slices) followed by the
321
+ * instruction-files and core-memory slices the session's prompt presents now,
322
+ * so a rebuild of the rest of the core (a mid-conversation workflow switch)
323
+ * leaves what the model was told about its workspace — and any staged
324
+ * workspaceReroot — as it was.
325
+ */
326
+ export function withPresentedWorkspaceSlices(session, nextHead) {
327
+ const core = session.bp3EnvSplit === true ? textOf(bp3SystemMessage(session)) : String(session.bp3CoreContext || '');
328
+ const { instructionFiles, memory } = splitWorkspaceCoreSlices(core);
329
+ return [nextHead, instructionFiles, memory].filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
330
+ }
331
+
304
332
  const WORKSPACE_SLICES = ['environmentContext', 'coreMemoryContext', 'instructionFilesContext'];
305
333
 
306
334
  /**
@@ -112,20 +112,36 @@ export function _buildAgentRules(profile = 'full') {
112
112
  );
113
113
  }
114
114
 
115
- export function _buildLeadRules({ includeLeadBrief = true } = {}) {
115
+ // One `part` of LEAD.md (rules-builder buildLeadRoleContent): 'common' (the
116
+ // lines every Lead shares), 'coding' / 'agent' (the mode-variant passages),
117
+ // 'identity' (the coding identity sentence).
118
+ export function _buildLeadRules({ includeLeadBrief = true, part = 'common' } = {}) {
116
119
  const PLUGIN_ROOT = mixdogRoot();
117
120
  const DATA_DIR = resolvePluginData();
118
121
  const RULES_DIR = join(PLUGIN_ROOT, 'rules');
122
+ const variant = includeLeadBrief ? 'delegating' : 'delegation-free';
119
123
  return buildCachedRules(
120
124
  'buildLeadRoleContent',
121
125
  'lead role rules',
122
126
  [join(RULES_DIR, 'lead'), join(DATA_DIR, 'mixdog-config.json')],
123
- { PLUGIN_ROOT, DATA_DIR, includeLeadBrief },
124
- includeLeadBrief ? 'delegating' : 'delegation-free'
127
+ { PLUGIN_ROOT, DATA_DIR, includeLeadBrief, part },
128
+ `${variant}\n${part}`
125
129
  );
126
130
  }
127
131
 
128
- export function _buildLeadMetaContext() {
132
+ // One agent-mode rule file (rules/agent-mode): 'conduct', 'mixy' or 'routine'.
133
+ export function _buildAgentModeRules({ part = 'conduct' } = {}) {
134
+ const PLUGIN_ROOT = mixdogRoot();
135
+ return buildCachedRules(
136
+ 'buildAgentModeContent',
137
+ 'agent mode rules',
138
+ [join(PLUGIN_ROOT, 'rules', 'agent-mode')],
139
+ { PLUGIN_ROOT, part },
140
+ part
141
+ );
142
+ }
143
+
144
+ export function _buildLeadMetaContext({ includeOutputStyle = true } = {}) {
129
145
  const PLUGIN_ROOT = mixdogRoot();
130
146
  const DATA_DIR = resolvePluginData();
131
147
  const RULES_DIR = join(PLUGIN_ROOT, 'rules');
@@ -138,7 +154,8 @@ export function _buildLeadMetaContext() {
138
154
  join(PLUGIN_ROOT, 'output-styles'),
139
155
  join(DATA_DIR, 'output-styles'),
140
156
  ],
141
- { PLUGIN_ROOT, DATA_DIR }
157
+ { PLUGIN_ROOT, DATA_DIR, includeOutputStyle },
158
+ includeOutputStyle ? 'style' : 'no-style'
142
159
  );
143
160
  }
144
161
 
@@ -23,6 +23,7 @@
23
23
  // }
24
24
  import { createAbortController } from '../../../../shared/abort-controller.mjs';
25
25
  import { emitSessionApprovalWaitChange } from '../../../../shared/session-approval-wait.mjs';
26
+ import { emitSessionActivityChange } from '../../../../shared/session-activity-signal.mjs';
26
27
  import { DEFAULT_ACTIVITY_HEARTBEAT_MS } from '../../stall-policy.mjs';
27
28
  import { MODEL_THINKING_STAGE, awaitingModelResponse, lastProgressAt } from '../../../../shared/quiet-progress.mjs';
28
29
  import { finishBrowserTurn } from '../../../../browser-bridge/client.mjs';
@@ -177,12 +178,14 @@ export function markSessionAskStart(id) {
177
178
  entry.modelRequestStartedAt = null;
178
179
  entry.lastProgressAt = now;
179
180
  entry.updatedAt = now;
181
+ entry.taskWaits = 0;
182
+ emitSessionActivityChange(id);
180
183
  }
181
184
  /** The provider put the request on the wire; its response headers are
182
185
  * pending. The turn stays `requesting` (every stage set and the watchdog
183
186
  * clock are unchanged); the send time lets surfaces show "model thinking"
184
187
  * until transport arrives. Not progress: lastProgressAt is untouched. */
185
- export function markSessionRequestSent(id) {
188
+ function markSessionRequestSent(id) {
186
189
  if (!id) return;
187
190
  const entry = _runtimeState.get(id);
188
191
  if (!entry || entry.closed || entry.controller?.signal?.aborted || entry.stage !== 'requesting') return;
@@ -321,6 +324,23 @@ export function beginSessionApprovalWait(id) {
321
324
  emitSessionApprovalWaitChange(id, null, meta);
322
325
  };
323
326
  }
327
+ // A turn parked on a background task (`task wait`): published as
328
+ // `awaitingTask` so status surfaces show the session waiting, not working.
329
+ // Counted like approval waits; never creates an entry. Returns the idempotent
330
+ // release.
331
+ export function beginSessionTaskWait(id) {
332
+ const entry = id ? _runtimeState.get(id) : null;
333
+ if (!entry) return () => {};
334
+ entry.taskWaits = (entry.taskWaits || 0) + 1;
335
+ if (entry.taskWaits === 1) emitSessionActivityChange(id);
336
+ let released = false;
337
+ return () => {
338
+ if (released) return;
339
+ released = true;
340
+ entry.taskWaits = Math.max(0, (entry.taskWaits || 0) - 1);
341
+ if (entry.taskWaits === 0) emitSessionActivityChange(id);
342
+ };
343
+ }
324
344
  // Parent AbortSignal listeners are dropped on askSession unwind (finally /
325
345
  // terminal return) and on error/cancel/close — not in markSessionDone, which
326
346
  // also runs between queued follow-up turns within one ask.
@@ -335,6 +355,8 @@ function _settleTerminalEntry(entry, stage, lastError) {
335
355
  entry.doneAt = settledAt;
336
356
  entry.lastProgressAt = settledAt;
337
357
  entry.updatedAt = settledAt;
358
+ entry.taskWaits = 0;
359
+ emitSessionActivityChange(entry.session?.id || '');
338
360
  }
339
361
  export function markSessionDone(id, { empty = false } = {}) {
340
362
  if (!id) return;
@@ -3,7 +3,16 @@
3
3
  // when tombstone=false), aborts the in-flight controller, tears down
4
4
  // runtime/bash/dedup/offload/pending state, and defers the runtime-map clear.
5
5
  // abortSessionTurn cancels the current turn without tombstoning.
6
- import { markSessionClosed, bumpSessionGeneration, getSessionLifecycleCommitError } from '../store.mjs';
6
+ import {
7
+ markSessionClosed,
8
+ markSessionClosedAsync,
9
+ markSessionClosedAttempt,
10
+ bumpSessionGeneration,
11
+ bumpSessionGenerationAttempt,
12
+ isLifecycleRetry,
13
+ getSessionLifecycleCommitError,
14
+ } from '../store.mjs';
15
+ import { sleep } from '../../../../shared/sleep.mjs';
7
16
  import { clearReadDedupSession } from '../read-dedup.mjs';
8
17
  import { SessionClosedError } from './session-errors.mjs';
9
18
  import { _dropPendingMessageState } from './pending-messages.mjs';
@@ -36,44 +45,79 @@ import { forgetSideQuestion } from '../side-question-store.mjs';
36
45
  * TOMBSTONE_MAX_AGE_MS (1h — vastly longer than the microsecond in-flight race).
37
46
  */
38
47
  export function closeSession(id, reason = 'manual', opts = {}) {
39
- // tombstone=false: detach runtime resources (heartbeat, bash shells,
40
- // controller abort, runtime-map clear) WITHOUT planting the disk
41
- // tombstone. Used for non-empty sessions on /resume-away, /new, and
42
- // TUI exit: a tombstone would drop the session from the Resume list
43
- // immediately and let sweepTombstones() hard-delete it even though it
44
- // has real conversation content worth resuming. Only truly-empty
45
- // scratch sessions should still tombstone.
46
- const tombstone = opts.tombstone !== false;
47
48
  if (!id) return false;
49
+ const tombstone = opts.tombstone !== false;
48
50
  const entry = _getRuntimeEntry(id);
49
- // askSession owns the interruption tracker that can merge committed
50
- // iterations, the current partial response, and observed tool results into
51
- // one canonical transcript. Stage that in-memory snapshot synchronously
52
- // BEFORE the lifecycle generation changes; once generation is bumped, the
53
- // normal cancellation cleanup save is intentionally rejected as stale. It
54
- // is only staged: a refused barrier rolls it back so the turn keeps running
55
- // and checkpointing as if no close had been attempted.
56
- let closeSnapshot = null;
57
- try {
58
- closeSnapshot = entry?.prepareCloseSnapshot?.tentative?.(reason) ?? null;
59
- } catch {
60
- /* best-effort */
61
- }
62
- // 1. Tombstone first — this wins the race against saveSession().
63
- // Skipped when tombstone=false: no closed:true marker is planted, so
64
- // the session file stays intact and resumeSession() will accept it.
65
- // We still bump the on-disk generation via bumpSessionGeneration() —
66
- // that alone is what protects the session from a late save race: any
67
- // saveSession() still in flight from this detached turn (e.g. the
68
- // cancel-cleanup save below) carries the OLD generation as its
69
- // expectedGeneration, so _shouldDrop()'s ownership-counter rule drops
70
- // it once disk generation moves past that. Without this bump the late
71
- // write could silently overwrite the session after the user resumes
72
- // it back (BL: burned-session late-save clobber).
51
+ const closeSnapshot = stageCloseSnapshot(entry, reason);
73
52
  const outcome = { absent: false };
74
53
  const newGen = tombstone
75
54
  ? markSessionClosed(id, reason, { outcome })
76
55
  : bumpSessionGeneration(id, reason, { outcome });
56
+ return finishClose({ id, reason, tombstone, entry, closeSnapshot, newGen, outcome });
57
+ }
58
+
59
+ /**
60
+ * closeSession that never blocks the event loop. Each barrier attempt runs
61
+ * synchronously exactly as closeSession's barrier does (the first one before
62
+ * this function first yields), but a rename the OS refuses with retries left
63
+ * is waited out with a timer instead of sleepSync. Between attempts the staged
64
+ * close snapshot is rolled back, so the turn keeps running untouched, and it
65
+ * is re-staged from the then-current turn right before the next attempt: the
66
+ * barrier that lands always carries the latest transcript. The session is
67
+ * torn down — and reported closed — only after its barrier is on disk.
68
+ */
69
+ export async function closeSessionAsync(id, reason = 'manual', opts = {}) {
70
+ if (!id) return false;
71
+ const tombstone = opts.tombstone !== false;
72
+ const entry = _getRuntimeEntry(id);
73
+ for (let attempt = 0; ; attempt++) {
74
+ const closeSnapshot = stageCloseSnapshot(entry, reason);
75
+ const outcome = { absent: false };
76
+ const newGen = tombstone
77
+ ? markSessionClosedAttempt(id, reason, { outcome }, attempt)
78
+ : bumpSessionGenerationAttempt(id, reason, { outcome }, attempt);
79
+ if (!isLifecycleRetry(newGen)) {
80
+ return finishClose({ id, reason, tombstone, entry, closeSnapshot, newGen, outcome });
81
+ }
82
+ closeSnapshot?.rollback();
83
+ await sleep(newGen.retryInMs);
84
+ }
85
+ }
86
+
87
+ // askSession owns the interruption tracker that can merge committed
88
+ // iterations, the current partial response, and observed tool results into
89
+ // one canonical transcript. Stage that in-memory snapshot synchronously
90
+ // BEFORE the lifecycle generation changes; once generation is bumped, the
91
+ // normal cancellation cleanup save is intentionally rejected as stale. It
92
+ // is only staged: a refused barrier rolls it back so the turn keeps running
93
+ // and checkpointing as if no close had been attempted.
94
+ function stageCloseSnapshot(entry, reason) {
95
+ try {
96
+ return entry?.prepareCloseSnapshot?.tentative?.(reason) ?? null;
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+
102
+ // tombstone=false: detach runtime resources (heartbeat, bash shells,
103
+ // controller abort, runtime-map clear) WITHOUT planting the disk
104
+ // tombstone. Used for non-empty sessions on /resume-away, /new, and
105
+ // TUI exit: a tombstone would drop the session from the Resume list
106
+ // immediately and let sweepTombstones() hard-delete it even though it
107
+ // has real conversation content worth resuming. Only truly-empty
108
+ // scratch sessions should still tombstone.
109
+ //
110
+ // The barrier (`newGen`) came first — it wins the race against saveSession().
111
+ // tombstone=false plants no closed:true marker, so the session file stays
112
+ // intact and resumeSession() will accept it, but the on-disk generation is
113
+ // still bumped (bumpSessionGeneration): that alone protects the session from
114
+ // a late save race. Any saveSession() still in flight from this detached turn
115
+ // (e.g. the cancel-cleanup save below) carries the OLD generation as its
116
+ // expectedGeneration, so _shouldDrop()'s ownership-counter rule drops it once
117
+ // disk generation moves past that. Without this bump the late write could
118
+ // silently overwrite the session after the user resumes it back (BL:
119
+ // burned-session late-save clobber).
120
+ function finishClose({ id, reason, tombstone, entry, closeSnapshot, newGen, outcome }) {
77
121
  // Durable lifecycle barrier check. ANY nonnumeric result means the barrier
78
122
  // is not on disk — a write failure, a liveness/ownership veto, a contended
79
123
  // commit lock, or a missing record. Only a failed write publishes a
@@ -154,7 +198,10 @@ function reportCloseBarrierFailure({ id, reason, tombstone, entry }) {
154
198
  */
155
199
  function cancelOwnedShellProcesses({ id, reason, tombstone }) {
156
200
  const record = (result) => {
157
- if (tombstone && result?.confirmed === false) markSessionClosed(id, reason, { cancelStatus: 'cancel-unconfirmed' });
201
+ // Off the event loop's critical path: a refused rename retries on timers.
202
+ if (tombstone && result?.confirmed === false) {
203
+ markSessionClosedAsync(id, reason, { cancelStatus: 'cancel-unconfirmed' }).catch(() => {});
204
+ }
158
205
  };
159
206
  // A cancel that threw proved nothing either.
160
207
  const unconfirmed = () => record({ confirmed: false });
@@ -28,6 +28,7 @@ import { mintSessionId } from './session-id.mjs';
28
28
  import { hideCompactionArchive } from '../compaction-archive-capture.mjs';
29
29
  import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
30
30
  import { forgetSideQuestion } from '../side-question-store.mjs';
31
+ import { isAgentHomeSessionId } from '../agent-home.mjs';
31
32
 
32
33
  /** Force-flush session metrics to disk. */
33
34
  export async function flushSessionMetrics(sessionId) {
@@ -207,9 +208,12 @@ function clearTokenAccounting(session, messages, keep, beforeMessageTokens) {
207
208
  // Fork the outgoing transcript to a separate resumable session BEFORE the wipe,
208
209
  // so the conversation about to be discarded stays reachable via /resume under a
209
210
  // fresh id. Best-effort: any failure here must never block the clear itself.
210
- function forkOutgoingTranscript(session, messages, now) {
211
+ // `archiveAs` names the fork (an agent home's "대화 새로 시작": the host
212
+ // archives that id before the clear) and makes it durable first: the returned
213
+ // promise settles once the copy is on disk, and the wipe waits on it.
214
+ function forkOutgoingTranscript(session, messages, now, archiveAs = '') {
211
215
  try {
212
- const forkId = mintSessionId();
216
+ const forkId = archiveAs || mintSessionId();
213
217
  const fork = {
214
218
  ...session,
215
219
  id: forkId,
@@ -234,6 +238,15 @@ function forkOutgoingTranscript(session, messages, now) {
234
238
  delete fork.compaction.archive;
235
239
  }
236
240
  setLiveSession(fork);
241
+ if (archiveAs) {
242
+ return saveSessionAsync(fork).then(
243
+ () => evictLiveSession(forkId),
244
+ (err) => {
245
+ evictLiveSession(forkId);
246
+ throw err;
247
+ }
248
+ );
249
+ }
237
250
  void saveSessionAsync(fork)
238
251
  .then(() => {
239
252
  // The fork is a cold snapshot kept for /resume. Once durable on
@@ -247,17 +260,33 @@ function forkOutgoingTranscript(session, messages, now) {
247
260
  writeStderrBestEffort(`[session] clear-fork save failed (sess=${forkId}): ${err?.message || err}\n`);
248
261
  });
249
262
  } catch (err) {
263
+ if (archiveAs) throw err;
250
264
  writeStderrBestEffort(`[session] clear-fork failed (sess=${session.id}): ${err?.message || err}\n`);
251
265
  }
252
266
  }
253
267
 
268
+ const ARCHIVE_ID = /^sess_[A-Za-z0-9_-]{8,200}$/;
269
+
270
+ /** The fork id a clear was asked to archive under, '' when none. */
271
+ function clearArchiveId(value, sessionId) {
272
+ if (value === undefined || value === null || value === '') return '';
273
+ const id = String(value);
274
+ if (!ARCHIVE_ID.test(id) || id === sessionId || isAgentHomeSessionId(id)) {
275
+ throw new Error('clear: archiveAs must be a fresh session id');
276
+ }
277
+ if (loadSession(id)) throw new Error(`clear: session ${id} already exists`);
278
+ return id;
279
+ }
280
+
254
281
  export async function clearSessionMessages(sessionId, options = {}) {
255
282
  const session = loadSession(sessionId);
256
283
  if (!session) return false;
257
284
  // Don't resurrect a closed session just to clear its messages.
258
285
  if (session.closed === true) return false;
259
286
  const clearOptions = options && typeof options === 'object' ? options : {};
260
- const compact = clearOptions.compact === true;
287
+ const archiveAs = clearArchiveId(clearOptions.archiveAs, sessionId);
288
+ // An archived reset keeps the whole transcript: never compacted first.
289
+ const compact = clearOptions.compact === true && !archiveAs;
261
290
  const currentMessages = Array.isArray(session.messages) ? session.messages : [];
262
291
  const beforeMessageTokens = estimateMessagesTokens(currentMessages);
263
292
  const { messages, clearCompactError } = compact
@@ -275,8 +304,14 @@ export async function clearSessionMessages(sessionId, options = {}) {
275
304
  // content the live session retains via the summary, so the fork duplicated
276
305
  // it as a confusing extra Recent row ("Re-attached after compaction…" —
277
306
  // user report). Plain /clear (no summary kept) still forks.
307
+ // `fork: false` (a room member's reset: the room's own log keeps the
308
+ // conversation) skips the copy; an `archiveAs` clear always keeps one.
278
309
  const summaryCarriedForward = keep.some(isCompactSummaryMessage);
279
- if (hasUserConversationMessage(messages) && !summaryCarriedForward) forkOutgoingTranscript(session, messages, now);
310
+ const keepCopy = clearOptions.fork !== false || Boolean(archiveAs);
311
+ if (keepCopy && hasUserConversationMessage(messages) && !summaryCarriedForward) {
312
+ const fork = forkOutgoingTranscript(session, messages, now, archiveAs);
313
+ if (archiveAs) await fork;
314
+ }
280
315
 
281
316
  session.messages = keep;
282
317
  // Clear truncates the transcript wholesale; drop the provider prefix
@@ -9,6 +9,7 @@ import { saveSession, saveSessionAsync, saveSessionAsyncDeferred, loadSession, s
9
9
  import { _liveSessions } from '../store/live-state.mjs';
10
10
  import { _recoverTurnCheckpointDurably } from './turn-checkpoint-recovery.mjs';
11
11
  import { isAgentOwner } from '../../agent-owner.mjs';
12
+ import { AGENT_MODE, resolveSessionMode, sessionModeOf } from '../agent-mode.mjs';
12
13
  import { getHiddenAgent } from '../../internal-agents.mjs';
13
14
  import { loadConfig } from '../../config.mjs';
14
15
  import {
@@ -19,6 +20,7 @@ import {
19
20
  import { normalizeAutoClearConfig, resolveAutoClearIdleMs } from '../../runtime-core/config-helpers.mjs';
20
21
  import { _buildBaseRules } from './rules-cache.mjs';
21
22
  import { composeSessionSystem, seedSessionMessages } from './session-prompt-composition.mjs';
23
+ import { agentProfileRevision, leadRosterRevision } from '../../context/role-instructions.mjs';
22
24
  import {
23
25
  _prepareResumeTools,
24
26
  delegationDisabled,
@@ -206,12 +208,22 @@ function logSessionSurface({ resolvedAgent, toolPermission, tools }) {
206
208
  }
207
209
  }
208
210
 
209
- export function createSession(opts) {
210
- const presetObj = opts.preset && typeof opts.preset === 'object' ? opts.preset : null;
211
- const resolved = resolveAgentRuntimeProfile(opts, presetObj);
211
+ export function createSession(createOpts) {
212
+ const presetObj = createOpts.preset && typeof createOpts.preset === 'object' ? createOpts.preset : null;
213
+ const resolved = resolveAgentRuntimeProfile(createOpts, presetObj);
212
214
  const { profile } = resolved;
213
- const route = resolveSessionRoute(opts, presetObj, profile);
215
+ const route = resolveSessionRoute(createOpts, presetObj, profile);
214
216
  const { providerName, modelName, provider, toolPreset, id } = route;
217
+ // Agent or coding mode (agent-mode.mjs), resolved once and persisted. An
218
+ // agent-mode Lead acts as its agent: a schedule/webhook run that names only
219
+ // its `ownerAgentId` gets that agent's role, persona and connector policy
220
+ // (all keyed by `session.agent`), as its home thread does.
221
+ const sessionMode = sessionModeOf({ ...createOpts, id });
222
+ const actingAgent = resolveSessionMode({ session: { ...createOpts, id } }).agentId;
223
+ const opts =
224
+ sessionMode === AGENT_MODE && !createOpts.agent && actingAgent
225
+ ? { ...createOpts, agent: actingAgent, sessionMode }
226
+ : { ...createOpts, sessionMode };
215
227
  const routeFallbacks = normalizeWorkflowRoute({
216
228
  provider: providerName,
217
229
  model: modelName,
@@ -227,7 +239,10 @@ export function createSession(opts) {
227
239
  // Scoped to explicit-breakpoint (Anthropic-family) providers only.
228
240
  const providerCacheOpts = resolved.providerCacheOpts || buildSessionProviderCacheOpts(providerName, id, opts.agent);
229
241
  const surface = resolveSessionToolSurface(opts, { profile, toolPreset, modelName });
230
- const prompt = composeSessionSystem(opts, { profile, providerName, modelName, surface });
242
+ // The agent profile the prompt is composed from, read first: an edit landing
243
+ // during composition leaves the stamp behind, never ahead (refreshAgentProfile).
244
+ const agentProfile = surface.ownerIsAgent ? '' : agentProfileRevision(surface.resolvedAgent);
245
+ const prompt = composeSessionSystem(opts, { profile, providerName, modelName, surface, sessionMode });
231
246
  const messages = seedSessionMessages(prompt, opts.files);
232
247
  logSessionSurface(surface);
233
248
  const contextMeta = resolveSessionContextMeta(provider, modelName, {
@@ -246,6 +261,11 @@ export function createSession(opts) {
246
261
  origin: sessionOriginFields(opts, { profile, presetObj, providerCacheOpts, surface }),
247
262
  });
248
263
  session.providerCacheOptsOverride = Boolean(resolved.providerCacheOpts);
264
+ if (agentProfile) {
265
+ session.agentProfileRevision = agentProfile;
266
+ // The bot roster its prompt lists (a Lead's catalog slices).
267
+ session.agentRosterRevision = leadRosterRevision(opts.leadWorkflowParts);
268
+ }
249
269
  if (routeFallbacks?.length) session.routeFallbacks = routeFallbacks;
250
270
  // A non-native agent sends core + bridge, built from its filtered tools.
251
271
  applyAgentToolExposure(session);
@@ -532,6 +552,8 @@ export async function resumeSession(sessionId, preset, options = {}) {
532
552
  ensureCodexWireSessionId(session);
533
553
  if (!_applyDesktopResumeScope(session, options)) return null;
534
554
  if (!session.owner) session.owner = 'user';
555
+ // Records from before the mode existed get it resolved and stamped.
556
+ session.sessionMode = sessionModeOf(session);
535
557
  if (Object.hasOwn(options, 'mcpScopeId')) {
536
558
  session.mcpScopeId = String(options.mcpScopeId || '').trim() || null;
537
559
  }