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
@@ -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 {
@@ -18,7 +19,9 @@ import {
18
19
  } from '../../agent-runtime/cache-strategy.mjs';
19
20
  import { normalizeAutoClearConfig, resolveAutoClearIdleMs } from '../../runtime-core/config-helpers.mjs';
20
21
  import { _buildBaseRules } from './rules-cache.mjs';
22
+ import { stripProviderReplayMessages } from './provider-replay.mjs';
21
23
  import { composeSessionSystem, seedSessionMessages } from './session-prompt-composition.mjs';
24
+ import { agentProfileRevision, leadRosterRevision } from '../../context/role-instructions.mjs';
22
25
  import {
23
26
  _prepareResumeTools,
24
27
  delegationDisabled,
@@ -206,12 +209,22 @@ function logSessionSurface({ resolvedAgent, toolPermission, tools }) {
206
209
  }
207
210
  }
208
211
 
209
- export function createSession(opts) {
210
- const presetObj = opts.preset && typeof opts.preset === 'object' ? opts.preset : null;
211
- const resolved = resolveAgentRuntimeProfile(opts, presetObj);
212
+ export function createSession(createOpts) {
213
+ const presetObj = createOpts.preset && typeof createOpts.preset === 'object' ? createOpts.preset : null;
214
+ const resolved = resolveAgentRuntimeProfile(createOpts, presetObj);
212
215
  const { profile } = resolved;
213
- const route = resolveSessionRoute(opts, presetObj, profile);
216
+ const route = resolveSessionRoute(createOpts, presetObj, profile);
214
217
  const { providerName, modelName, provider, toolPreset, id } = route;
218
+ // Agent or coding mode (agent-mode.mjs), resolved once and persisted. An
219
+ // agent-mode Lead acts as its agent: a schedule/webhook run that names only
220
+ // its `ownerAgentId` gets that agent's role, persona and connector policy
221
+ // (all keyed by `session.agent`), as its home thread does.
222
+ const sessionMode = sessionModeOf({ ...createOpts, id });
223
+ const actingAgent = resolveSessionMode({ session: { ...createOpts, id } }).agentId;
224
+ const opts =
225
+ sessionMode === AGENT_MODE && !createOpts.agent && actingAgent
226
+ ? { ...createOpts, agent: actingAgent, sessionMode }
227
+ : { ...createOpts, sessionMode };
215
228
  const routeFallbacks = normalizeWorkflowRoute({
216
229
  provider: providerName,
217
230
  model: modelName,
@@ -227,7 +240,12 @@ export function createSession(opts) {
227
240
  // Scoped to explicit-breakpoint (Anthropic-family) providers only.
228
241
  const providerCacheOpts = resolved.providerCacheOpts || buildSessionProviderCacheOpts(providerName, id, opts.agent);
229
242
  const surface = resolveSessionToolSurface(opts, { profile, toolPreset, modelName });
230
- const prompt = composeSessionSystem(opts, { profile, providerName, modelName, surface });
243
+ // The agent profile the prompt is composed from, read first: an edit landing
244
+ // during composition leaves the stamp behind, never ahead (refreshAgentProfile).
245
+ const agentProfile = surface.ownerIsAgent
246
+ ? ''
247
+ : agentProfileRevision(surface.resolvedAgent, { mixy: sessionMode === AGENT_MODE });
248
+ const prompt = composeSessionSystem(opts, { profile, providerName, modelName, surface, sessionMode });
231
249
  const messages = seedSessionMessages(prompt, opts.files);
232
250
  logSessionSurface(surface);
233
251
  const contextMeta = resolveSessionContextMeta(provider, modelName, {
@@ -246,6 +264,11 @@ export function createSession(opts) {
246
264
  origin: sessionOriginFields(opts, { profile, presetObj, providerCacheOpts, surface }),
247
265
  });
248
266
  session.providerCacheOptsOverride = Boolean(resolved.providerCacheOpts);
267
+ if (agentProfile) {
268
+ session.agentProfileRevision = agentProfile;
269
+ // The bot roster its prompt lists (a Lead's catalog slices).
270
+ session.agentRosterRevision = leadRosterRevision(opts.leadWorkflowParts);
271
+ }
249
272
  if (routeFallbacks?.length) session.routeFallbacks = routeFallbacks;
250
273
  // A non-native agent sends core + bridge, built from its filtered tools.
251
274
  applyAgentToolExposure(session);
@@ -401,27 +424,11 @@ function resetSessionForRouteChange(id, session, previousModel, previousProvider
401
424
  _forgetPreparedResume(id);
402
425
  }
403
426
 
404
- // Every message field a provider adapter replays or re-reads as native state.
405
- const PROVIDER_REPLAY_FIELDS = [
406
- 'providerReplay',
407
- 'reasoning',
408
- 'thinkingBlocks',
409
- 'assistantBlocks',
410
- 'reasoningItems',
411
- 'reasoningContent',
412
- 'providerMetadata',
413
- ];
414
-
415
- // Provider-native replay (reasoning items, thinking blocks) belongs to the
416
- // provider that produced it. Replace, never mutate, so delta saves rewrite.
427
+ // Provider-native replay (reasoning items, thinking blocks, signatures)
428
+ // belongs to the provider that produced it (see provider-replay.mjs).
417
429
  function stripProviderReplay(session) {
418
430
  if (!Array.isArray(session.messages)) return;
419
- session.messages = session.messages.map((message) => {
420
- if (!message || !PROVIDER_REPLAY_FIELDS.some((field) => field in message)) return message;
421
- const plain = { ...message };
422
- for (const field of PROVIDER_REPLAY_FIELDS) delete plain[field];
423
- return plain;
424
- });
431
+ session.messages = stripProviderReplayMessages(session.messages);
425
432
  }
426
433
 
427
434
  // `options.crossProvider` is the explicit in-place continuation of an
@@ -532,6 +539,8 @@ export async function resumeSession(sessionId, preset, options = {}) {
532
539
  ensureCodexWireSessionId(session);
533
540
  if (!_applyDesktopResumeScope(session, options)) return null;
534
541
  if (!session.owner) session.owner = 'user';
542
+ // Records from before the mode existed get it resolved and stamped.
543
+ session.sessionMode = sessionModeOf(session);
535
544
  if (Object.hasOwn(options, 'mcpScopeId')) {
536
545
  session.mcpScopeId = String(options.mcpScopeId || '').trim() || null;
537
546
  }
@@ -4,9 +4,13 @@
4
4
  // carry all four to the provider with their cache tiers.
5
5
  import { buildSkillManifest, composeSystemPrompt } from '../../context/collect.mjs';
6
6
  import { configuredInstructionFilesMode, loadInstructionFilesContext } from '../../context/instruction-files.mjs';
7
+ import { agentModeIdentityLine } from '../../context/role-instructions.mjs';
8
+ import { AGENT_MODE, isRoutineSource, sessionModeOf } from '../agent-mode.mjs';
9
+ import { MIXY_AGENT_ID } from '../agent-home.mjs';
7
10
  import {
8
11
  _buildBaseRules,
9
12
  _buildAgentRules,
13
+ _buildAgentModeRules,
10
14
  _buildLeadRules,
11
15
  _buildLeadMetaContext,
12
16
  _buildLeadLanguageContext,
@@ -81,8 +85,9 @@ export function buildWorkspacePromptContext(session, cwd) {
81
85
  ];
82
86
  return {
83
87
  environmentContext: buildShellEnvironmentContext({ cwd }, ownerIsAgent, tools),
88
+ // Agent mode never carries project instruction files.
84
89
  instructionFilesContext:
85
- session?.skipInstructionFiles === true
90
+ session?.skipInstructionFiles === true || sessionModeOf(session) === AGENT_MODE
86
91
  ? undefined
87
92
  : loadInstructionFilesContext({ mode: configuredInstructionFilesMode(), cwd }),
88
93
  };
@@ -90,24 +95,60 @@ export function buildWorkspacePromptContext(session, cwd) {
90
95
 
91
96
  // BP1/BP2/BP3 rule blocks plus the volatile environment, composed into the
92
97
  // four system blocks; environmentTailContext is the persisted env tail.
93
- export function sessionRolePromptFields(identity, { ownerIsAgent, toolFreeRole, delegationFree }) {
98
+ // A Lead's BP3 rule slices, ordered for prefix sharing (composeSystemPrompt):
99
+ // LEAD.md's common lines first; then the mode block (coding: LEAD.md's two
100
+ // passages; agent mode: their conversational variants and AGENT_MODE.md);
101
+ // then Mixy's MIXY.md; last the per-session tail — routine line, identity.
102
+ // Agent mode is a Lead created as an agent (agent-mode.mjs).
103
+ export function sessionRolePromptFields(
104
+ identity,
105
+ { ownerIsAgent, toolFreeRole, delegationFree, sessionMode, routine = false }
106
+ ) {
94
107
  const skipRules = identity.skipAgentRules || toolFreeRole;
108
+ const lead = !ownerIsAgent && !skipRules;
109
+ const agentMode = lead && sessionMode === AGENT_MODE;
110
+ const mixy = (identity.agent || MIXY_AGENT_ID) === MIXY_AGENT_ID;
95
111
  let roleRules = '';
96
112
  if (!skipRules) {
97
113
  roleRules = ownerIsAgent
98
114
  ? _buildAgentRules(identity.agentRulesProfile)
99
115
  : _buildLeadRules({ includeLeadBrief: !delegationFree });
100
116
  }
117
+ const identityLine = agentMode
118
+ ? agentModeIdentityLine(identity.agent || MIXY_AGENT_ID)
119
+ : lead
120
+ ? _buildLeadRules({ includeLeadBrief: !delegationFree, part: 'identity' })
121
+ : '';
101
122
  return {
102
123
  roleRules,
124
+ ...(lead
125
+ ? {
126
+ leadModeRules: _buildLeadRules({ includeLeadBrief: !delegationFree, part: agentMode ? 'agent' : 'coding' }),
127
+ identityContext: identityLine ? `# Identity\n\n${identityLine}` : '',
128
+ }
129
+ : {}),
130
+ ...(agentMode
131
+ ? {
132
+ agentModeRules: _buildAgentModeRules({ part: 'conduct' }),
133
+ ...(mixy ? { mixyRules: _buildAgentModeRules({ part: 'mixy' }) } : {}),
134
+ ...(routine ? { routineRules: _buildAgentModeRules({ part: 'routine' }) } : {}),
135
+ }
136
+ : {}),
103
137
  userPrompt: identity.systemPrompt,
104
- skipRoleCatalog: !ownerIsAgent,
138
+ // A coding Lead carries no role section; an agent's home thread (a user
139
+ // session running as that agent) carries its own AGENT.md role, and Mixy
140
+ // in agent mode its optional agents/lead/AGENT.md.
141
+ skipRoleCatalog: !ownerIsAgent && (!identity.agent || identity.agent === 'lead') && !(agentMode && mixy),
142
+ // Mixy's persona (bots/mixy/PERSONA.md) is Mixy's alone: keyed on Mixy's
143
+ // own agent-mode thread, never on the `lead` role a coding Lead shares.
144
+ skipPersona: !ownerIsAgent && (!identity.agent || identity.agent === 'lead') && !(agentMode && mixy),
105
145
  agent: identity.agent,
106
146
  profile: { skip: identity.profileSkip },
107
147
  };
108
148
  }
109
149
 
110
- export function composeSessionSystem(opts, { profile, providerName, modelName, surface }) {
150
+ export function composeSessionSystem(opts, { profile, providerName, modelName, surface, sessionMode }) {
151
+ const agentMode = sessionMode === AGENT_MODE;
111
152
  const { ownerIsAgent, resolvedAgent, skills } = surface;
112
153
  // A tool-free hidden role (a one-shot classifier or title writer) carries
113
154
  // only its own role rules: the shared tool policy and the worker conduct
@@ -130,9 +171,15 @@ export function composeSessionSystem(opts, { profile, providerName, modelName, s
130
171
  agent: resolvedAgent,
131
172
  agentRulesProfile: surface.agentRulesProfile,
132
173
  };
133
- const roleFields = sessionRolePromptFields(promptIdentity, { ownerIsAgent, toolFreeRole, delegationFree });
174
+ const roleFields = sessionRolePromptFields(promptIdentity, {
175
+ ownerIsAgent,
176
+ toolFreeRole,
177
+ delegationFree,
178
+ sessionMode,
179
+ routine: isRoutineSource(opts.sourceType),
180
+ });
134
181
  const leadOnly = !skipAgentRules && !ownerIsAgent;
135
- const metaContext = leadOnly ? _buildLeadMetaContext() : '';
182
+ const metaContext = leadOnly ? _buildLeadMetaContext({ includeOutputStyle: !agentMode }) : '';
136
183
  // Lead-only response language; closes the environment block so no English
137
184
  // system text (session/shell/git lines) follows it.
138
185
  const languageContext = leadOnly ? _buildLeadLanguageContext() : '';
@@ -146,9 +193,10 @@ export function composeSessionSystem(opts, { profile, providerName, modelName, s
146
193
  // already sit at the end of this string to stay last after a refresh.
147
194
  const environmentTailContext = [shellEnvironmentContext, languageContext].filter(Boolean).join('\n\n---\n\n');
148
195
  // User instruction files (AGENTS.md / CLAUDE.md, global then the project
149
- // root's); a tool-free role and a profile skipping `claudemd` get none.
196
+ // root's); a tool-free role, a profile skipping `claudemd` and agent mode
197
+ // get none.
150
198
  const instructionFilesContext =
151
- toolFreeRole || profile?.skip?.claudemd
199
+ toolFreeRole || profile?.skip?.claudemd || agentMode
152
200
  ? ''
153
201
  : loadInstructionFilesContext({ mode: configuredInstructionFilesMode(), cwd: opts.cwd });
154
202
  const composed = composeSystemPrompt({
@@ -157,6 +205,8 @@ export function composeSessionSystem(opts, { profile, providerName, modelName, s
157
205
  metaContext: metaContext || undefined,
158
206
  languageContext: languageContext || undefined,
159
207
  workflowContext: opts.workflowContext || null,
208
+ // The workflow's orchestration and agent-catalog slices (workflow/context.mjs leadPromptParts).
209
+ ...(ownerIsAgent ? {} : opts.leadWorkflowParts),
160
210
  coreMemoryContext: opts.coreMemoryContext || null,
161
211
  instructionFilesContext: instructionFilesContext || null,
162
212
  skillManifest: buildSkillManifest(skills),
@@ -70,6 +70,20 @@ export function sessionOriginFields(opts, { profile, presetObj, providerCacheOpt
70
70
  // scheduler/daily-standup, webhook/github-push, lead/worker.
71
71
  sourceType: opts.sourceType || null,
72
72
  sourceName: opts.sourceName || null,
73
+ // The agent a schedule/webhook run acts for: whose memory it may write
74
+ // (session-runtime/services/memory-access.mjs). Absent on other sessions.
75
+ ...(opts.ownerAgentId ? { ownerAgentId: String(opts.ownerAgentId) } : {}),
76
+ // A coding task session an agent created (session create): who asked, from
77
+ // which thread, the request, and the title it was given.
78
+ ...(opts.taskOrigin
79
+ ? {
80
+ taskOrigin: opts.taskOrigin,
81
+ title: opts.taskOrigin.title,
82
+ titleLocked: true,
83
+ titleSource: 'ai',
84
+ titleUpdatedAt: Date.now(),
85
+ }
86
+ : {}),
73
87
  // Provider-scoped unified cache key — one shard per provider,
74
88
  // shared across all roles / sources (agent/maintenance/mcp/
75
89
  // scheduler/webhook). Role or source-specific context must be
@@ -137,6 +151,8 @@ export function buildSessionRecord({ opts, route, presetObj, surface, prompt, me
137
151
  selectedContextWindow: opts.selectedContextWindow || null,
138
152
  agent: opts.agent,
139
153
  owner: opts.owner || 'user',
154
+ // 'agent' | 'coding' (session/agent-mode.mjs), resolved at creation.
155
+ sessionMode: opts.sessionMode,
140
156
  bp3CoreContext: prompt.sessionMarkerCore,
141
157
  promptIdentity: prompt.promptIdentity,
142
158
  callerDisallowedTools: [...(opts.callerDisallowedTools ?? opts.disallowedTools ?? [])],
@@ -22,8 +22,9 @@ import { ROUTE_TOOL_ORDER } from '../../runtime-core/tool-catalog-data.mjs';
22
22
  // the sort overhead on ~30 tools is negligible.
23
23
  function _getMcpTools(mcpScopeId = null, cwd = null) {
24
24
  // Project scope: servers limited to other project roots stay connected
25
- // but leave this session's schema (session-runtime/extension-scopes).
26
- const mcp = filterMcpToolsForSession(getMcpTools(mcpScopeId) || [], cwd);
25
+ // but leave this session's schema (session-runtime/extension-scopes). An
26
+ // agent's connectors apply at dispatch, never here.
27
+ const mcp = filterMcpToolsForSession(getMcpTools(mcpScopeId) || [], cwd, null);
27
28
  // `public:false` tools stay registered in internal-tools for runtime
28
29
  // rewrites/dispatch, but must never enter any model-visible schema (Lead
29
30
  // full/mcp included). Filter before mapping because the projection below
@@ -18,6 +18,7 @@ import {
18
18
  writeCheckpointHeader,
19
19
  } from './turn-checkpoint-journal.mjs';
20
20
  import { captureTurnCheckpointContextState, restoreTurnCheckpointContextState } from './turn-checkpoint-context.mjs';
21
+ import { stripProviderReplayMessages } from './provider-replay.mjs';
21
22
 
22
23
  export { turnCheckpointPath, turnMessagesForCheckpoint };
23
24
  export { captureTurnCheckpointContextState, restoreTurnCheckpointContextState };
@@ -198,7 +199,12 @@ export function recoverTurnCheckpoint(session) {
198
199
  const current = Array.isArray(session.messages) ? session.messages : [];
199
200
  const start = findTurnStart(current, checkpoint.currentUserContent);
200
201
  const kept = start >= 0 ? current.slice(0, start) : current;
201
- session.messages = checkpoint.fullTranscript === true ? finalized.messages : [...kept, ...finalized.messages];
202
+ // The route switched models mid-turn (the marker says so) and the journal
203
+ // may predate the switch: its turn still carries the previous provider's
204
+ // native replay state, which the saved route's provider must not receive.
205
+ const turnMessages =
206
+ marker?.routeChanged === true ? stripProviderReplayMessages(finalized.messages) : finalized.messages;
207
+ session.messages = checkpoint.fullTranscript === true ? turnMessages : [...kept, ...turnMessages];
202
208
  // Re-anchor provider usage / post-compact replacement state to the exact
203
209
  // canonical checkpoint representation. If a legacy or corrupted checkpoint
204
210
  // cannot prove alignment, retain the last provider reading for display but
@@ -7,6 +7,7 @@
7
7
  // flag usageMetricsTurnIncremental. manager.mjs wires it via configureUsageMetricsRuntime().
8
8
  import { providerInputExcludesCache } from '../../providers/registry.mjs';
9
9
  import { reasoningUsage, combineReasoningUsage } from '../../../../shared/llm/reasoning-usage.mjs';
10
+ import { foldUsageReadout } from '../../../../shared/usage-readout-totals.mjs';
10
11
  import {
11
12
  loadSession,
12
13
  saveSessionAsync,
@@ -553,6 +554,11 @@ export async function persistIterationMetrics(delta) {
553
554
  if (!isReplay) {
554
555
  if (runtimeEntry) runtimeEntry.usageMetricsTurnIncremental = true;
555
556
  accumulateSessionUsage(session, delta);
557
+ // The composer readout's session-cumulative cache hit and speed: one fold
558
+ // per model call of this session (every tool-loop iteration), replaced
559
+ // immutably so a consumer can tell by identity that it changed. Sessions
560
+ // saved before this field began at their first call after it.
561
+ session.usageReadout = foldUsageReadout(session.usageReadout, delta, delta.requestMs);
556
562
  recordLastContextTokens(session, iterationContext(delta));
557
563
  }
558
564
  session.lastIterationIndex = iterationIndex;
@@ -0,0 +1,58 @@
1
+ // manager/workflow-switch-notice.mjs
2
+ // A mid-conversation workflow switch (session-runtime/tool-policy-refresh.mjs
3
+ // switchSessionWorkflow) rebuilds the session's prompt and tool surface and
4
+ // stages `session.workflowSwitch`, the one-time notice the next user turn
5
+ // carries in its reminder block. Lifecycle mirrors the working-folder notice
6
+ // (prompt-utils snapshotPendingWorkspaceNotice): snapshot when the turn is
7
+ // built, acknowledge once the provider accepted it, so a failed turn re-sends
8
+ // it. The state object is replaced, never mutated:
9
+ // from — the workflow the model was last told about (kept across switches
10
+ // made before a notice reached it);
11
+ // to — the workflow the prompt carries now.
12
+
13
+ function workflowRef(workflow) {
14
+ const id = String(workflow?.id || '').trim();
15
+ return id ? { id, name: String(workflow?.name || id).trim() } : null;
16
+ }
17
+
18
+ /** Stage the notice of a switch from `from` to `to`; a switch back to what the
19
+ * model was last told drops it. Returns the staged state or null. */
20
+ export function stageWorkflowSwitchNotice(session, from, to) {
21
+ const prior = session.workflowSwitch;
22
+ const told = prior ? prior.from : workflowRef(from);
23
+ const next = workflowRef(to);
24
+ if (!next || told?.id === next.id) {
25
+ delete session.workflowSwitch;
26
+ return null;
27
+ }
28
+ session.workflowSwitch = { version: 1, revision: (Number(prior?.revision) || 0) + 1, from: told, to: next };
29
+ return session.workflowSwitch;
30
+ }
31
+
32
+ function workflowNoticeContent({ from, to }) {
33
+ const before = from?.name || 'the previous workflow';
34
+ return [
35
+ '<system-reminder>',
36
+ '# Workflow Changed',
37
+ `The workflow changed from ${before} to ${to.name}. Earlier turns of this conversation ran under ${before}; the system prompt was updated in place, so its Active Workflow rules, delegation instructions, available agents and tools are now those of ${to.name}. Follow them from this turn on.`,
38
+ `Agents already started under ${before} keep running with their own instructions; new delegation uses the current tools and agents.`,
39
+ '</system-reminder>',
40
+ ].join('\n');
41
+ }
42
+
43
+ /**
44
+ * The workflow notice the next user turn carries, or null.
45
+ * @returns {{ revision: number, content: string } | null}
46
+ */
47
+ export function snapshotPendingWorkflowNotice(session) {
48
+ const pending = session?.workflowSwitch;
49
+ if (!pending?.to?.id) return null;
50
+ return { revision: pending.revision, content: workflowNoticeContent(pending) };
51
+ }
52
+
53
+ /** The notice of `revision` reached the model: nothing is due any more. */
54
+ export function acknowledgePendingWorkflowNotice(session, revision) {
55
+ if (!session?.workflowSwitch || session.workflowSwitch.revision !== Number(revision)) return false;
56
+ delete session.workflowSwitch;
57
+ return true;
58
+ }
@@ -0,0 +1,27 @@
1
+ // The representative agent's display name. Its id stays `lead`; the user may
2
+ // rename it (agents/lead/agent.json `name`, user data dir), Mixy by default.
3
+ // Every model- or user-facing string that names it reads this, re-read on each
4
+ // use so a rename applies at once. Text shared across agents (tool schemas,
5
+ // other agents' shared prompt block) says "the representative agent" instead,
6
+ // so a rename never changes cached bytes.
7
+ import { dirname, join } from 'node:path';
8
+ import { mixdogGlobalDir, readSafe } from '../context/skill-catalog.mjs';
9
+ import { mixyIdentityDir } from '../../../shared/agent-store.mjs';
10
+
11
+ export const DEFAULT_MIXY_NAME = 'Mixy';
12
+
13
+ /** The display name in a lead manifest (agent.json), else Mixy. */
14
+ export function mixyNameOf(manifest) {
15
+ const name = typeof manifest?.name === 'string' ? manifest.name.replace(/\s+/g, ' ').trim() : '';
16
+ return name || DEFAULT_MIXY_NAME;
17
+ }
18
+
19
+ export function mixyDisplayName() {
20
+ try {
21
+ // bots/mixy once Mixy is its own bot, agents/lead before (agent-store.mjs).
22
+ const dir = mixyIdentityDir(dirname(mixdogGlobalDir('agents')));
23
+ return mixyNameOf(JSON.parse(readSafe(join(dir, 'agent.json')) || '{}'));
24
+ } catch {
25
+ return DEFAULT_MIXY_NAME;
26
+ }
27
+ }
@@ -31,6 +31,7 @@ import { bumpUsageMetricsEpoch } from './manager/usage-metrics.mjs';
31
31
  import { resetReadStateAfterCompaction } from './read-dedup.mjs';
32
32
  import { scheduleCompactionArchive, stampCompactionMessageIds } from './compaction-archive-capture.mjs';
33
33
  import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
34
+ import { isAgentModeSession } from './agent-mode.mjs';
34
35
  import { applyPendingWorkspaceReroot } from './manager/prompt-utils.mjs';
35
36
  import { backfillFallbackSessionTitle } from './manager/session-fallback-title.mjs';
36
37
  import {
@@ -335,6 +336,32 @@ function reportCompactFailure(ctx, run, compactErr) {
335
336
  // Detection is narrow on purpose — signal.aborted or a true AbortError — so
336
337
  // the recall pipeline's SYNTHETIC "…aborted: memory … ; head preserved"
337
338
  // failure (a real compact failure, message text aside) still escalates.
339
+ function isGenuineCompactOverflow(compactErr) {
340
+ return (
341
+ compactErr?.code === 'AGENT_CONTEXT_OVERFLOW' ||
342
+ compactErr?.name === 'AgentContextOverflowError' ||
343
+ isContextOverflowError(compactErr)
344
+ );
345
+ }
346
+
347
+ // Agent mode retries a failed compaction quietly: the first ordinary failure
348
+ // in a turn neither ends the send nor reaches the user — the context is still
349
+ // under the model's boundary, and the next send's pre-send pass compacts
350
+ // again. A second failure, an abort or a genuine overflow takes the normal path.
351
+ function retryCompactQuietly(ctx, run, compactErr) {
352
+ const { state } = ctx;
353
+ // `opts` is the turn's loop options (one per turn; each send builds a new state).
354
+ if (!isAgentModeSession(state.sessionRef) || !state.opts || state.opts.quietCompactRetryUsed) return false;
355
+ if (isCompactAbort(state.signal, compactErr) || isGenuineCompactOverflow(compactErr)) return false;
356
+ state.opts.quietCompactRetryUsed = true;
357
+ acknowledgeGoalReminder(state.sessionRef, run);
358
+ reportCompactFailure(ctx, run, compactErr);
359
+ writeStderrBestEffort(
360
+ `[loop] pre-send compact failed; retrying on the next send (sess=${state.sessionId || 'unknown'}): ${compactErr?.message || compactErr}\n`
361
+ );
362
+ return true;
363
+ }
364
+
338
365
  function throwCompactFailure(ctx, run, compactErr) {
339
366
  const { state, compactPolicy, decision } = ctx;
340
367
  const { sessionRef, sessionId, signal, model } = state;
@@ -353,11 +380,7 @@ function throwCompactFailure(ctx, run, compactErr) {
353
380
  // compact failure, not "latest turn cannot fit" — mislabeling it as
354
381
  // overflow hides the real cause and misroutes downstream overflow
355
382
  // handling. Surface an explicit compact-failed error.
356
- const genuineOverflow =
357
- compactErr?.code === 'AGENT_CONTEXT_OVERFLOW' ||
358
- compactErr?.name === 'AgentContextOverflowError' ||
359
- isContextOverflowError(compactErr);
360
- if (genuineOverflow) {
383
+ if (isGenuineCompactOverflow(compactErr)) {
361
384
  throw agentContextOverflowError(
362
385
  {
363
386
  stage: 'pre_send',
@@ -513,6 +536,10 @@ async function compactBeforeSend(ctx) {
513
536
  }
514
537
  run.summaryChanged = messagesArrayChanged(messages, run.compacted);
515
538
  } catch (compactErr) {
539
+ if (retryCompactQuietly(ctx, run, compactErr)) {
540
+ await setStage(opts, 'requesting');
541
+ return;
542
+ }
516
543
  throwCompactFailure(ctx, run, compactErr);
517
544
  }
518
545
  await setStage(opts, 'requesting');
@@ -0,0 +1,86 @@
1
+ export interface RoutineProvenance {
2
+ kind: 'schedule' | 'webhook';
3
+ name: string;
4
+ status: 'ok' | 'failed';
5
+ summary?: string;
6
+ error?: string;
7
+ }
8
+
9
+ export interface RoutineMessageMeta {
10
+ source: string;
11
+ routine: Record<string, unknown>;
12
+ transcript: { at: number; messageId?: string; attachments?: unknown[] };
13
+ }
14
+ export interface RoutineMessage {
15
+ role: 'user';
16
+ content: string;
17
+ meta: RoutineMessageMeta;
18
+ }
19
+ export interface RoutineNotice {
20
+ text: string;
21
+ tone: 'info' | 'warn';
22
+ routine: Record<string, unknown>;
23
+ /** Live run notices only (routineRunNotice). */
24
+ messageId?: string;
25
+ /** A task report's stored files (handoff-artifacts.mjs). */
26
+ attachments?: Array<{
27
+ id: string;
28
+ attachmentRef: string;
29
+ kind: string;
30
+ name: string;
31
+ mimeType: string;
32
+ size: number;
33
+ }>;
34
+ }
35
+
36
+ export const ROUTINE_RESULT_SOURCE: 'schedule-result';
37
+ export const TASK_LOOP_CAP: number;
38
+ export function routineSummary(text: unknown): string;
39
+ export function routineResultMessage(options: {
40
+ kind?: 'schedule' | 'webhook';
41
+ name: string;
42
+ ok: boolean;
43
+ text?: unknown;
44
+ runId?: string | null;
45
+ runSessionId?: string | null;
46
+ reason?: string | null;
47
+ paused?: boolean;
48
+ at?: number;
49
+ messageId?: string;
50
+ }): RoutineMessage;
51
+ export function routineOmittedMessage(options: { count: number; at?: number; messageId?: string }): RoutineMessage;
52
+ export function agentTaskMessage(options: {
53
+ title: string;
54
+ status: string;
55
+ text?: unknown;
56
+ taskSessionId: string;
57
+ /** 'session': any session other than a task session the agent created. */
58
+ target?: 'task' | 'session';
59
+ round?: number;
60
+ /** false: the report was posted without waking the agent (round-trip cap). */
61
+ woken?: boolean;
62
+ /** The files the turn produced (handoff-artifacts.mjs). */
63
+ files?: ReadonlyArray<{ name: string; path: string; mimeType?: string; size?: number; kind?: string; id?: string }>;
64
+ omitted?: number;
65
+ at?: number;
66
+ messageId?: string;
67
+ }): RoutineMessage;
68
+ export function agentTaskWakePrompt(messages: ReadonlyArray<{ content: string }>): string;
69
+ export function isRoutineResultMessage(message: unknown): boolean;
70
+ export function normalizeRoutineProvenance(value: unknown): RoutineProvenance | null;
71
+ export function routineProvenance(message: unknown): RoutineProvenance | null;
72
+ export function routineResultNotice(message: unknown): RoutineNotice;
73
+ export function routineRunNotice(options: {
74
+ kind?: 'schedule' | 'webhook';
75
+ name: string;
76
+ runId: string;
77
+ state: string;
78
+ ok?: boolean;
79
+ runSessionId?: string | null;
80
+ messageId?: string;
81
+ }): RoutineNotice;
82
+ export type RoutineHumanNeededOutcome = 'login_required' | 'human_action_required' | 'sensitive_confirmation_required';
83
+ export function routineHumanNeededText(outcome: RoutineHumanNeededOutcome, site: string): string;
84
+ export function routineHumanNeeded(summary: unknown): { outcome: RoutineHumanNeededOutcome; site: string } | null;
85
+ export function routineLoginRequiredText(site: string): string;
86
+ export function routineLoginRequiredSite(summary: unknown): string | null;