mixdog 1.2.6 → 1.3.1

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 (294) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +71 -9
  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/isolated-test-env.mjs +16 -0
  7. package/src/agents/reviewer/AGENT.md +4 -2
  8. package/src/agents/reviewer/agent.json +1 -1
  9. package/src/defaults/shipped-history.json +10 -0
  10. package/src/defaults/skills/browser-use/SKILL.md +5 -0
  11. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  12. package/src/defaults/skills/setup/references/actions.md +14 -4
  13. package/src/defaults/skills/setup-guide/SKILL.md +178 -0
  14. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  15. package/src/lib/rules-builder.cjs +55 -6
  16. package/src/output-styles/common.md +13 -8
  17. package/src/rules/agent/AGENT.md +4 -0
  18. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  19. package/src/rules/agent-mode/MIXY.md +28 -0
  20. package/src/rules/agent-mode/ROUTINE.md +6 -0
  21. package/src/rules/lead/LEAD.md +7 -3
  22. package/src/rules/shared/70-delivery.md +3 -0
  23. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  24. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  25. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +241 -14
  26. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +4 -2
  27. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +216 -0
  28. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -4
  29. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  30. package/src/runtime/agent/orchestrator/mcp/client.mjs +115 -11
  31. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  33. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  34. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  35. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  36. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  37. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +1 -0
  40. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +10 -3
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  43. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +7 -8
  44. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +3 -1
  46. package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
  47. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +9 -2
  49. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  50. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  51. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
  52. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
  53. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +96 -1
  55. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  56. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  57. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +14 -0
  58. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  59. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  60. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  61. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +21 -5
  62. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +40 -7
  63. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +52 -20
  64. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  66. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  67. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  68. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  69. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  70. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +166 -0
  71. package/src/runtime/agent/orchestrator/session/agent-room.mjs +650 -0
  72. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  73. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  75. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +192 -0
  76. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  77. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  78. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  79. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  80. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  81. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
  82. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  83. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +108 -6
  84. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -1
  85. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +74 -15
  86. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +9 -1
  87. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  88. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  89. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +49 -0
  90. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
  91. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
  93. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  94. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  95. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  96. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  97. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
  98. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  99. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  100. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +33 -24
  101. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +58 -8
  102. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  103. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
  105. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  106. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  107. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +27 -0
  108. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  109. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +86 -0
  110. package/src/runtime/agent/orchestrator/session/routine-result.mjs +329 -0
  111. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  112. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +28 -9
  113. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  114. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  115. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  116. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  117. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +58 -10
  118. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +92 -15
  119. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  120. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  121. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +25 -1
  123. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +8 -0
  124. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +6 -0
  125. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +3 -0
  126. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +75 -57
  127. package/src/runtime/attachments/store.mjs +6 -0
  128. package/src/runtime/browser-bridge/client.mjs +24 -0
  129. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  130. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  131. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  132. package/src/runtime/channels/lib/webhook.mjs +0 -1
  133. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  134. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  135. package/src/runtime/computer-bridge/client.mjs +28 -1
  136. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  137. package/src/runtime/connectors/google-mcp.mjs +446 -0
  138. package/src/runtime/effort-judge/judge-client.mjs +1 -1
  139. package/src/runtime/effort-judge/model-manifest.json +7 -7
  140. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  141. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  142. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  143. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  144. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  145. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  146. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  147. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  148. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  149. package/src/runtime/memory/lib/memory.mjs +7 -1
  150. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  151. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  152. package/src/runtime/memory/lib/tool-call-handler.mjs +2 -0
  153. package/src/runtime/memory/tool-defs.mjs +5 -0
  154. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  155. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  156. package/src/runtime/shared/agent-avatar.mjs +320 -0
  157. package/src/runtime/shared/agent-kind.mjs +152 -0
  158. package/src/runtime/shared/agent-route-config.d.mts +4 -0
  159. package/src/runtime/shared/agent-route-config.mjs +30 -0
  160. package/src/runtime/shared/agent-store.mjs +132 -0
  161. package/src/runtime/shared/atomic-file.mjs +14 -1
  162. package/src/runtime/shared/bot-split-migration.mjs +457 -0
  163. package/src/runtime/shared/config-patch.mjs +20 -1
  164. package/src/runtime/shared/config.mjs +8 -1
  165. package/src/runtime/shared/connector-accounts.mjs +15 -0
  166. package/src/runtime/shared/connector-catalog.d.mts +51 -0
  167. package/src/runtime/shared/connector-catalog.mjs +228 -0
  168. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  169. package/src/runtime/shared/connector-triggers.mjs +45 -0
  170. package/src/runtime/shared/err-text.mjs +3 -1
  171. package/src/runtime/shared/error-presentation.mjs +91 -6
  172. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  173. package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
  174. package/src/runtime/shared/handoff-artifacts.mjs +61 -0
  175. package/src/runtime/shared/held-request-host.d.mts +12 -0
  176. package/src/runtime/shared/held-request-host.mjs +71 -0
  177. package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
  178. package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
  179. package/src/runtime/shared/model-route-fallback.mjs +79 -7
  180. package/src/runtime/shared/plain-preview.d.mts +1 -0
  181. package/src/runtime/shared/plain-preview.mjs +33 -0
  182. package/src/runtime/shared/routine-limits.d.mts +11 -0
  183. package/src/runtime/shared/routine-limits.mjs +21 -0
  184. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  185. package/src/runtime/shared/routine-trigger.mjs +44 -0
  186. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  187. package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
  188. package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
  189. package/src/runtime/shared/slow-span.mjs +4 -12
  190. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  191. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  192. package/src/runtime/shared/turn-changes.mjs +0 -8
  193. package/src/runtime/shared/usage-readout-totals.mjs +58 -0
  194. package/src/runtime/shared/user-data-guard.mjs +119 -38
  195. package/src/session-runtime/agent-home-delivery.mjs +826 -0
  196. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  197. package/src/session-runtime/agent-room-delivery.mjs +133 -0
  198. package/src/session-runtime/agent-task-sessions.mjs +460 -0
  199. package/src/session-runtime/boot/apis.mjs +63 -18
  200. package/src/session-runtime/boot/facade.mjs +5 -8
  201. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  202. package/src/session-runtime/boot/providers.mjs +14 -4
  203. package/src/session-runtime/boot/tools.mjs +3 -0
  204. package/src/session-runtime/channel-config-api.mjs +53 -5
  205. package/src/session-runtime/config-lifecycle.mjs +43 -2
  206. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  207. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  208. package/src/session-runtime/handoff-attachments.mjs +593 -0
  209. package/src/session-runtime/held-requests.mjs +228 -0
  210. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  211. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  212. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  213. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +56 -3
  214. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  215. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  216. package/src/session-runtime/lifecycle/session-switch/resume.mjs +4 -0
  217. package/src/session-runtime/lifecycle/shared.mjs +6 -0
  218. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  219. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  220. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  221. package/src/session-runtime/model-route/route-persist.mjs +2 -2
  222. package/src/session-runtime/model-route/route-tuning.mjs +7 -1
  223. package/src/session-runtime/model-route/set-route.mjs +35 -4
  224. package/src/session-runtime/native-web-search.mjs +2 -2
  225. package/src/session-runtime/new-session-config.mjs +6 -1
  226. package/src/session-runtime/owned-automation-run.mjs +229 -0
  227. package/src/session-runtime/resource-api.mjs +5 -0
  228. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  229. package/src/session-runtime/resource-skills-api.mjs +12 -0
  230. package/src/session-runtime/room-member-gate.mjs +48 -0
  231. package/src/session-runtime/route-resolution.mjs +5 -11
  232. package/src/session-runtime/routine-home-turn.mjs +156 -0
  233. package/src/session-runtime/routine-owner.mjs +48 -0
  234. package/src/session-runtime/schedule-session-run.mjs +30 -64
  235. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -1
  236. package/src/session-runtime/services/agent-tool/send-flow.mjs +11 -3
  237. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  238. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  239. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  240. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
  241. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  242. package/src/session-runtime/services/channel-admin.mjs +93 -24
  243. package/src/session-runtime/services/memory-access.mjs +37 -0
  244. package/src/session-runtime/services/provider-admin.mjs +74 -39
  245. package/src/session-runtime/services/schedule-access.mjs +91 -0
  246. package/src/session-runtime/session-create/session-options.mjs +34 -7
  247. package/src/session-runtime/session-hooks.mjs +16 -8
  248. package/src/session-runtime/setup-tool/executor.mjs +79 -6
  249. package/src/session-runtime/setup-tool/extended-actions.mjs +90 -20
  250. package/src/session-runtime/setup-tool/settings-contract.mjs +14 -6
  251. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  252. package/src/session-runtime/skills-api.mjs +16 -1
  253. package/src/session-runtime/tool-policy-refresh.mjs +249 -29
  254. package/src/session-runtime/turn/session-ops.mjs +8 -1
  255. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +10 -2
  256. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +14 -0
  257. package/src/session-runtime/webhook-session-run.mjs +49 -67
  258. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  259. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +650 -25
  260. package/src/session-runtime/workflow-agents-api/agent-route.mjs +67 -7
  261. package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
  262. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  263. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  264. package/src/standalone/daemon-agent-control.mjs +1 -8
  265. package/src/standalone/daemon-session-control.mjs +21 -4
  266. package/src/standalone/daemon.mjs +31 -5
  267. package/src/standalone/session-protocol.mjs +27 -0
  268. package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
  269. package/src/standalone/session-service/agent-tree.mjs +4 -3
  270. package/src/standalone/session-service/session-calls/session-turns.mjs +28 -0
  271. package/src/standalone/session-service.mjs +19 -0
  272. package/src/tui/app/slash-commands.mjs +20 -0
  273. package/src/tui/dist/index.mjs +234 -95
  274. package/src/tui/session/context-state.mjs +6 -1
  275. package/src/tui/session/notice-surface.mjs +34 -4
  276. package/src/tui/session/queue-helpers.mjs +2 -1
  277. package/src/tui/session/session-api/extensions.mjs +4 -0
  278. package/src/tui/session/session-api/intake/submission.mjs +5 -0
  279. package/src/tui/session/session-api/integrations.mjs +8 -0
  280. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  281. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  282. package/src/tui/session/session-api/routes.mjs +61 -5
  283. package/src/tui/session/session-flow/drain.mjs +12 -3
  284. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  285. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  286. package/src/tui/session/session-flow/steering.mjs +58 -24
  287. package/src/tui/session/tool-approval.mjs +26 -5
  288. package/src/tui/session/transcript-intake.mjs +8 -2
  289. package/src/tui/session/transcript-spill.mjs +5 -1
  290. package/src/tui/session/tui-steering-persist.mjs +65 -42
  291. package/src/tui/session/turn-live-call.mjs +64 -0
  292. package/src/tui/session/turn.mjs +34 -8
  293. package/src/session-runtime/automation-agents.mjs +0 -94
  294. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -4,8 +4,22 @@ import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.
