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
@@ -367,6 +367,7 @@ function oauthAuthFields(p, auth) {
367
367
  status: auth.status || (authenticated ? 'Set' : 'Not Set'),
368
368
  detail: auth.detail || p.desc,
369
369
  expiresAt: auth.expiresAt || null,
370
+ plan: auth.plan || null,
370
371
  usable: auth.usable === true || (auth.usable == null && authenticated),
371
372
  refreshable: auth.refreshable === true,
372
373
  reauthRequired: auth.reauthRequired === true,
@@ -958,8 +959,8 @@ async function withTimeout(run, ms, label) {
958
959
  }
959
960
  }
960
961
 
961
- function existingCustomEntry(cfgMod, id) {
962
- const entry = cfgMod.loadConfig().providers?.[id];
962
+ function existingCustomEntry(config, id) {
963
+ const entry = config.providers?.[id];
963
964
  if (!isCustomProviderId(id) || !isCustomProviderConfig(entry)) throw new Error(`unknown custom provider "${id}"`);
964
965
  return entry;
965
966
  }
@@ -1021,16 +1022,15 @@ function withStoredSettings(input, entry) {
1021
1022
  return out;
1022
1023
  }
1023
1024
 
1024
- // Validated, secret-free config for `input`; the key (typed, else stored for an
1025
- // existing provider) is resolved separately and never enters the config.
1026
- async function resolveCustomInput(cfgMod, rawInput, options) {
1027
- let input = inputObject(rawInput);
1028
- validateCustomTransportFields(input);
1025
+ // Validated, secret-free config for `input` over one config snapshot (the
1026
+ // stored entry supplies an edit's omitted settings); the key (typed, else
1027
+ // stored for an existing provider) is resolved separately and never enters
1028
+ // the config. Synchronous, so a caller can merge and commit one snapshot.
1029
+ function customInputAt(snapshot, input, normalizeCustomProviderConfig, options) {
1029
1030
  const id = input.id === undefined ? null : input.id;
1030
- if (id) input = withStoredSettings(input, existingCustomEntry(cfgMod, id));
1031
- const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
1032
- const config = normalizeCustomProviderConfig(input, options);
1033
- const typed = typeof input.apiKey === 'string' ? input.apiKey.trim() : '';
1031
+ const merged = id ? withStoredSettings(input, existingCustomEntry(snapshot, id)) : input;
1032
+ const config = normalizeCustomProviderConfig(merged, options);
1033
+ const typed = typeof merged.apiKey === 'string' ? merged.apiKey.trim() : '';
1034
1034
  const keyless = config.auth === 'none';
1035
1035
  const envName = keyless ? '' : config.apiKeyEnv || '';
1036
1036
  let apiKey = '';
@@ -1039,36 +1039,71 @@ async function resolveCustomInput(cfgMod, rawInput, options) {
1039
1039
  return { id, config, typed: keyless || envName ? '' : typed, apiKey, keyless, envName };
1040
1040
  }
1041
1041
 
1042
+ async function resolveCustomInput(cfgMod, rawInput, options) {
1043
+ const input = inputObject(rawInput);
1044
+ validateCustomTransportFields(input);
1045
+ const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
1046
+ return customInputAt(cfgMod.loadConfig(), input, normalizeCustomProviderConfig, options);
1047
+ }
1048
+
1049
+ function requireCustomKey({ apiKey, keyless, envName }) {
1050
+ if (!keyless && !envName && !apiKey) throw new Error('API key is required');
1051
+ }
1052
+
1053
+ // One key transaction per provider, from reading the rollback key to the
1054
+ // config commit or rollback: a failed save restores the key the preceding
1055
+ // save committed and never one a newer save wrote. Settings-only saves and
1056
+ // other providers never wait.
1057
+ const customKeySaves = new Map();
1058
+
1059
+ function serializeCustomKeySave(id, run) {
1060
+ const result = (customKeySaves.get(id) || Promise.resolve()).then(run);
1061
+ const settled = result.then(
1062
+ () => {},
1063
+ () => {}
1064
+ );
1065
+ customKeySaves.set(id, settled);
1066
+ settled.then(() => {
1067
+ if (customKeySaves.get(id) === settled) customKeySaves.delete(id);
1068
+ });
1069
+ return result;
1070
+ }
1071
+
1042
1072
  export async function saveCustomProvider(cfgMod, rawInput) {
1043
1073
  requireLocalProviderManagement();
1044
- const {
1045
- id: existingId,
1046
- config,
1047
- typed,
1048
- apiKey,
1049
- keyless,
1050
- envName,
1051
- } = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
1052
- if (!keyless && !envName && !apiKey) throw new Error('API key is required');
1074
+ // Early verdict: an invalid save never touches the keychain.
1075
+ const resolved = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
1076
+ requireCustomKey(resolved);
1077
+ const { id: existingId, typed } = resolved;
1053
1078
  const id = existingId || `custom-${randomUUID()}`;
1054
- const entry = { ...config, type: 'custom', enabled: config.enabled !== false };
1055
- const account = SECRET_ACCOUNTS.agentApiKey(id);
1056
- const previousKey = typed && existingId ? getAgentApiKey(id) : null;
1057
- if (typed) await saveSecretAsync(account, typed);
1058
- try {
1059
- const current = cfgMod.loadConfig();
1060
- cfgMod.saveConfig(
1061
- { ...current, providers: { ...(current.providers || {}), [id]: entry } },
1062
- { baseConfig: current }
1063
- );
1064
- } catch (error) {
1065
- if (typed) {
1066
- if (previousKey) await saveSecretAsync(account, previousKey);
1067
- else await deleteSecretAsync(account);
1079
+ const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
1080
+ const commit = async () => {
1081
+ const account = SECRET_ACCOUNTS.agentApiKey(id);
1082
+ const previousKey = typed && existingId ? getAgentApiKey(id) : null;
1083
+ if (typed) await saveSecretAsync(account, typed);
1084
+ let entry;
1085
+ try {
1086
+ // Synchronous from here: omitted settings come from the config this
1087
+ // save commits over, so an edit that landed during the awaits above (a
1088
+ // model's visibility, a peer save, a removal) is never reverted.
1089
+ const current = cfgMod.loadConfig();
1090
+ const latest = customInputAt(current, rawInput, normalizeCustomProviderConfig, { requireModels: false });
1091
+ requireCustomKey(latest);
1092
+ entry = { ...latest.config, type: 'custom', enabled: latest.config.enabled !== false };
1093
+ cfgMod.saveConfig(
1094
+ { ...current, providers: { ...(current.providers || {}), [id]: entry } },
1095
+ { baseConfig: current }
1096
+ );
1097
+ } catch (error) {
1098
+ if (typed) {
1099
+ if (previousKey) await saveSecretAsync(account, previousKey);
1100
+ else await deleteSecretAsync(account);
1101
+ }
1102
+ throw error;
1068
1103
  }
1069
- throw error;
1070
- }
1071
- return customProviderRow(id, entry);
1104
+ return customProviderRow(id, entry);
1105
+ };
1106
+ return typed ? serializeCustomKeySave(id, commit) : commit();
1072
1107
  }
1073
1108
 
1074
1109
  const MODEL_PATCH_KEYS = new Set(['manual', 'hidden', 'name', 'contextWindow', 'maxOutputTokens', 'reasoning']);
@@ -1104,7 +1139,7 @@ export async function updateCustomProviderModel(cfgMod, rawId, rawModelId, patch
1104
1139
  // Everything below is synchronous: the provider is read, patched and saved
1105
1140
  // without yielding, so a concurrent update can never be overwritten by a
1106
1141
  // provider object read before the await above.
1107
- const entry = existingCustomEntry(cfgMod, id);
1142
+ const entry = existingCustomEntry(cfgMod.loadConfig(), id);
1108
1143
  const current = normalizeCustomProviderConfig(entry, { requireModels: false });
1109
1144
  const overrides = new Map(Object.entries(current.modelOverrides || {}));
1110
1145
  if (patch === null) {
@@ -1241,7 +1276,7 @@ function withoutProviderReferences(config, id) {
1241
1276
  export async function removeCustomProvider(cfgMod, rawId) {
1242
1277
  requireLocalProviderManagement();
1243
1278
  const id = String(rawId || '').trim();
1244
- existingCustomEntry(cfgMod, id);
1279
+ existingCustomEntry(cfgMod.loadConfig(), id);
1245
1280
  // Secret first: a real keychain failure (locked, permission) leaves the
1246
1281
  // provider registered so the removal can be retried. A missing secret
1247
1282
  // (keyless provider) is not an error.
@@ -0,0 +1,91 @@
1
+ // Who may manage automations (schedules and webhooks) from a chat, by the
2
+ // calling session's provenance. Enforced by every setup automation action
3
+ // (setup-tool/extended-actions.mjs), the chat-facing door of channel-admin:
4
+ // - the human's own sessions (an ordinary Lead session, Mixy's home) manage
5
+ // every automation and may move one to an agent that exists;
6
+ // - another agent's home thread manages only the automations it owns and can
7
+ // never give one away;
8
+ // - a headless run (schedule, webhook, delegated agent, or a session that
9
+ // cannot be resolved) manages none — nor does it change any other setting
10
+ // (setup-tool/executor.mjs refuses every mutating setup action for it).
11
+ // - a group-room member session is no background run, but it speaks for the
12
+ // room, not for the user: it creates or changes no agent, automation or
13
+ // other setting either.
14
+ // The app's own panels call channel-admin directly and act for the human.
15
+ import { MIXY_AGENT_ID, agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
16
+ import { roomMemberOfSession } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
17
+ import { mixyDisplayName } from '../../runtime/agent/orchestrator/session/mixy-name.mjs';
18
+ import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
19
+
20
+ const INTERACTIVE_SOURCE_TYPES = new Set(['', 'lead', 'cli']);
21
+
22
+ /** `{ kind: 'human' | 'agent' | 'room-member' | 'headless', agentId? }` of a session record. */
23
+ export function scheduleCallerScope(session) {
24
+ const id = String(session?.id || '');
25
+ const member = roomMemberOfSession(id);
26
+ if (member) return { kind: 'room-member', agentId: member.agentId };
27
+ const homeAgent = agentIdOfHomeSession(id);
28
+ if (homeAgent) return homeAgent === MIXY_AGENT_ID ? { kind: 'human' } : { kind: 'agent', agentId: homeAgent };
29
+ const headless =
30
+ !id ||
31
+ isAgentOwner(session) ||
32
+ Boolean(session.parentSessionId) ||
33
+ Boolean(session.ownerSessionId && session.ownerSessionId !== id) ||
34
+ !INTERACTIVE_SOURCE_TYPES.has(String(session.sourceType || '').toLowerCase());
35
+ return headless ? { kind: 'headless' } : { kind: 'human' };
36
+ }
37
+
38
+ /** Throws unless `scope` may `action` the `kind` ('schedule' | 'webhook')
39
+ * automation `current` (null when creating) with `owner` the requested owner
40
+ * ('' keeps the saved one). `agentIds` are the agents a transfer may name;
41
+ * Mixy always counts. */
42
+ export function assertAutomationAccess(
43
+ scope,
44
+ { kind = 'schedule', action, current = null, owner = '', agentIds = [] }
45
+ ) {
46
+ if (scope?.kind === 'room-member') throw new Error(`A group-room member cannot ${action} ${kind}s`);
47
+ if (scope?.kind !== 'human' && scope?.kind !== 'agent') {
48
+ throw new Error(`A background run cannot ${action} ${kind}s`);
49
+ }
50
+ const requested = String(owner ?? '').trim();
51
+ const saved = current ? current.owner || MIXY_AGENT_ID : '';
52
+ if (scope.kind === 'agent') {
53
+ if (current && saved !== scope.agentId) {
54
+ throw new Error(
55
+ `${kind === 'webhook' ? 'Webhook' : 'Schedule'} "${current.name}" belongs to another agent; this thread manages only its own ${kind}s`
56
+ );
57
+ }
58
+ if (requested && requested !== scope.agentId) throw new Error(`This thread cannot give a ${kind} to another agent`);
59
+ return;
60
+ }
61
+ if (requested && requested !== saved && requested !== MIXY_AGENT_ID && !agentIds.includes(requested)) {
62
+ throw new Error(`Agent "${requested}" does not exist`);
63
+ }
64
+ }
65
+
66
+ /** Throws unless `caller` (scope `scope`) is Mixy — its home thread or an
67
+ * ordinary Lead session acting as 'lead': only Mixy creates, edits or
68
+ * deletes agent definitions from a chat. The app's own panels call the agent
69
+ * editor directly and act for the human. */
70
+ export function assertAgentEditorAccess(scope, caller, action) {
71
+ const acting = String(caller?.agent || '').trim();
72
+ if (scope?.kind === 'human' && (!acting || acting === MIXY_AGENT_ID)) return;
73
+ const name = mixyDisplayName();
74
+ throw new Error(
75
+ `Only ${name} (the representative agent) can create, edit or delete agents (setup ${action}); ask the user to do it with ${name} or in the Agents settings`
76
+ );
77
+ }
78
+
79
+ /** Throws when an agent's home thread changes another agent's settings. */
80
+ export function assertOwnAgentSettings(scope, agentId, action) {
81
+ if (scope?.kind === 'agent' && String(agentId || '').trim() !== scope.agentId) {
82
+ throw new Error(`This thread manages only its own agent's settings (setup ${action})`);
83
+ }
84
+ }
85
+
86
+ /** Throws when `scope` is a headless run: it reads settings, never changes them. */
87
+ export function assertInteractiveCaller(scope, action) {
88
+ if (scope?.kind === 'human' || scope?.kind === 'agent') return;
89
+ if (scope?.kind === 'room-member') throw new Error(`A group-room member cannot change settings (setup ${action})`);
90
+ throw new Error(`A background run cannot change settings (setup ${action})`);
91
+ }
@@ -2,11 +2,14 @@
2
2
  // the current runtime state: route, tool spec, ownership/lane, workflow pack,
3
3
  // memory context and compaction.
4
4
  import { normalizeCompactionConfig } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
5
- import { hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
5
+ import { clean, hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
6
6
  import { toolSpecForMode } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
7
- import { pluginDataDir } from '../runtime-paths.mjs';
7
+ import { STANDALONE_ROOT, pluginDataDir } from '../runtime-paths.mjs';
8
+ import { readAgentFrontmatterPermission } from '../services/agent-tool/helpers.mjs';
8
9
  import { LEAD_DISALLOWED_TOOLS } from '../../runtime/agent/orchestrator/runtime-core/tool-defs.mjs';
9
10
  import { HEADLESS_TOOL_PROFILE } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
11
+ import { AGENT_MODE, resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
12
+ import { MIXY_AGENT_ID } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
10
13
 
11
14
  // Ownership and permission fields an agent-owned session inherits from its profile.
12
15
  function agentOwnedSessionFields(sessionProfile) {
@@ -31,14 +34,30 @@ export function sessionOptions(deps, coreMemoryContext) {
31
34
  // Load the active WORKFLOW.md pack once for both summary + context block.
32
35
  const {
33
36
  summary: workflow,
34
- context: leadWorkflowContext,
37
+ leadPromptParts,
35
38
  packContext,
36
39
  orchestrationMode,
37
40
  } = activeWorkflowContext(rt.config, dataDir);
38
41
  const sessionProfile = rt.sessionProfile && typeof rt.sessionProfile === 'object' ? rt.sessionProfile : null;
39
42
  const agentOwned = sessionProfile?.owner === 'agent' || sessionProfile?.visibility === 'agent-only';
40
- // Agent-owned sessions keep the Active Workflow block only.
41
- const workflowContext = agentOwned ? packContext : leadWorkflowContext;
43
+ // Agent or coding mode, from the one resolver (agent-mode.mjs).
44
+ const { mode: sessionMode, agentId: actingAgent } = resolveSessionMode({
45
+ sessionId: rt.reservedSessionId,
46
+ profile: sessionProfile,
47
+ });
48
+ // An agent's home thread: a user conversation that runs AS that agent (its
49
+ // AGENT.md role and permission); route comes with the create call.
50
+ const homeAgent = agentOwned ? '' : clean(sessionProfile?.homeAgent);
51
+ const homePermission = homeAgent ? readAgentFrontmatterPermission(homeAgent, dataDir, STANDALONE_ROOT) : null;
52
+ // A coding task session an agent created (session create): the user's own
53
+ // Lead, tagged with its origin; `sourceName` lets the Agents list file it.
54
+ const taskOrigin = agentOwned ? null : sessionProfile?.taskOrigin || null;
55
+ // Every session leads BP3 with the Active Workflow block; a Lead adds its
56
+ // orchestration and catalog slices (coding, agent mode or Mixy's).
57
+ const workflowContext = packContext;
58
+ const leadWorkflowParts = agentOwned
59
+ ? undefined
60
+ : leadPromptParts({ agentMode: sessionMode === AGENT_MODE, mixy: actingAgent === MIXY_AGENT_ID });
42
61
  const callerDisallowedTools = [
43
62
  ...(agentOwned ? [] : LEAD_DISALLOWED_TOOLS),
44
63
  ...(!agentOwned && rt.disallowDelegation ? ['agent'] : []),
@@ -51,7 +70,8 @@ export function sessionOptions(deps, coreMemoryContext) {
51
70
  tools: toolSpecForMode(rt.mode),
52
71
  ...(Array.isArray(schemaAllowedTools) ? { schemaAllowedTools } : {}),
53
72
  owner: agentOwned ? 'agent' : 'cli',
54
- agent: agentOwned ? sessionProfile?.agent || 'worker' : 'lead',
73
+ agent: agentOwned ? sessionProfile?.agent || 'worker' : homeAgent || 'lead',
74
+ ...(homePermission ? { permission: homePermission } : {}),
55
75
  lane: agentOwned ? 'agent' : 'cli',
56
76
  // One-shot `mixdog exec` runs carry their own source so session catalogs
57
77
  // can keep them out of the user's resumable list.
@@ -60,7 +80,12 @@ export function sessionOptions(deps, coreMemoryContext) {
60
80
  : rt.toolProfile === HEADLESS_TOOL_PROFILE
61
81
  ? 'exec'
62
82
  : 'lead',
63
- sourceName: agentOwned ? sessionProfile?.sourceName || sessionProfile?.agent || 'agent' : 'main',
83
+ sourceName: agentOwned
84
+ ? sessionProfile?.sourceName || sessionProfile?.agent || 'agent'
85
+ : taskOrigin
86
+ ? `agent:${taskOrigin.agentId}`
87
+ : 'main',
88
+ ...(taskOrigin ? { taskOrigin } : {}),
64
89
  ...(rt.approvalMode ? { approvalMode: rt.approvalMode } : {}),
65
90
  clientHostPid: sessionProfile?.clientHostPid || process.pid,
66
91
  mcpScopeId: rt.mcpScopeId,
@@ -71,6 +96,8 @@ export function sessionOptions(deps, coreMemoryContext) {
71
96
  coreMemoryContext,
72
97
  workflow,
73
98
  workflowContext,
99
+ ...(leadWorkflowParts ? { leadWorkflowParts } : {}),
100
+ sessionMode,
74
101
  orchestrationMode,
75
102
  fast: rt.route.fast === true,
76
103
  autoEffort: rt.route.autoEffort !== false,
@@ -5,6 +5,8 @@
5
5
  // facade's hookCommonPayload / currentCwd accessor + the session itself. All
6
6
  // properties are non-enumerable/configurable/writable so the loop/manager can
7
7
  // read them by name without changing session enumeration.
8
+ import { roomMemberOfSession } from '../runtime/agent/orchestrator/session/agent-room.mjs';
9
+ import { roomMemberToolDecision } from './room-member-gate.mjs';
8
10
 
9
11
  export function attachSessionHooks(session, { hooks, hookCommonPayload, getCwd }) {
10
12
  const identity = (input) => ({
@@ -26,20 +28,26 @@ export function attachSessionHooks(session, { hooks, hookCommonPayload, getCwd }
26
28
  // rejection cannot escape the tool loop or the compaction flow.
27
29
  const dispatcher = (event, payload) => (input) => hooks.dispatch(event, hookCommonPayload(payload(input)));
28
30
 
29
- // PreToolUse bridge.
30
- define('beforeToolHook', (input, options = {}) =>
31
- hooks.beforeTool(
31
+ // PreToolUse bridge. A group-room member's mutating calls always ask the
32
+ // user (room-member-gate.mjs), whatever the hooks decided short of a deny.
33
+ define('beforeToolHook', async (input, options = {}) => {
34
+ const name = input?.name || input?.tool_name;
35
+ const args = input?.args || input?.tool_input;
36
+ const cwd = input?.cwd || getCwd();
37
+ const decision = await hooks.beforeTool(
32
38
  hookCommonPayload({
33
39
  ...input,
34
40
  session_id: input?.sessionId || input?.session_id || session?.id,
35
- tool_name: input?.name || input?.tool_name,
36
- tool_input: input?.args || input?.tool_input,
41
+ tool_name: name,
42
+ tool_input: args,
37
43
  tool_use_id: input?.toolCallId || input?.tool_use_id,
38
- cwd: input?.cwd || getCwd(),
44
+ cwd,
39
45
  }),
40
46
  options
41
- )
42
- );
47
+ );
48
+ if (!roomMemberOfSession(session?.id)) return decision;
49
+ return roomMemberToolDecision({ name, args, cwd }, decision);
50
+ });
43
51
  // PostToolUse: bridge runtime tool completions to the standard hook bus.
44
52
  define('afterToolHook', dispatcher('PostToolUse', toolPayload));
45
53
  // PostToolUseFailure: dispatched by loop.mjs only when a tool execution
@@ -9,6 +9,12 @@ import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-
9
9
  import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
10
10
  import { SETUP_DESKTOP_DOMAINS, SETUP_HANDOFFS } from './settings-contract.mjs';
11
11
  import { executeExtendedSetupAction, publicAutomation, publicHookRule, validateMcpInput } from './extended-actions.mjs';
12
+ import {
13
+ assertAgentEditorAccess,
14
+ assertInteractiveCaller,
15
+ assertOwnAgentSettings,
16
+ scheduleCallerScope,
17
+ } from '../services/schedule-access.mjs';
12
18
  import { SETUP_DESKTOP_UNCLAIMED, createSetupUiRequests } from './ui-requests.mjs';
13
19
  import {
14
20
  SETUP_ACTIONS,
@@ -20,8 +26,11 @@ import {
20
26
  } from './tool-defs.mjs';
21
27
  import { schemaValueError } from '../../runtime/shared/schema-value-error.mjs';
22
28
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
29
+ import { CONNECTOR_IDS, connectorById } from '../../runtime/shared/connector-catalog.mjs';
23
30
  import profileConfig from '../../runtime/shared/profile-config.cjs';
24
31
 
32
+ const AGENT_DEFINITION_ACTIONS = new Set(['create_definition', 'save_definition', 'delete_definition']);
33
+
25
34
  const PROFILE_VALUE_IDS = Object.freeze({
26
35
  language: profileConfig.PROFILE_LANGUAGES.map((entry) => entry.id),
27
36
  experienceLevel: ['', ...profileConfig.PROFILE_EXPERIENCE_LEVELS.map((entry) => entry.id)],
@@ -35,6 +44,7 @@ const ACTION_APPLIES_TO = {
35
44
  save_automation: 'the automation worker; scheduled runs follow the saved timing',
36
45
  delete_automation: 'future automation runs',
37
46
  set_automation_enabled: 'future automation runs',
47
+ run_automation: "one run now; its result posts into the owning agent's thread",
38
48
  set_webhook_config: 'the webhook worker after configuration reload',
39
49
  add_hook_rule: 'subsequent tool calls; no restart required',
40
50
  set_hook_rule_enabled: 'subsequent tool calls; no restart required',
@@ -55,9 +65,10 @@ const OPEN_TARGET_HINTS = Object.freeze({
55
65
  autoclear: 'Desktop: Settings → Context · TUI: /autoclear',
56
66
  memory:
57
67
  'Desktop: Extensions → Plugin → Built-in (Memory); core memories under Projects → project → Memories · TUI: /memory',
58
- mcp: 'Desktop: Extensions → Skill tab → MCP · TUI: /mcp',
59
- skills: 'Desktop: Extensions → Skill tab → Skills · TUI: /skills',
60
- plugins: 'Desktop: Extensions → Plugin tab → Plugins · TUI: /plugins',
68
+ mcp: 'Desktop: Extensions → Connectors tab · TUI: /mcp',
69
+ skills: 'Desktop: Skills (activity rail) · TUI: /skills',
70
+ plugins:
71
+ 'Desktop: Extensions → Extensions tab (plugins that bring MCP servers: Connectors tab → Custom connectors) · TUI: /plugins',
61
72
  update: 'Desktop: Settings → System → Update · TUI: /update',
62
73
  usage: 'Desktop: /usage in the composer · TUI: /usage',
63
74
  doctor: 'Desktop: Settings → System → Doctor, or /doctor in the composer · TUI: /doctor',
@@ -133,6 +144,7 @@ function publicProviderRows(setup) {
133
144
  pick(row, {
134
145
  reauthRequired: row.reauthRequired === true,
135
146
  expiresAt: row.expiresAt || null,
147
+ plan: row.plan || null,
136
148
  })
137
149
  ),
138
150
  local: (setup?.local || []).map((row) =>
@@ -167,6 +179,7 @@ function configuredProviderRows(rows, accountCount) {
167
179
  kind,
168
180
  authenticated: row.authenticated,
169
181
  status: row.status,
182
+ ...(kind === 'oauth' ? { plan: row.plan || null } : {}),
170
183
  ...(row.reauthRequired ? { reauthRequired: true } : {}),
171
184
  ...accounts,
172
185
  });
@@ -364,6 +377,17 @@ const SETUP_STATUS_READERS = {
364
377
  // schema, the per-action field list and the route/object-argument minimums.
365
378
  function validateSetupInput(args) {
366
379
  const action = requireEnum(args?.action, SETUP_ACTIONS, 'action');
380
+ // Old callers still ask to connect GitHub: it is the signed-in GitHub CLI, not a catalog connector.
381
+ if (
382
+ action === 'connect_connector' &&
383
+ String(args.connector ?? '')
384
+ .trim()
385
+ .toLowerCase() === 'github'
386
+ ) {
387
+ throw new Error(
388
+ 'GitHub uses the signed-in GitHub CLI; no connector needed. Use the github tool or Settings → Git.'
389
+ );
390
+ }
367
391
  const validationError = schemaValueError({ ...args, action }, SETUP_TOOL_DEFS[0].inputSchema, 'setup');
368
392
  if (validationError) throw new Error(`[tool-input-validation] ${validationError}`);
369
393
  const fields = SETUP_ACTION_FIELDS[action].split(' ').filter(Boolean);
@@ -627,6 +651,25 @@ const SETUP_ACTION_HANDLERS = {
627
651
  remove_plugin: async (rt, args) => ({
628
652
  plugin: (await rt.removePlugin(requireText(args.name, 'name')))?.plugin || null,
629
653
  }),
654
+ // The chat path of the one-click connect: the attached Desktop renders this
655
+ // call as a connect card (agent-chat connector card) that runs the same flow
656
+ // as Extensions → 커넥터. Nothing is connected here.
657
+ connect_connector: async (rt, args) => {
658
+ const entry = connectorById(requireText(args.connector, 'connector'));
659
+ if (!entry || entry.ownKey) throw new Error(`connector must be one of ${CONNECTOR_IDS.join(', ')}`);
660
+ const server = (rt.mcpStatus?.()?.servers || []).find((row) => row.connector === entry.id) || null;
661
+ const connected = server?.connected === true;
662
+ return {
663
+ connector: entry.id,
664
+ name: entry.name,
665
+ backend: entry.backend.kind,
666
+ ...(server ? { server: server.name } : {}),
667
+ connected,
668
+ note: connected
669
+ ? `${entry.name} is already connected.`
670
+ : `A connect card for ${entry.name} is shown in this conversation; the user presses it (Desktop: Extensions → 커넥터). Check status mcp afterwards.`,
671
+ };
672
+ },
630
673
  enable_plugin_mcp: async (rt, args) => {
631
674
  const name = requireText(args.name, 'name');
632
675
  const plugin = ((await rt.pluginsStatus())?.plugins || []).find((row) => row.name === name || row.id === name);
@@ -645,6 +688,7 @@ const READ_ONLY_SETUP_ACTIONS = new Set([
645
688
  'open',
646
689
  'list_models',
647
690
  'get_mcp_server',
691
+ 'connect_connector',
648
692
  'read_definition',
649
693
  'search_local_models',
650
694
  'inspect_hf_model',
@@ -656,6 +700,7 @@ const BACKGROUND_SETUP_ACTIONS = new Set([
656
700
  'maintain_local_model',
657
701
  'cancel_local_installation',
658
702
  'reconnect_mcp',
703
+ 'run_automation',
659
704
  ]);
660
705
 
661
706
  function setupMutationReceipt(action, result) {
@@ -671,7 +716,16 @@ function setupMutationReceipt(action, result) {
671
716
  );
672
717
  }
673
718
 
674
- export function createSetupToolExecutor({ getApi, getConfig, notifySessionUi, getSessionId, flushSettings }) {
719
+ /** `getCallerSession(id)` returns the calling session's record (null when it
720
+ * cannot be resolved): automation actions authorize by its provenance. */
721
+ export function createSetupToolExecutor({
722
+ getApi,
723
+ getConfig,
724
+ notifySessionUi,
725
+ getSessionId,
726
+ getCallerSession,
727
+ flushSettings,
728
+ }) {
675
729
  const desktop = createSetupUiRequests({ notifySessionUi, getSessionId });
676
730
  const api = () => {
677
731
  const facade = getApi?.();
@@ -701,12 +755,31 @@ export function createSetupToolExecutor({ getApi, getConfig, notifySessionUi, ge
701
755
  };
702
756
  }
703
757
 
704
- async function execute(args, { signal }) {
758
+ async function execute(args, { signal, callerSessionId }) {
705
759
  signal?.throwIfAborted();
706
760
  const requestDesktop = (request) => desktop.request(request, { signal });
707
761
  const action = validateSetupInput(args);
762
+ // The session that made this call, not just the runtime's own Lead.
763
+ const sessionId = clean(callerSessionId) || clean(getSessionId?.());
764
+ const caller = (sessionId && getCallerSession?.(sessionId)) || null;
765
+ // A headless run (schedule, webhook, delegated agent) only reads settings:
766
+ // it never creates agents, definitions or automations, nor changes any
767
+ // other setting. Hosts without session provenance (no getCallerSession)
768
+ // skip this gate; automation actions still refuse an unknown caller.
769
+ if (getCallerSession && !READ_ONLY_SETUP_ACTIONS.has(action)) {
770
+ const scope = scheduleCallerScope(caller);
771
+ assertInteractiveCaller(scope, action);
772
+ // Agent definitions (contacts) change only from Mixy; another agent's
773
+ // home changes only its own agent settings.
774
+ if (AGENT_DEFINITION_ACTIONS.has(action) && args.definitionKind === 'agent') {
775
+ assertAgentEditorAccess(scope, caller, action);
776
+ }
777
+ if (action === 'set_agent_route') assertOwnAgentSettings(scope, args.agent, action);
778
+ }
708
779
  const rt = action === 'status' || action === 'open' ? null : api();
709
- if (!Object.hasOwn(SETUP_ACTION_HANDLERS, action)) return executeExtendedSetupAction(rt, args, requestDesktop);
780
+ if (!Object.hasOwn(SETUP_ACTION_HANDLERS, action)) {
781
+ return executeExtendedSetupAction(rt, args, requestDesktop, { sessionId, caller });
782
+ }
710
783
  return SETUP_ACTION_HANDLERS[action](rt, args, { requestDesktop, readStatus, openSurface });
711
784
  }
712
785