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
@@ -12,6 +12,8 @@ import {
12
12
  import { PRESET_ALIASES } from './tool-def.mjs';
13
13
  import { positiveInt } from '../../../runtime/shared/numbers.mjs';
14
14
  import { clean } from '../../../runtime/shared/clean.mjs';
15
+ import { readAgentDefinitionKind } from '../../../runtime/shared/agent-kind.mjs';
16
+ import { isStoreBot, userDefinitionDir } from '../../../runtime/shared/agent-store.mjs';
15
17
  import { normalizeContextTokens } from '../../../runtime/shared/llm/default-context-window.mjs';
16
18
 
17
19
  export { positiveInt };
@@ -300,13 +302,18 @@ const _frontmatterPermCache = new Map(); // key -> { value, atMs }
300
302
  const FRONTMATTER_PERM_CACHE_TTL_MS = envTimeoutMs('MIXDOG_AGENT_FRONTMATTER_TTL_MS', 5_000);
301
303
 
302
304
  function agentDeleted(cleanAgent, dataDir) {
303
- return Boolean(dataDir) && existsSync(join(dataDir, 'agents', cleanAgent, '.deleted'));
305
+ return Boolean(dataDir) && existsSync(join(userDefinitionDir(dataDir, cleanAgent), '.deleted'));
304
306
  }
305
307
 
306
308
  /** Where an agent's definition may live, in precedence order: the data dir
307
309
  * (user/custom agents) before the shipped source root. */
308
310
  function agentDefinitionFiles(cleanAgent, dataDir, standaloneSourceRoot) {
309
311
  const candidates = [];
312
+ if (dataDir && isStoreBot(dataDir, cleanAgent)) {
313
+ // A bot of the store: only its own folder (never the agents/ original).
314
+ candidates.push(join(userDefinitionDir(dataDir, cleanAgent), 'AGENT.md'));
315
+ return candidates;
316
+ }
310
317
  if (dataDir) {
311
318
  candidates.push(join(dataDir, 'agents', cleanAgent, 'AGENT.md'));
312
319
  candidates.push(join(dataDir, 'agents', `${cleanAgent}.md`));
@@ -352,6 +359,28 @@ export function readAgentFrontmatterPermission(agent, dataDir, standaloneSourceR
352
359
  );
353
360
  }
354
361
 
362
+ const _frontmatterKindCache = new Map(); // key -> { value, atMs }
363
+ const _frontmatterKindValueCache = new Map(); // key -> { value, atMs }
364
+
365
+ /** The agent definition's kind (isContactAgentId reads it), by the one
366
+ * read-only rule (agent-kind.mjs), cached per agent for the frontmatter TTL. */
367
+ export function readAgentFrontmatterKind(agent, dataDir, standaloneSourceRoot) {
368
+ const id = clean(agent);
369
+ if (!id || agentDeleted(id, dataDir)) return null;
370
+ const cacheKey = `${dataDir}\u0000${id}`;
371
+ const cached = _frontmatterKindCache.get(cacheKey);
372
+ if (cached && Date.now() - cached.atMs < FRONTMATTER_PERM_CACHE_TTL_MS) return cached.value;
373
+ // An explicit kind in the definition in effect (any layout) wins; only an
374
+ // unmarked user copy goes to the evidence rule.
375
+ const explicit = cachedFrontmatterValue(_frontmatterKindValueCache, id, dataDir, standaloneSourceRoot, (fm) =>
376
+ typeof fm.kind === 'string' && fm.kind.trim() ? fm.kind.trim().toLowerCase() : null
377
+ );
378
+ const value =
379
+ explicit || (dataDir ? readAgentDefinitionKind({ dataDir, shippedRoot: standaloneSourceRoot, id }) : null);
380
+ _frontmatterKindCache.set(cacheKey, { value, atMs: Date.now() });
381
+ return value;
382
+ }
383
+
355
384
  /** The agent definition's `firstResponseTimeoutMs` (a non-negative integer;
356
385
  * 0 disables the first-response deadline), or null when it sets none. */
357
386
  export function readAgentFirstResponseTimeoutMs(agent, dataDir, standaloneSourceRoot) {
@@ -16,6 +16,7 @@ import {
16
16
  terminalResultHook,
17
17
  } from './job-task-reconcile.mjs';
18
18
  import { createTurnReviewCollector } from './turn-review.mjs';
19
+ import { handoffContent } from '../../handoff-attachments.mjs';
19
20
  import { settledWorkerStatus } from './worker-index/row-liveness.mjs';
20
21
 
21
22
  // `send` to an existing worker: resolve the live session, run one follow-up
@@ -69,8 +70,10 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
69
70
  return hooks;
70
71
  }
71
72
 
72
- function runTurn({ session, sessionId, prompt, args }, turnHooks) {
73
+ function runTurn({ session, sessionId, prompt: text, args }, turnHooks) {
73
74
  const cwd = session.cwd || defaultCwd;
75
+ // The caller's forwarded files ride along as attachment parts.
76
+ const prompt = handoffContent(text, args.attachmentParts);
74
77
  if (canUseSessionSurface(session)) {
75
78
  return sessionSurface.runTurn({ session, prompt, context: args.context || null, cwd, ...turnHooks });
76
79
  }
@@ -194,7 +197,11 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
194
197
  const { session, sessionId, prompt } = prepared;
195
198
  const tag = registry.tagForSession(sessionId);
196
199
  if (canUseSessionSurface(session) && views.isSessionBusy(sessionId)) {
197
- const queued = await sessionSurface.enqueueTurn({ session, prompt, context: prepared.args.context || null });
200
+ const queued = await sessionSurface.enqueueTurn({
201
+ session,
202
+ prompt: handoffContent(prompt, prepared.args.attachmentParts),
203
+ context: prepared.args.context || null,
204
+ });
198
205
  if (queued) {
199
206
  return renderResult({ queued: true, ...extras, tag, sessionId, agent: session.agent || null, ...queued });
200
207
  }
@@ -204,7 +211,8 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
204
211
  views.isSessionBusy(sessionId) &&
205
212
  typeof mgr.enqueuePendingMessage === 'function'
206
213
  ) {
207
- const queueDepth = mgr.enqueuePendingMessage(sessionId, prompt);
214
+ // The queued follow-up keeps the forwarded files, as the canonical queue does.
215
+ const queueDepth = mgr.enqueuePendingMessage(sessionId, handoffContent(prompt, prepared.args.attachmentParts));
208
216
  return renderResult({ queued: true, ...extras, tag, sessionId, agent: session.agent || null, queueDepth });
209
217
  }
210
218
  const job = spawnFlow.startJob(
@@ -9,6 +9,7 @@ import {
9
9
  agentDefinitionExists,
10
10
  clean,
11
11
  normalizeAgentName,
12
+ readAgentFrontmatterKind,
12
13
  readAgentFrontmatterPermission,
13
14
  resolvePrompt,
14
15
  terminalPidForContext,
@@ -18,8 +19,13 @@ import { normalizeAgentPermission } from '../../../../runtime/shared/markdown-fr
18
19
  import { resolveAgentSpawnPreset } from '../spawn-preset.mjs';
19
20
  import { AGENT_OWNER } from '../../../../runtime/agent/orchestrator/agent-owner.mjs';
20
21
  import { workspaceCwdLock } from '../../../workspace/admission-gate.mjs';
22
+ import { isContactAgentId } from '../../../../runtime/agent/orchestrator/runtime-core/workflow-ids.mjs';
23
+ import { AGENT_MODE, resolveSessionMode } from '../../../../runtime/agent/orchestrator/session/agent-mode.mjs';
24
+ import { MIXY_AGENT_ID } from '../../../../runtime/agent/orchestrator/session/agent-home.mjs';
25
+ import { mixyDisplayName } from '../../../../runtime/agent/orchestrator/session/mixy-name.mjs';
21
26
 
22
27
  export function createSpawnPlanner({
28
+ mgr = null,
23
29
  cfgMod,
24
30
  dataDir,
25
31
  STANDALONE_SOURCE_ROOT,
@@ -44,6 +50,18 @@ export function createSpawnPlanner({
44
50
  if (isAgentDisabled(config, agent)) {
45
51
  throw new Error(`agent spawn: agent "${agent}" is turned off`);
46
52
  }
53
+ // Only Mixy hands work to a contact agent; any other agent-mode Lead
54
+ // delegates to sub-agents alone (its catalog lists no contact either).
55
+ if (isContactAgentId(agent, readAgentFrontmatterKind(agent, dataDir, STANDALONE_SOURCE_ROOT))) {
56
+ const callerId = clean(context?.callerSessionId || context?.sessionId);
57
+ const caller = (callerId && mgr?.getSession?.(callerId)) || null;
58
+ const { mode, agentId } = resolveSessionMode({ session: caller, sessionId: callerId });
59
+ if (mode === AGENT_MODE && agentId !== MIXY_AGENT_ID) {
60
+ throw new Error(
61
+ `agent spawn: only ${mixyDisplayName()} (the representative agent) can hand work to the contact agent "${agent}"; delegate to a sub-agent instead`
62
+ );
63
+ }
64
+ }
47
65
  const agentPermission = readAgentFrontmatterPermission(agent, dataDir, STANDALONE_SOURCE_ROOT);
48
66
  const agentPerm = normalizeAgentPermission(agentPermission) || null;
49
67
  const { presetName, preset } = resolveAgentSpawnPreset(config, args);
@@ -56,10 +74,7 @@ export function createSpawnPlanner({
56
74
  // Workers share their Lead's execution root. While the Lead runs in its
57
75
  // worktree, that root is locked: neither args.cwd nor an inherited
58
76
  // (pre-transition) cwd can send a worker elsewhere.
59
- const lockedCwd = workspaceCwdLock([
60
- context?.ownerSessionId,
61
- context?.callerSessionId || context?.sessionId,
62
- ]);
77
+ const lockedCwd = workspaceCwdLock([context?.ownerSessionId, context?.callerSessionId || context?.sessionId]);
63
78
  const baseCwd = resolve(lockedCwd || callerCwd || defaultCwd || process.cwd());
64
79
  const workerCwd = !lockedCwd && clean(args.cwd) ? resolve(baseCwd, args.cwd) : baseCwd;
65
80
  const prompt = await resolvePrompt(args, workerCwd);
@@ -58,8 +58,10 @@ export function createSpawnPreparer({
58
58
  ensureProvider,
59
59
  sessionSurface = null,
60
60
  refreshTagsFromSessions,
61
+ mgr = null,
61
62
  }) {
62
63
  const { resolveSpawnPlan, spawnSessionSpec } = createSpawnPlanner({
64
+ mgr,
63
65
  cfgMod,
64
66
  dataDir,
65
67
  STANDALONE_SOURCE_ROOT,
@@ -25,6 +25,7 @@ import { settledWorkerStatus } from '../worker-index/row-liveness.mjs';
25
25
  import { saveSessionAsync } from '../../../../runtime/agent/orchestrator/session/store.mjs';
26
26
  import { presetDescriptor } from './spawn-prep.mjs';
27
27
  import { workerStageForStatus } from '../worker-rows.mjs';
28
+ import { handoffContent } from '../../../handoff-attachments.mjs';
28
29
 
29
30
  // Mid-turn worker persistence cadence. The watchdog sweep runs every second;
30
31
  // a transcript save is far more expensive than a bookkeeping stamp, so the
@@ -117,7 +118,9 @@ export function createSpawnRunner({
117
118
  };
118
119
 
119
120
  async function runTurn(prepared, turnHooks) {
120
- const { args, session, prompt, workerCwd } = prepared;
121
+ const { args, session, workerCwd } = prepared;
122
+ // The caller's forwarded files ride along as attachment parts.
123
+ const prompt = handoffContent(prepared.prompt, args.attachmentParts);
121
124
  if (typeof sessionSurface?.runTurn === 'function' && sessionSurface.canRun?.(session) !== false) {
122
125
  return sessionSurface.runTurn({ session, prompt, context: args.context || null, cwd: workerCwd, ...turnHooks });
123
126
  }
@@ -27,11 +27,10 @@ export function resolveAgentSpawnPreset(config, args = {}) {
27
27
  }
28
28
 
29
29
  const agentName = normalizeAgentName(args.agent);
30
- const agentRoute = !clean(args.preset)
31
- ? configuredAgentRouteCandidates(config, agentName)
32
- .map((candidate) => normalizeAgentRoute(candidate))
33
- .find(Boolean) || null
34
- : null;
30
+ // The agent's stored route: config.agents, or a store bot's own folder.
31
+ const candidates = !clean(args.preset) ? configuredAgentRouteCandidates(config, agentName) : [];
32
+ const agentRoute = candidates.map((candidate) => normalizeAgentRoute(candidate)).find(Boolean) || null;
33
+ const storedFallbacks = candidates.find((candidate) => normalizeAgentRoute(candidate))?.fallbacks;
35
34
  if (agentRoute) {
36
35
  return {
37
36
  presetName: agentPresetName(agentName),
@@ -41,9 +40,7 @@ export function resolveAgentSpawnPreset(config, args = {}) {
41
40
  type: 'agent',
42
41
  provider: agentRoute.provider,
43
42
  model: agentRoute.model,
44
- ...(config.agents?.[agentName]?.fallbacks?.length
45
- ? { fallbacks: config.agents[agentName].fallbacks.map(normalizeAgentRoute) }
46
- : {}),
43
+ ...(storedFallbacks?.length ? { fallbacks: storedFallbacks.map(normalizeAgentRoute) } : {}),
47
44
  effort: agentRoute.effort,
48
45
  fast: agentRoute.fast === true,
49
46
  ...(agentRoute.autoEffort === false ? { autoEffort: false } : {}),
@@ -53,6 +53,13 @@ export const AGENT_TOOL = {
53
53
  file: { type: 'string', description: 'Path to task text; alternative to prompt/message for spawn/send.' },
54
54
  cwd: { type: 'string', description: 'Project directory for a new or respawned agent.' },
55
55
  context: { type: 'string', description: 'Extra context for the spawn/send turn.' },
56
+ attachments: {
57
+ type: 'array',
58
+ items: { type: 'string' },
59
+ maxItems: 8,
60
+ description:
61
+ "spawn/send: files to attach (images, PDF/Office, text): a conversation attachment's file name or [attachment:id], or a path inside your folders. Produced files come back as paths; forward them by path.",
62
+ },
56
63
  model: {
57
64
  type: 'string',
58
65
  description:
@@ -23,13 +23,14 @@ import {
23
23
  setEndpointEnabled as dbSetEndpointEnabled,
24
24
  } from '../../runtime/channels/lib/webhooks-db.mjs';
25
25
  import { normalizeAutomationAttachments } from '../../runtime/shared/automation-attachments.mjs';
26
- import { migrateCodexModelRef } from '../../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs';
26
+ import { assertRoutineOwner, routineOwnerProblem } from '../routine-owner.mjs';
27
27
  import { hasEnabledAutomation } from '../../runtime/shared/automation-presence.mjs';
28
28
  import { resolveScheduleTimezone, validateScheduleCron } from '../../runtime/shared/schedule-time.mjs';
29
29
  import { pgClusterExists } from '../../runtime/memory/lib/pg/adapter.mjs';
30
30
  import { resolvePluginData } from '../../runtime/shared/plugin-paths.mjs';
31
31
  import { readHookPublicBase } from '../../runtime/channels/lib/webhook/relay-tunnel.mjs';
32
32
  import { WEBHOOK_TEST_FIELD, WEBHOOK_TEST_HEADER } from '../../runtime/channels/lib/webhook/request-handler.mjs';
33
+ import { MIXY_AGENT_ID, agentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
33
34
 
34
35
  const NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
35
36
 
@@ -187,7 +188,7 @@ function parseAtDatetime(at) {
187
188
  // Map the store's def shape onto the flat display shape every schedule reader
188
189
  // (channelSetup, renderChannelStatus, TUI pickers) consumes: `.time` renders
189
190
  // the cron or the one-shot datetime, `.route` the channel/session target.
190
- function scheduleToDisplay(s) {
191
+ function scheduleToDisplay(s, ownerProblem = '') {
191
192
  return {
192
193
  name: s.name,
193
194
  description: s.description || '',
@@ -195,26 +196,85 @@ function scheduleToDisplay(s) {
195
196
  whenAt: s.whenAt || undefined,
196
197
  whenCron: s.whenCron || undefined,
197
198
  timezone: s.timezone || undefined,
198
- // Shown (and re-saved by the editor) on the catalog successor of a superseded model.
199
- model: s.model ? migrateCodexModelRef(s.model) : undefined,
200
199
  cwd: s.cwd || undefined,
201
- workflow: s.workflow || undefined,
202
200
  attachments: s.attachments || undefined,
203
201
  enabled: s.enabled !== false,
204
202
  instructions: s.prompt,
205
203
  route: 'session',
204
+ // The bot it belongs to: each run is a turn in that bot's chat.
205
+ owner: s.ownerAgentId || MIXY_AGENT_ID,
206
+ // Why it is paused for its owner (routine-owner.mjs); absent when it runs.
207
+ ...(ownerProblem ? { ownerProblem } : {}),
208
+ nextFireAt: isoOrUndefined(s.nextFireAt),
209
+ lastSuccessAt: isoOrUndefined(s.lastSuccessAt),
210
+ lastFailedAt: isoOrUndefined(s.lastFailedAt),
211
+ lastSkippedAt: isoOrUndefined(s.lastSkippedAt),
212
+ ...routineHealth(s),
213
+ };
214
+ }
215
+
216
+ // Failures in a row (the routine pauses itself at the limit) and the results
217
+ // of finished runs still not queued for the owner: `{ count, failed, error }`,
218
+ // `failed` when the queue refused one (the app offers a retry).
219
+ function routineHealth(row) {
220
+ const entries = Object.values(row.resultDelivery || {}).filter(Boolean);
221
+ const refused = entries.filter((entry) => entry.state === 'failed').at(-1);
222
+ return {
223
+ consecutiveFailures: Number(row.consecutiveFailures) || 0,
224
+ ...(entries.length
225
+ ? {
226
+ undelivered: {
227
+ count: entries.length,
228
+ failed: Boolean(refused),
229
+ ...(refused?.error ? { error: String(refused.error) } : {}),
230
+ },
231
+ }
232
+ : {}),
233
+ };
234
+ }
235
+
236
+ function isoOrUndefined(value) {
237
+ if (!value) return undefined;
238
+ const at = new Date(value);
239
+ return Number.isNaN(at.getTime()) ? undefined : at.toISOString();
240
+ }
241
+
242
+ // A schedule's or webhook's owner: an explicit agent id, else the saved owner
243
+ // on an overwrite, else Mixy.
244
+ function scheduleOwner(owner, existing) {
245
+ const value = String(owner ?? '').trim();
246
+ if (value) {
247
+ if (!agentHomeSessionId(value)) throw new Error(`schedule owner "${value}" is not a valid agent id`);
248
+ return value;
249
+ }
250
+ return existing?.ownerAgentId || MIXY_AGENT_ID;
251
+ }
252
+
253
+ /** Each owner's problem (routine-owner.mjs), probed once per owner. */
254
+ function ownerProblems() {
255
+ const seen = new Map();
256
+ return (ownerAgentId) => {
257
+ const owner = ownerAgentId || MIXY_AGENT_ID;
258
+ if (!seen.has(owner))
259
+ seen.set(
260
+ owner,
261
+ routineOwnerProblem(owner).catch(() => '')
262
+ );
263
+ return seen.get(owner);
206
264
  };
207
265
  }
208
266
 
209
267
  export async function listSchedules() {
210
268
  const rows = await dbListSchedules();
211
- return rows.map(scheduleToDisplay);
269
+ const problemOf = ownerProblems();
270
+ return Promise.all(rows.map(async (row) => scheduleToDisplay(row, await problemOf(row.ownerAgentId))));
212
271
  }
213
272
 
214
273
  // Register or update a schedule in the PG store. Recurring input maps `time`
215
274
  // (+ optional `days`) to a cron; one-shot input maps an `at` datetime; the two
216
275
  // are mutually exclusive (also enforced by the store's when_at/when_cron XOR).
217
- // Every schedule runs as a visible app session and requires a model.
276
+ // Every schedule runs as a turn in its owner bot's chat; a bot that does not
277
+ // exist (no representative bot yet, an archived bot) cannot own one.
218
278
  export async function saveSchedule({
219
279
  name,
220
280
  description = '',
@@ -222,26 +282,26 @@ export async function saveSchedule({
222
282
  at,
223
283
  timezone,
224
284
  days,
225
- model,
226
285
  cwd,
227
- workflow,
228
286
  attachments,
229
287
  enabled,
230
288
  instructions,
289
+ owner,
231
290
  overwrite = false,
232
291
  } = {}) {
233
292
  const id = assertScheduleName(name);
234
293
  const body = String(instructions || '').trim();
235
294
  if (!body) throw new Error('schedule instructions are required');
236
- const scheduleModel = String(model || '').trim();
237
- if (!scheduleModel) throw new Error('schedule model is required — choose a model before saving');
238
295
  const hasTime = time != null && String(time).trim() !== '';
239
296
  const hasAt = at != null && String(at).trim() !== '';
240
297
  if (hasTime && hasAt) throw new Error('provide either `time` (recurring) or `at` (one-shot), not both');
241
298
  if (!hasTime && !hasAt) throw new Error('either `time` (recurring cron) or `at` (one-shot datetime) is required');
242
- if (overwrite !== true && (await dbGetSchedule(id))) {
299
+ const existing = await dbGetSchedule(id);
300
+ if (overwrite !== true && existing) {
243
301
  throw new Error(`schedule "${id}" already exists`);
244
302
  }
303
+ const ownerAgentId = scheduleOwner(owner, existing);
304
+ await assertRoutineOwner(ownerAgentId);
245
305
  const whenCron = hasTime ? validateScheduleCron(foldDaysIntoCron(normalizeCron(time), days)) : null;
246
306
  const whenAt = hasAt ? parseAtDatetime(at) : null;
247
307
  const scheduleTimezone = hasTime ? resolveScheduleTimezone(timezone) : null;
@@ -253,14 +313,13 @@ export async function saveSchedule({
253
313
  timezone: scheduleTimezone,
254
314
  target: 'session',
255
315
  channelId: null,
256
- model: scheduleModel,
257
316
  cwd: cwd ? String(cwd).trim() : null,
258
- workflow: workflow ? String(workflow).trim() : null,
259
317
  attachments: normalizeAutomationAttachments(attachments),
260
318
  delivery: 'app',
261
319
  prompt: body,
262
320
  enabled: enabled !== false,
263
321
  nextFireAt: whenAt,
322
+ ownerAgentId,
264
323
  });
265
324
  automationProbe.invalidate();
266
325
  return scheduleToDisplay(saved);
@@ -287,13 +346,13 @@ export async function setScheduleEnabled(name, enabled) {
287
346
  // store's migration hook.
288
347
  async function listWebhooks() {
289
348
  const endpoints = await dbListEndpoints();
290
- return endpoints.map((ep) => ({
349
+ const problemOf = ownerProblems();
350
+ const problems = await Promise.all(endpoints.map((ep) => problemOf(ep.ownerAgentId)));
351
+ return endpoints.map((ep, index) => ({
291
352
  name: ep.name,
292
353
  description: ep.description || '',
293
354
  parser: ep.parser || 'github',
294
- ...(ep.model ? { model: migrateCodexModelRef(ep.model) } : {}),
295
355
  ...(ep.cwd ? { cwd: ep.cwd } : {}),
296
- ...(ep.workflow ? { workflow: ep.workflow } : {}),
297
356
  ...(ep.attachments ? { attachments: ep.attachments } : {}),
298
357
  enabled: ep.enabled,
299
358
  // The store never projects the plaintext secret through list paths; it
@@ -302,6 +361,16 @@ async function listWebhooks() {
302
361
  secret: undefined,
303
362
  instructions: ep.instructions,
304
363
  route: 'session',
364
+ // The bot it belongs to: each run is a turn in that bot's chat.
365
+ owner: ep.ownerAgentId || MIXY_AGENT_ID,
366
+ ...(problems[index] ? { ownerProblem: problems[index] } : {}),
367
+ lastEventAt: isoOrUndefined(ep.lastEventAt),
368
+ lastSuccessAt: isoOrUndefined(ep.lastSuccessAt),
369
+ lastFailedAt: isoOrUndefined(ep.lastFailedAt),
370
+ lastSkippedAt: isoOrUndefined(ep.lastSkippedAt),
371
+ // Whether "Run now" has a received payload to re-run (never the payload).
372
+ hasLastPayload: ep.hasLastPayload === true,
373
+ ...routineHealth(ep),
305
374
  }));
306
375
  }
307
376
 
@@ -310,12 +379,11 @@ export async function saveWebhook({
310
379
  description = '',
311
380
  parser = 'github',
312
381
  secret,
313
- model,
314
382
  cwd,
315
- workflow,
316
383
  attachments,
317
384
  enabled,
318
385
  instructions,
386
+ owner,
319
387
  overwrite = false,
320
388
  } = {}) {
321
389
  const id = assertName(name, 'webhook name');
@@ -327,9 +395,12 @@ export async function saveWebhook({
327
395
  }
328
396
  const body = String(instructions || '').trim();
329
397
  if (!body) throw new Error('webhook instructions are required');
330
- if (overwrite !== true && (await dbLoadEndpoint(id))) {
398
+ const existing = await dbLoadEndpoint(id);
399
+ if (overwrite !== true && existing) {
331
400
  throw new Error(`webhook "${id}" already exists`);
332
401
  }
402
+ const ownerAgentId = scheduleOwner(owner, existing);
403
+ await assertRoutineOwner(ownerAgentId);
333
404
  // Secret semantics: an explicit value always wins; an EMPTY value on an
334
405
  // overwrite PRESERVES the stored secret (editing instructions must not
335
406
  // silently rotate the key the external service was configured with); only
@@ -343,23 +414,21 @@ export async function saveWebhook({
343
414
  description: String(description || '').trim(),
344
415
  parser: nextParser,
345
416
  channelId: null,
346
- model: model ? String(model).trim() : null,
347
417
  cwd: cwd ? String(cwd).trim() : null,
348
- workflow: workflow ? String(workflow).trim() : null,
349
418
  attachments: normalizeAutomationAttachments(attachments),
350
419
  delivery: 'app',
351
420
  secret: secretValue,
352
421
  instructions: body,
353
422
  enabled: enabled !== false,
423
+ ownerAgentId,
354
424
  });
355
425
  automationProbe.invalidate();
356
426
  return {
357
427
  name: id,
428
+ owner: saved.ownerAgentId || ownerAgentId,
358
429
  description: saved.description,
359
430
  parser: saved.parser,
360
- ...(saved.model ? { model: saved.model } : {}),
361
431
  ...(saved.cwd ? { cwd: saved.cwd } : {}),
362
- ...(saved.workflow ? { workflow: saved.workflow } : {}),
363
432
  ...(saved.attachments ? { attachments: saved.attachments } : {}),
364
433
  ...(enabled === false ? { enabled: false } : {}),
365
434
  secret: secretValue,
@@ -0,0 +1,37 @@
1
+ // Whose memory a session reads and writes (runtime/memory/lib/
2
+ // core-memory-access.mjs enforces it). Mixy is the Lead configuration, so the
3
+ // user's own sessions (an ordinary Lead session, Mixy's home) act as Mixy; an
4
+ // agent's home thread and its room member sessions act as that agent; a
5
+ // schedule or webhook run acts for its owning agent (`ownerAgentId`, stamped
6
+ // at creation); a delegated agent as its own agent id.
7
+ import { MIXY_AGENT_ID, agentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
8
+ import { resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
9
+ import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
10
+ import { scheduleCallerScope } from './schedule-access.mjs';
11
+
12
+ const validAgentId = (value) => {
13
+ const id = typeof value === 'string' ? value.trim() : '';
14
+ return id && agentHomeSessionId(id) ? id : '';
15
+ };
16
+
17
+ /** The `memory_caller` `{ kind, agentId }` of a calling session record:
18
+ * 'human' and 'agent' are conversations with the user, 'room-member' and
19
+ * 'headless' unattended runs. */
20
+ export function memoryCallerScope(session) {
21
+ const scope = scheduleCallerScope(session);
22
+ if (scope.kind === 'human') return { kind: 'human', agentId: MIXY_AGENT_ID };
23
+ if (scope.kind !== 'headless') return scope;
24
+ const delegated = isAgentOwner(session) || Boolean(session?.parentSessionId);
25
+ return {
26
+ kind: 'headless',
27
+ agentId: validAgentId(session?.ownerAgentId) || (delegated ? validAgentId(session?.agent) : ''),
28
+ };
29
+ }
30
+
31
+ /** The agent whose own memory a session's prompt carries: its home thread's,
32
+ * room member's or run owner's agent, Mixy for the user's Lead sessions, and
33
+ * none for a delegated agent. `session` is the live record when there is
34
+ * one, else the reserved id and creation profile. */
35
+ export function injectedMemoryAgentId({ session = null, sessionId = '', profile = null } = {}) {
36
+ return resolveSessionMode({ session, sessionId, profile }).agentId;
37
+ }