4
4
  import { normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
5
5
  import { ensureProviderEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
6
6
  import { resolveRouteContextState, selectionFastCapable } from '../route-state.mjs';
7
- import { canonicalizeAgentRouteStorage, withAgentDisabled } from '../../runtime/shared/agent-route-config.mjs';
8
- import { agentEditorId } from './shared.mjs';
7
+ import {
8
+ canonicalizeAgentRouteStorage,
9
+ isAgentDisabled,
10
+ withAgentDisabled,
11
+ } from '../../runtime/shared/agent-route-config.mjs';
12
+ import { agentEditorId, resolveDataDir } from './shared.mjs';
13
+ import {
14
+ isStoreBot,
15
+ readBotRoute,
16
+ readMixyRoute,
17
+ writeBotRoute,
18
+ writeMixyRoute,
19
+ } from '../../runtime/shared/agent-store.mjs';
20
+
21
+ /** Mixy's runtime id (agent-home.mjs MIXY_AGENT_ID). */
22
+ const MIXY_RUNTIME_ID = 'lead';
9
23
  import { routeChain, validateRouteChainProviders } from '../../runtime/shared/model-route-chain.mjs';
10
24
 
11
25
  // Agent route persistence. Route-scope isolation: an agent route owns its
@@ -23,8 +37,35 @@ export function createAgentRouteApi(deps) {
23
37
  saveConfigAndAdopt(canonicalizeAgentRouteStorage(withAgentDisabled(getConfig(), id, disabled)));
24
38
  }
25
39
 
40
+ // A bot of the store keeps its route in its own folder (agent-store.mjs);
41
+ // Mixy (`lead` here, its runtime id) keeps its own in bots/mixy — never the
42
+ // Lead's route, which the Main route setter owns.
43
+ const dataDir = () => (deps.cfgMod || deps.STANDALONE_DATA_DIR ? resolveDataDir(deps) : '');
44
+ const isMixy = (id) => id === MIXY_RUNTIME_ID && Boolean(dataDir());
45
+ const storeBot = (id) => isStoreBot(dataDir(), id);
46
+ function saveRoute(id, route, keepOff) {
47
+ if (isMixy(id)) {
48
+ writeMixyRoute(dataDir(), route);
49
+ return;
50
+ }
51
+ if (storeBot(id)) {
52
+ writeBotRoute(dataDir(), id, route);
53
+ saveDisabled(id, keepOff);
54
+ return;
55
+ }
56
+ const nextConfig = withAgentDisabled(getConfig(), id, keepOff);
57
+ nextConfig.agents = { ...(nextConfig.agents || {}), [id]: route };
58
+ saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
59
+ }
60
+
26
61
  // Only an explicitly empty provider removes an override: the agent inherits Main.
27
62
  function clearOverride(id) {
63
+ if (isMixy(id) || storeBot(id)) {
64
+ if (isMixy(id)) writeMixyRoute(dataDir(), null);
65
+ else writeBotRoute(dataDir(), id, null);
66
+ const inherited = normalizeWorkflowRoute(resolveRoute(getConfig(), {}));
67
+ return { ...(inherited || {}), inherited: true };
68
+ }
28
69
  const nextConfig = { ...getConfig() };
29
70
  const agents = { ...(nextConfig.agents || {}) };
30
71
  const hadOverride = Object.hasOwn(agents, id);
@@ -83,7 +124,20 @@ export function createAgentRouteApi(deps) {
83
124
  if (!id) throw new Error(`unknown agent "${agentId}"`);
84
125
  if (isHiddenAgent(id)) throw new Error(`agent "${id}" is internal and has no configurable route`);
85
126
  const requested = { ...(next || {}) };
86
- const stored = agentRouteFromConfig(getConfig(), id) || {};
127
+ // Compare-and-set: fill the route only while the agent has none. A route
128
+ // picked while the model resolved below stays (answered with `skipped`).
129
+ const onlyIfEmpty = requested.onlyIfEmpty === true;
130
+ delete requested.onlyIfEmpty;
131
+ const readStored = () => {
132
+ if (isMixy(id)) return readMixyRoute(dataDir()) || {};
133
+ if (storeBot(id)) return readBotRoute(dataDir(), id) || {};
134
+ return agentRouteFromConfig(getConfig(), id) || {};
135
+ };
136
+ const kept = () => ({ ...readStored(), id, skipped: true });
137
+ const stored = readStored();
138
+ if (onlyIfEmpty && clean(stored.provider)) return kept();
139
+ // Mixy is never off: its route request is a model pick alone.
140
+ if (isMixy(id)) delete requested.disabled;
87
141
  // Off is an explicit state, stored apart from the route so the model the
88
142
  // user picked survives and comes back when the agent is switched on. An
89
143
  // off request that also carries a model saves that model and stays off.
@@ -126,11 +180,17 @@ export function createAgentRouteApi(deps) {
126
180
  const [primary, ...fallbacks] = resolved;
127
181
  const routeToSave = normalizeWorkflowRoute({ ...primary, fallbacks });
128
182
  if (!routeToSave) throw new Error('agent route requires provider and model');
183
+ // No await from here to the write: the emptiness checked is the emptiness
184
+ // written over. An off agent stays off when it was only back-filled.
185
+ // A store bot's or Mixy's route is read from its folder on disk right here.
186
+ // An agent routed in the config file may have been written by another
187
+ // process whose change notification is still pending: re-read it first.
188
+ if (onlyIfEmpty && !isMixy(id) && !storeBot(id)) deps.reloadFullConfig?.();
189
+ if (onlyIfEmpty && clean(readStored().provider)) return kept();
190
+ const stayOff = onlyIfEmpty ? isAgentDisabled(getConfig(), id) : keepOff;
129
191
  // Picking a model is also the "on" switch unless the request keeps it off.
130
- const nextConfig = withAgentDisabled(getConfig(), id, keepOff);
131
- nextConfig.agents = { ...(nextConfig.agents || {}), [id]: routeToSave };
132
- saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
133
- return keepOff ? { ...routeToSave, id, disabled: true } : routeToSave;
192
+ saveRoute(id, routeToSave, stayOff);
193
+ return stayOff ? { ...routeToSave, id, disabled: true } : routeToSave;
134
194
  }
135
195
 
136
196
  return { setAgentRoute };
@@ -25,6 +25,7 @@ function applyWorkflowSlots(workflowInput, draft) {
25
25
  if (!normalized) continue;
26
26
  draft.presets = upsertWorkflowPreset(draft.presets, 'lead', normalized);
27
27
  draft.defaultPreset = workflowPresetId('lead');
28
+ draft.leadRoute = normalized;
28
29
  } else if (slot === 'agent') {
29
30
  if (normalized) draft.agentRoutes.worker = normalized;
30
31
  else delete draft.agentRoutes.worker;
@@ -72,10 +73,13 @@ export function createOnboardingApi(deps) {
72
73
  presets: Array.isArray(nextConfig.presets) ? nextConfig.presets.slice() : [],
73
74
  defaultPreset: nextConfig.default,
74
75
  agentRoutes: { ...(nextConfig.agents || {}) },
76
+ // The Lead route this save writes, from `defaultRoute` or `workflowRoutes.lead`.
77
+ leadRoute: null,
75
78
  };
76
79
  if (defaultRoute) {
77
80
  draft.presets = upsertWorkflowPreset(draft.presets, 'lead', defaultRoute);
78
81
  draft.defaultPreset = workflowPresetId('lead');
82
+ draft.leadRoute = defaultRoute;
79
83
  }
80
84
  const workflowInput =
81
85
  payload.workflowRoutes && typeof payload.workflowRoutes === 'object' ? payload.workflowRoutes : {};
@@ -104,12 +108,15 @@ export function createOnboardingApi(deps) {
104
108
  }
105
109
 
106
110
  saveConfigAndAdopt(canonicalizeAgentRouteStorage(nextConfig));
107
- if (defaultRoute) {
111
+ // Whatever wrote the Lead route also moves the runtime Main route, in the
112
+ // same synchronous step as the save: a pending conditional backfill then
113
+ // finds a route that is not empty instead of overwriting this choice.
114
+ if (draft.leadRoute) {
108
115
  setRouteState(
109
116
  resolveRoute(getConfig(), {
110
- provider: defaultRoute.provider,
111
- model: defaultRoute.model,
112
- effort: defaultRoute.effort,
117
+ provider: draft.leadRoute.provider,
118
+ model: draft.leadRoute.model,
119
+ effort: draft.leadRoute.effort,
113
120
  })
114
121
  );
115
122
  deps.invalidatePreSessionToolSurface?.();
@@ -48,11 +48,21 @@ export function createWorkflowPacksApi(deps) {
48
48
  }));
49
49
  }
50
50
 
51
- async function setWorkflow(workflowId) {
51
+ // `{ scope: 'session' }` (the composer's selector in a session): a session
52
+ // with a conversation switches alone from its next turn
53
+ // (switchSessionWorkflow) and the configured workflow — the default for new
54
+ // sessions — stays as it is. Without a conversation, and without the
55
+ // scope, the choice becomes the configured workflow and an empty session
56
+ // picks it up in place, as before.
57
+ async function setWorkflow(workflowId, options = {}) {
52
58
  const requested = normalizeWorkflowId(workflowId, DEFAULT_WORKFLOW_ID);
53
59
  const id = requested === 'solo' ? DEFAULT_WORKFLOW_ID : requested;
54
60
  const pack = loadWorkflowPack(resolveDataDir(deps), id);
55
61
  if (!pack || pack.id !== id) throw new Error(`workflow "${workflowId}" not found`);
62
+ if (options?.scope === 'session') {
63
+ const switched = await deps.switchSessionWorkflow?.(id);
64
+ if (switched) return { ...switched.workflow, ...switched };
65
+ }
56
66
  saveActiveWorkflow(id);
57
67
  const applied = await deps.refreshEmptySessionToolPolicy?.();
58
68
  deps.invalidatePreSessionToolSurface?.();
@@ -1,5 +1,6 @@
1
1
  import { createOnboardingApi } from './workflow-agents-api/onboarding.mjs';
2
2
  import { createAgentEditorApi } from './workflow-agents-api/agent-editor.mjs';
3
+ import { createAgentConnectorsApi } from './workflow-agents-api/agent-connectors.mjs';
3
4
  import { createAgentRouteApi } from './workflow-agents-api/agent-route.mjs';
4
5
  import { createWorkflowPacksApi } from './workflow-agents-api/workflow-packs.mjs';
5
6
  import { createStyleAndModeApi } from './workflow-agents-api/style-and-mode.mjs';
@@ -11,9 +12,11 @@ import { createStyleAndModeApi } from './workflow-agents-api/style-and-mode.mjs'
11
12
  // getOnboardingStatus from the composed runtime API through `this`.
12
13
  export function createWorkflowAgentsApi(deps) {
13
14
  const routeApi = createAgentRouteApi(deps);
15
+ const editorApi = createAgentEditorApi({ ...deps, setAgentRoute: routeApi.setAgentRoute });
14
16
  return {
15
17
  ...createOnboardingApi(deps),
16
- ...createAgentEditorApi({ ...deps, setAgentRoute: routeApi.setAgentRoute }),
18
+ ...editorApi,
19
+ ...createAgentConnectorsApi(deps, editorApi),
17
20
  ...routeApi,
18
21
  ...createWorkflowPacksApi(deps),
19
22
  ...createStyleAndModeApi(deps),
@@ -8,8 +8,6 @@
8
8
  // this control exists — the host takes `execute` as a constructor option), and
9
9
  // `cwd`. Output: { canonicalAgentTool, execute }.
10
10
 
11
- import { deliverAutomationCompletion } from '../session-runtime/automation-agents.mjs';
12
-
13
11
  export function createCanonicalAgentControl({ getSessionService, getSessionRuntimeHost, cwd }) {
14
12
  let canonicalAgentToolPromise = null;
15
13
 
@@ -34,12 +32,7 @@ export function createCanonicalAgentControl({ getSessionService, getSessionRunti
34
32
  isKeychainPrewarmReady: () => true,
35
33
  sessionSurface: sessionService.agentSurface,
36
34
  notifySessionCompletion(ownerSessionId, text, meta = {}) {
37
- // A schedule/webhook run owns no session runtime; its own
38
- // agent-result waiter takes the completions it is owed.
39
- return (
40
- getSessionRuntimeHost()?.notifySessionCompletion?.(ownerSessionId, text, meta) === true ||
41
- deliverAutomationCompletion(ownerSessionId, text, meta)
42
- );
35
+ return getSessionRuntimeHost()?.notifySessionCompletion?.(ownerSessionId, text, meta) === true;
43
36
  },
44
37
  });
45
38
  })
@@ -1,20 +1,27 @@
1
1
  import { SESSION_ID_PATTERN } from '../runtime/agent/orchestrator/runtime-core/session-id.mjs';
2
2
  import { sessionListOutput, sessionReadOutput, SESSION_TOOL_OUTPUT_BYTES } from './session-tool-output.mjs';
3
+ import { createAgentTaskSessions } from '../session-runtime/agent-task-sessions.mjs';
4
+ import { handoffContent } from '../session-runtime/handoff-attachments.mjs';
3
5
 
4
6
  const TARGET_ACTIONS = new Set(['status', 'read', 'send', 'cancel', 'delete']);
5
7
  const MUTATING_ACTIONS = new Set(['send', 'cancel', 'delete']);
6
8
 
7
9
  /** Model-facing session operations over the same service used by desktop and TUI. */
8
- export function createSessionControl({ getSessionService }) {
10
+ export function createSessionControl({
11
+ getSessionService,
12
+ agentTasks = createAgentTaskSessions({ getSessionService }),
13
+ }) {
9
14
  return async function executeSessionControl(args = {}, context = {}) {
10
15
  const action = args.action;
11
- if (action !== 'list' && !TARGET_ACTIONS.has(action)) {
16
+ if (action !== 'list' && action !== 'create' && !TARGET_ACTIONS.has(action)) {
12
17
  throw new Error(`session: unsupported remote action "${action}"`);
13
18
  }
14
19
  const caller = context.callerSessionId;
15
20
  if (typeof caller !== 'string' || !SESSION_ID_PATTERN.test(caller)) {
16
21
  throw new Error('session: caller session is required');
17
22
  }
23
+ // `callerAgentId`: set only for an attended agent conversation (workspace-tools.mjs).
24
+ if (action === 'create') return agentTasks.create(args, context);
18
25
  const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
19
26
  if (TARGET_ACTIONS.has(action) && (typeof sessionId !== 'string' || !SESSION_ID_PATTERN.test(sessionId))) {
20
27
  throw new Error(`session: session_id is required for action=${action}`);
@@ -45,12 +52,22 @@ export function createSessionControl({ getSessionService }) {
45
52
  if (typeof args.prompt !== 'string' || !args.prompt.trim()) {
46
53
  throw new Error('session: prompt is required for action=send');
47
54
  }
55
+ // An agent conversation's send reports its turn back to the agent's
56
+ // home: a task session it created by its stored origin, any other
57
+ // session by an origin for this send only.
58
+ // Forwarded files ride along as stored attachment parts (workspace-tools.mjs).
59
+ const prompt = handoffContent(args.prompt, context.attachmentParts);
60
+ const origin = await agentTasks.reportOrigin(sessionId, context.callerAgentId, caller);
61
+ if (origin) {
62
+ const { accepted, reportsBack } = await agentTasks.send(sessionId, prompt, origin);
63
+ return { sessionId, accepted, reportsBack };
64
+ }
48
65
  const result = await service.submitSession({
49
66
  sessionId,
50
- prompt: args.prompt,
67
+ prompt,
51
68
  options: { mode: 'prompt', transcriptMeta: { sender: 'lead' } },
52
69
  });
53
- return { sessionId, accepted: result.accepted === true };
70
+ return { sessionId, accepted: result.accepted === true, reportsBack: false };
54
71
  }
55
72
  if (action === 'cancel') {
56
73
  const result = await service.abortSession({ sessionId });
@@ -77,6 +77,8 @@ import { createStoredSessionViews } from './daemon-stored-session-views.mjs';
77
77
  import * as quotaResumeStore from '../runtime/shared/quota-resume-store.mjs';
78
78
  import { registerWorktreeCacheCleanups } from '../runtime/shared/worktrees/cache-cleanups.mjs';
79
79
  import { createInlineSessionRuntimeHost } from './session-runtime-inline-host.mjs';
80
+ import { recoverScheduleResults, setAgentHomeLiveSurface } from '../session-runtime/agent-home-delivery.mjs';
81
+ import { recoverRoutineDeliveries } from '../session-runtime/owned-automation-run.mjs';
80
82
  import { getStandaloneMemoryRuntime } from '../session-runtime/services/memory-runtime-proxy.mjs';
81
83
  import { registerTranscriptPurgeHook } from '../session-runtime/session-transcript-purge.mjs';
82
84
  import { createBootPhaseProfiler } from './boot-phase-profiler.mjs';
@@ -94,8 +96,8 @@ import { createChannelsRuntimeLoader } from './daemon-channels-loader.mjs';
94
96
  import { createDesktopRuntime } from './daemon-desktop-runtime.mjs';
95
97
  import { createCanonicalAgentControl } from './daemon-agent-control.mjs';
96
98
  import { createSessionControl } from './daemon-session-control.mjs';
99
+ import { createAgentTaskSessions } from '../session-runtime/agent-task-sessions.mjs';
97
100
  import { flushAllIndexWrites } from '../session-runtime/services/agent-tool/index-write-queue.mjs';
98
- import { registerAutomationAgentTool } from '../session-runtime/automation-agents.mjs';
99
101
  import {
100
102
  compareRuntimeVersions,
101
103
  SESSION_CAPABILITY_FINGERPRINT,
@@ -925,9 +927,8 @@ async function main() {
925
927
  cwd: CWD,
926
928
  });
927
929
  canonicalAgentControl = agentControl;
928
- // Schedules and webhooks run headless here; their sessions reach the same
929
- // canonical agent control through the automation tool scope.
930
- registerAutomationAgentTool(agentControl.execute);
930
+ // Agents' cross-session sends and task sessions, and their report watches.
931
+ const agentTasks = createAgentTaskSessions({ getSessionService: () => sessionService });
931
932
  const requestedRuntimeMode = String(process.env.MIXDOG_SESSION_RUNTIME_MODE || '')
932
933
  .trim()
933
934
  .toLowerCase();
@@ -941,7 +942,7 @@ async function main() {
941
942
  log,
942
943
  measureBootPhase: bootPhases.measure,
943
944
  executeAgentControl: agentControl.execute,
944
- executeSessionControl: createSessionControl({ getSessionService: () => sessionService }),
945
+ executeSessionControl: createSessionControl({ getSessionService: () => sessionService, agentTasks }),
945
946
  });
946
947
  // Codex-style runtime: daemon routing and independent async session actors
947
948
  // share one V8 isolate and module graph. CPU-heavy work stays in bounded
@@ -984,6 +985,31 @@ async function main() {
984
985
  },
985
986
  log,
986
987
  });
988
+ // Schedule results reach an open home thread at once (and wait out its turn).
989
+ setAgentHomeLiveSurface({
990
+ busy: (sessionId) => sessionService.liveSessionBusy(sessionId),
991
+ notice: (sessionId, notice) => sessionService.pushLiveSessionNotice(sessionId, notice),
992
+ // A task session's result (session create): the agent relays it in one
993
+ // turn. Resolves whether the turn was taken.
994
+ wake: async (sessionId, prompt, { id } = {}) =>
995
+ (
996
+ await sessionService.submitSession({
997
+ sessionId,
998
+ prompt,
999
+ options: { mode: 'prompt', kickoff: true, ...(id ? { id } : {}) },
1000
+ })
1001
+ )?.accepted === true,
1002
+ // A routine fire: one queued turn in its bot's thread (routine-home-turn.mjs).
1003
+ submit: async (sessionId, prompt, options) =>
1004
+ (await sessionService.submitSession({ sessionId, prompt, options }))?.accepted === true,
1005
+ abort: (sessionId) => sessionService.abortSession({ sessionId }),
1006
+ });
1007
+ // Results queued before a restart are posted now; results a finished run
1008
+ // could not queue (kept on its routine's record) are queued again.
1009
+ recoverScheduleResults().catch((error) => log(`routine results recovery failed: ${error?.message || error}`));
1010
+ // Agents' sends whose report an earlier process had not queued yet.
1011
+ agentTasks.recover().catch((error) => log(`agent task report recovery failed: ${error?.message || error}`));
1012
+ recoverRoutineDeliveries().catch((error) => log(`routine delivery recovery failed: ${error?.message || error}`));
987
1013
  sessionTransport = createSessionTransport({
988
1014
  // ctx carries the CLIENT token: the session service refcounts views across
989
1015
  // processes with it, so a terminal exiting cannot destroy the session a
@@ -9,7 +9,18 @@ export const SESSION_READ_ACTIONS = Object.freeze([
9
9
  'inspectProjectBranch',
10
10
  'isSessionTerminal',
11
11
  'contextStatus',
12
+ 'agentDefinitionPresence',
13
+ 'archivedAgentIdentities',
12
14
  'getAgentDefinition',
15
+ 'getAgentAvatarImage',
16
+ 'getAgentConnectors',
17
+ 'getProjectConnectors',
18
+ 'getAgentPermissions',
19
+ 'listConnectors',
20
+ 'listConnectorTools',
21
+ 'getLeadPersona',
22
+ 'getLeadAgentBody',
23
+ 'getLeadStatus',
13
24
  'getAutoClear',
14
25
  'getChannelSettings',
15
26
  'getChannelSetup',
@@ -91,6 +102,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
91
102
  'addPlugin',
92
103
  'addSkill',
93
104
  'agentControl',
105
+ 'appendRoomRows',
94
106
  'askSide',
95
107
  'authenticateProvider',
96
108
  'beginOAuthProviderLogin',
@@ -111,6 +123,8 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
111
123
  'consumeCodexRateLimitResetCredit',
112
124
  'createWorkflow',
113
125
  'deleteAgentDefinition',
126
+ 'restoreAgentDefinition',
127
+ 'purgeAgentDefinition',
114
128
  'deleteHookRule',
115
129
  'deleteMediaAsset',
116
130
  'deleteMediaAssets',
@@ -142,7 +156,9 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
142
156
  'reloadSkills',
143
157
  'rememberPromptHistory',
144
158
  'removeMcpServer',
159
+ 'removeSkill',
145
160
  'continueSessionInModel',
161
+ 'decideRoomTurn',
146
162
  'scheduleQuotaResume',
147
163
  'cancelQuotaResume',
148
164
  'retryFailedTurn',
@@ -164,7 +180,18 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
164
180
  'runDoctor',
165
181
  'runScheduleNow',
166
182
  'runUpdateNow',
183
+ 'runWebhookNow',
184
+ 'retryRoutineDelivery',
167
185
  'saveAgentDefinition',
186
+ 'setAgentConnectors',
187
+ 'setAgentConnectorEnabled',
188
+ 'setAgentConnectorToolPermission',
189
+ 'setProjectConnectorEnabled',
190
+ 'setAgentPermission',
191
+ 'saveLeadPersona',
192
+ 'saveLeadAgentBody',
193
+ 'setLeadCreated',
194
+ 'saveLeadIdentity',
168
195
  'saveMcpServer',
169
196
  'saveOpenAIUsageSessionKey',
170
197
  'saveProviderApiKey',
@@ -4,6 +4,43 @@
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { createAgentChildFactory } from './agent-child.mjs';
6
6
  import { partialHandoffTextFromSession } from '../../../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
7
+ import { attachmentsForIntake } from '../../../runtime/attachments/store.mjs';
8
+ import { artifactLines } from '../../../runtime/shared/handoff-artifacts.mjs';
9
+ import { collectTurnArtifacts, handoffContent, sessionRoots } from '../../../session-runtime/handoff-attachments.mjs';
10
+
11
+ /** An agent turn's prompt: text, or text with forwarded attachment parts
12
+ * (handoff-attachments.mjs), never any other shape. */
13
+ export function agentTurnContent(prompt) {
14
+ if (!Array.isArray(prompt)) return String(prompt || '');
15
+ const text = prompt
16
+ .filter((part) => part?.type === 'text' && !part.attachmentRef)
17
+ .map((part) => String(part.text || ''))
18
+ .join('\n');
19
+ return handoffContent(text, prompt);
20
+ }
21
+
22
+ /** The prompt row's metadata: a Lead's message, with the files it carries. */
23
+ export function agentTurnTranscriptMeta(content) {
24
+ const attachments = Array.isArray(content) ? attachmentsForIntake(content) : [];
25
+ return { sender: 'lead', ...(attachments.length ? { attachments } : {}) };
26
+ }
27
+
28
+ /** The artifact lines of the turn that began at `messageStart`; [] on any
29
+ * failure to read it (the handoff still goes out). */
30
+ async function turnArtifactLines(runtime, messageStart, descriptor) {
31
+ try {
32
+ const { messages } = await runtime.readModelMessages(messageStart);
33
+ const session = runtime.session || descriptor;
34
+ const { files, omitted } = collectTurnArtifacts(messages, {
35
+ cwd: descriptor.cwd,
36
+ roots: sessionRoots(session, descriptor.cwd),
37
+ });
38
+ // Text only: the caller forwards these by path (an id in text is never trusted).
39
+ return artifactLines(files, omitted, { ids: false });
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
7
44
 
8
45
  export function createAgentTurns({
9
46
  registry,
@@ -67,15 +104,16 @@ export function createAgentTurns({
67
104
  descriptor.stage = 'running';
68
105
  descriptor.updatedAt = Date.now();
69
106
  try {
107
+ const content = agentTurnContent(prompt);
70
108
  const options = {
71
109
  id: `agent-turn-${randomUUID()}`,
72
110
  mode: 'prompt',
73
111
  priority: 'next',
74
112
  context,
75
- transcriptMeta: { sender: 'lead' },
113
+ transcriptMeta: agentTurnTranscriptMeta(content),
76
114
  ...(entry.runtime.isWireSafe === true || typeof onToolResult !== 'function' ? {} : { onToolResult }),
77
115
  };
78
- const detail = await target.call(entry.runtime, String(prompt || ''), options);
116
+ const detail = await target.call(entry.runtime, content, options);
79
117
  if (detail?.status === 'failed') {
80
118
  throw new Error(String(detail.error || 'agent session turn failed'));
81
119
  }
@@ -91,7 +129,12 @@ export function createAgentTurns({
91
129
  const reason = String(detail?.reason || detail?.error || '').trim();
92
130
  throw new Error(`agent turn ${status}${reason ? `: ${reason}` : ''}`);
93
131
  }
94
- const result = detail?.result || { content: '' };
132
+ let result = detail?.result || { content: '' };
133
+ // The files the turn produced reach the owner with its handoff.
134
+ const files = await turnArtifactLines(entry.runtime, messageStart, descriptor);
135
+ if (files.length) {
136
+ result = { ...result, content: [String(result.content || ''), files.join('\n')].join('\n\n') };
137
+ }
95
138
  descriptor.status = 'idle';
96
139
  descriptor.stage = 'idle';
97
140
  descriptor.lastHandoff = typeof result?.content === 'string' ? result.content : '';
@@ -10,7 +10,7 @@
10
10
  import { createAgentCancellation } from './agent-tree/agent-cancel.mjs';
11
11
  import { createAgentRegistry } from './agent-tree/agent-registry.mjs';
12
12
  import { createAgentRehydration, lastStoredAgentHandoff } from './agent-tree/agent-rehydrate.mjs';
13
- import { createAgentTurns } from './agent-tree/agent-turns.mjs';
13
+ import { agentTurnContent, agentTurnTranscriptMeta, createAgentTurns } from './agent-tree/agent-turns.mjs';
14
14
  import { randomUUID } from 'node:crypto';
15
15
 
16
16
  export { SESSION_ID_PATTERN } from './agent-tree/agent-registry.mjs';
@@ -139,12 +139,13 @@ export function createAgentTree({
139
139
  const cancelEpoch = registry.assertAdmission(descriptor.id);
140
140
  const runtime = sessionOwner(descriptor.id)?.runtime;
141
141
  if (!runtime || !stateBusy(runtime.getState())) return null;
142
- const accepted = await runtime.submitAsync(String(prompt || ''), {
142
+ const content = agentTurnContent(prompt);
143
+ const accepted = await runtime.submitAsync(content, {
143
144
  id: `agent-message-${randomUUID()}`,
144
145
  mode: 'prompt',
145
146
  priority: 'next',
146
147
  context,
147
- transcriptMeta: { sender: 'lead' },
148
+ transcriptMeta: agentTurnTranscriptMeta(content),
148
149
  });
149
150
  if (accepted === false) throw new Error('agent follow-up was not accepted');
150
151
  registry.assertAdmission(descriptor.id, cancelEpoch);
@@ -11,6 +11,10 @@ import {
11
11
  } from '../../../runtime/attachments/store.mjs';
12
12
  import { providerNativePdf } from '../../../runtime/agent/orchestrator/providers/registry.mjs';
13
13
  import { cancelBackgroundTasks } from '../../../runtime/shared/background-tasks.mjs';
14
+ import { isAgentModeSession } from '../../../runtime/agent/orchestrator/session/agent-mode.mjs';
15
+ import { isAgentHomeSessionId } from '../../../runtime/agent/orchestrator/session/agent-home.mjs';
16
+ import { recognisedAgentRoomSlash } from '../../../tui/app/slash-commands.mjs';
17
+ import { promptContentText } from '../../../tui/session/queue-helpers.mjs';
14
18
 
15
19
  /** Whether an abort on this session is an Agent cancelling its own turn: the
16
20
  * catalog knows it, the runtime declares agent-only visibility, or it is a
@@ -35,6 +39,17 @@ function abortsAgentTurn(id, state, hasAgentSession) {
35
39
  );
36
40
  }
37
41
 
42
+ /** A prompt the user typed: not an agent's send (`sender: 'lead'`), a hidden
43
+ * wake, a routine fire or any other runtime-made turn. */
44
+ export function isUserTurn(options = {}) {
45
+ return (
46
+ (options.mode || 'prompt') === 'prompt' &&
47
+ options.kickoff !== true &&
48
+ options.isMeta !== true &&
49
+ options.transcriptMeta?.sender !== 'lead'
50
+ );
51
+ }
52
+
38
53
  export function createSessionTurnCalls(ctx) {
39
54
  const { log, readStoredGoal, listStoredActiveGoalSessionIds, hasAgentSession, sessionResult, advanceForCaller } = ctx;
40
55
  const { retainUnwatched } = ctx.retention;
@@ -76,9 +91,22 @@ export function createSessionTurnCalls(ctx) {
76
91
  },
77
92
  };
78
93
  }
94
+ // A bot's conversation room runs only its own /compact and /clear (the
95
+ // composer's rule): every other leading `/` is message text for the model.
96
+ if (isAgentModeSession(entry.runtime.session ?? { id })) {
97
+ const text = typeof intake.prompt === 'string' ? intake.prompt : promptContentText(intake.prompt);
98
+ if (!recognisedAgentRoomSlash(text)) submissionOptions = { ...submissionOptions, skipSlashCommands: true };
99
+ }
79
100
  const accepted = await Promise.resolve(target.call(entry.runtime, intake.prompt, submissionOptions));
80
101
  if (accepted === true) {
81
102
  entry.reservedOnly = false;
103
+ if (isAgentHomeSessionId(id) && isUserTurn(submissionOptions)) {
104
+ // The user spoke in this agent's home: its round trips with other
105
+ // sessions count from zero again, so a capped thread wakes again.
106
+ void import('../../../session-runtime/agent-home-delivery.mjs')
107
+ .then(({ resetAgentTaskRounds }) => resetAgentTaskRounds(id))
108
+ .catch((error) => log(`round reset of ${id} failed: ${error?.message || error}`));
109
+ }
82
110
  }
83
111
  const step = settleAction(entry, 'headless session submit');
84
112
  log(`session submit session=${id} accepted=${accepted === true}`);
@@ -237,6 +237,23 @@ export function createSessionService({
237
237
  return stopPromise;
238
238
  }
239
239
 
240
+ /** Whether the runtime hosting `sessionId` is mid-turn or holds queued input. */
241
+ function liveSessionBusy(sessionId) {
242
+ const owner = projection.sessionOwner(sessionId);
243
+ return Boolean(owner) && retention.stateBusy(owner.runtime?.getState?.());
244
+ }
245
+
246
+ /** Show a transcript notice in the hosted runtime of `sessionId` (no turn)
247
+ * and publish it to its views; false when no runtime hosts it. */
248
+ function pushLiveSessionNotice(sessionId, { text, tone = 'info', ...options } = {}) {
249
+ const owner = projection.sessionOwner(sessionId);
250
+ if (typeof owner?.runtime?.pushNotice !== 'function') return false;
251
+ owner.runtime.pushNotice(text, tone, { ...options, transcript: true });
252
+ const step = projection.advance(owner);
253
+ if (step.changed) projection.publishStep(owner, step);
254
+ return true;
255
+ }
256
+
240
257
  function getStatus() {
241
258
  const busy = retention.liveBusyCount();
242
259
  let watched = 0;
@@ -282,6 +299,8 @@ export function createSessionService({
282
299
  },
283
300
  methods: {
284
301
  ...calls,
302
+ liveSessionBusy,
303
+ pushLiveSessionNotice,
285
304
  stop,
286
305
  startQuotaResume: () => quotaResume?.start(),
287
306
  runDueQuotaResumes: () => quotaResume?.tick() ?? Promise.resolve(),
@@ -142,6 +142,26 @@ for (const command of SLASH_COMMANDS) {
142
142
  }
143
143
  }
144
144
 
145
+ /**
146
+ * The registered command a prompt runs, by the renderer's rule
147
+ * (apps/desktop renderer/slash-commands.ts recognisedSlashSpan): the text,
148
+ * trimmed, begins with `/` and its first whitespace-delimited token names a
149
+ * command or alias. Returns that token lowercased, else null: any other
150
+ * leading `/` (an absolute path, a stray word) is ordinary message text.
151
+ */
152
+ export function recognisedSlashToken(text) {
153
+ const token = /^\s*\/(\S+)/.exec(String(text ?? ''))?.[1].toLowerCase();
154
+ return token && SLASH_COMMAND_BY_TOKEN.has(token) ? token : null;
155
+ }
156
+
157
+ /** The only commands a bot's conversation room runs, by their literal name
158
+ * (`/new` stays text); mirrors the renderer's recognisedAgentRoomSlash. */
159
+ const AGENT_ROOM_SLASH_TOKENS = new Set(['compact', 'clear']);
160
+
161
+ export function recognisedAgentRoomSlash(text) {
162
+ return AGENT_ROOM_SLASH_TOKENS.has(recognisedSlashToken(text));
163
+ }
164
+
145
165
  export function normalizeSlashCommandName(cmd) {
146
166
  const name = String(cmd || '').toLowerCase();
147
167
  return SLASH_COMMAND_BY_TOKEN.get(name)?.name || name;