@duckmind/dm-windows-x64 0.61.5 → 0.62.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 (285) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +215 -71
  3. package/extensions/dm-cua/bin/browser-cua.mjs +6 -1
  4. package/extensions/dm-cua/index.js +3 -2
  5. package/extensions/dm-cua/src/browser-cua-lib.mjs +69 -13
  6. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  7. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  8. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  9. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  10. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  11. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  12. package/extensions/dm-subagents/agents/delegate.md +3 -2
  13. package/extensions/dm-subagents/agents/oracle.md +10 -5
  14. package/extensions/dm-subagents/agents/researcher.md +2 -2
  15. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  16. package/extensions/dm-subagents/agents/scout.md +5 -5
  17. package/extensions/dm-subagents/agents/worker.md +6 -2
  18. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  19. package/extensions/dm-subagents/index.js +4 -0
  20. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  21. package/extensions/dm-subagents/install.mjs +3 -2
  22. package/extensions/dm-subagents/package.json +2 -2
  23. package/extensions/dm-subagents/prompts/council.md +60 -0
  24. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  25. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  26. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  27. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  29. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  32. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  33. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  34. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  35. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  36. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  37. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  38. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  39. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  40. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  41. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  42. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  43. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  44. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  45. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  46. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  47. package/extensions/dm-subagents/src/api/agents.js +6 -0
  48. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  49. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  50. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  51. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  52. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  53. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  54. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  55. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  56. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  57. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  58. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  59. package/extensions/dm-subagents/src/extension/config.js +175 -0
  60. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  61. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  62. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  63. package/extensions/dm-subagents/src/extension/index.js +711 -265
  64. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  65. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  66. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  67. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  68. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  69. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  70. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  75. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  76. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  77. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  78. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  79. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  80. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  81. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  82. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  83. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  84. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  85. package/extensions/dm-subagents/src/missions/store.js +548 -0
  86. package/extensions/dm-subagents/src/missions/types.js +9 -0
  87. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  88. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  89. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  90. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  91. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  92. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  93. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  94. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  95. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  96. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  97. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  98. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  99. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  100. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  101. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  102. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  103. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  104. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  105. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  106. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  107. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  108. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  109. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  110. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  111. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  112. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  113. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  114. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  115. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  116. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  117. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  118. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  119. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  120. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  121. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  122. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  123. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  124. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  125. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  126. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  127. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  128. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  129. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  130. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  131. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  132. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  133. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  134. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  135. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  136. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  137. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  138. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  139. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  140. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  141. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  142. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  143. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  144. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  145. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  146. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  147. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  148. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  149. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  150. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  151. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  152. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  153. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  154. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  155. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  156. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  157. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  158. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  161. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  162. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  163. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  164. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  165. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  166. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  167. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  168. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  169. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  170. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  171. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  172. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  173. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  174. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  175. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  176. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  177. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  178. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  179. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  180. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  181. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  182. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  183. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  184. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  185. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  186. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  187. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  188. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  189. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  190. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  191. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  192. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  193. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  194. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  195. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  196. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  197. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  198. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  199. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  200. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  201. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  202. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  203. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  204. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  205. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  206. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  207. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  208. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  209. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  210. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  211. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  212. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  213. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  214. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  215. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  216. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  217. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  218. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  219. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  220. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  221. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  222. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  223. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  224. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  225. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  226. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  227. package/extensions/dm-subagents/src/shared/types.js +47 -7
  228. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  229. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  230. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  231. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  232. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  233. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  234. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  235. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  236. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  237. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  238. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  239. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  240. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  241. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  242. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  243. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  244. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  245. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  246. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  247. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  248. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  249. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  250. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  251. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  252. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  253. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  254. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  255. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  256. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  257. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  258. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  259. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  260. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  261. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  262. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  263. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  264. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  266. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  268. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  269. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  270. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  271. package/extensions/greedysearch-dm/bin/cdp.mjs +28 -3
  272. package/extensions/greedysearch-dm/bin/launch.mjs +2 -3
  273. package/extensions/greedysearch-dm/src/search/constants.mjs +5 -2
  274. package/package.json +4 -3
  275. package/extensions/dm-fff/package.json +0 -21
  276. package/extensions/dm-fff/src/index.js +0 -691
  277. package/extensions/dm-fff/src/query.js +0 -60
  278. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  279. package/extensions/dm-subagents/agents/planner.md +0 -55
  280. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  281. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  282. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  283. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  284. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  285. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -7,25 +7,40 @@ import {
7
7
  defaultSystemPromptMode,
8
8
  discoverAgentsAll,
9
9
  buildRuntimeName,
10
+ findBlockingAgentDiagnostic,
10
11
  frontmatterNameForConfig,
11
12
  parsePackageName,
12
13
  mergeBuiltinAgentOverride,
13
14
  removeBuiltinAgentOverride,
14
- removeBuiltinAgentOverrideFields
15
+ removeBuiltinAgentOverrideFields,
16
+ resolveAgentName
15
17
  } from "./agents.js";
16
18
  import { serializeAgent } from "./agent-serializer.js";
17
- import { serializeChain, serializeJsonChain } from "./chain-serializer.js";
18
- import { discoverAvailableSkills } from "./skills.js";
19
+ import { mergeAgentsForScope } from "./agent-selection.js";
20
+ import { discoverAvailableSkills, resolveSkills } from "./skills.js";
19
21
  import {
20
22
  buildProactiveSkillSubagentRecommendationLines
21
23
  } from "./proactive-skills.js";
22
- import { parseFrontmatter } from "./frontmatter.js";
24
+ import { parseFrontmatter, parseFrontmatterList } from "./frontmatter.js";
23
25
  import { toModelInfo } from "../shared/model-info.js";
24
26
  import { resolveSubagentModelOverride } from "../runs/shared/model-fallback.js";
25
27
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.js";
28
+ import { validateAcceptanceInput } from "../runs/shared/acceptance.js";
29
+ import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.js";
26
30
  import { getProjectConfigDir } from "../shared/utils.js";
27
- function result(text, isError = false) {
28
- return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
31
+ import { previewDisplayText } from "../shared/display-text.js";
32
+ import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.js";
33
+ import { listRuntimeAgentConfigs, mergeRuntimeAgents } from "./runtime-agent-registry.js";
34
+ import { listExternalJobProviders } from "../api/external-job-provider.js";
35
+ function result(text, isError = false, details) {
36
+ return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [], ...details } };
37
+ }
38
+ function jsonDetails(value) {
39
+ return JSON.parse(JSON.stringify(value));
40
+ }
41
+ function presentDetails(value) {
42
+ const cleaned = jsonDetails(value);
43
+ return Object.keys(cleaned).length > 0 ? cleaned : undefined;
29
44
  }
30
45
  function parseCsv(value) {
31
46
  return [...new Set(value.split(",").map((v) => v.trim()).filter(Boolean))];
@@ -37,16 +52,27 @@ function configObject(config) {
37
52
  val = JSON.parse(val);
38
53
  } catch (error) {
39
54
  const message = error instanceof Error ? error.message : String(error);
40
- return { error: `config must be valid JSON: ${message}` };
55
+ return { status: "error", message: `config must be valid JSON: ${message}` };
41
56
  }
42
57
  }
43
58
  if (!val || typeof val !== "object" || Array.isArray(val))
44
- return {};
45
- return { value: val };
59
+ return { status: "missing" };
60
+ return { status: "ok", value: val };
46
61
  }
47
62
  function hasKey(obj, key) {
48
63
  return Object.prototype.hasOwnProperty.call(obj, key);
49
64
  }
65
+ function isJsonSerializable(value) {
66
+ if (value === null || typeof value === "string" || typeof value === "boolean")
67
+ return true;
68
+ if (typeof value === "number")
69
+ return Number.isFinite(value);
70
+ if (Array.isArray(value))
71
+ return value.every(isJsonSerializable);
72
+ if (value && typeof value === "object")
73
+ return Object.values(value).every(isJsonSerializable);
74
+ return false;
75
+ }
50
76
  function asDisambiguationScope(scope) {
51
77
  if (scope === "user" || scope === "project")
52
78
  return scope;
@@ -74,30 +100,48 @@ function parsePackageConfig(value) {
74
100
  function allAgents(d) {
75
101
  return [...d.builtin, ...d.package, ...d.user, ...d.project];
76
102
  }
77
- function availableNames(cwd, kind) {
78
- const d = discoverAgentsAll(cwd);
79
- const items = kind === "agent" ? allAgents(d) : d.chains;
80
- return [...new Set(items.map((x) => x.name))].sort((a, b) => a.localeCompare(b));
103
+ function availableAgentNamesFromDiscovery(d) {
104
+ return [...new Set(allAgents(d).map((agent) => agent.name))].sort((a, b) => a.localeCompare(b));
81
105
  }
82
- function findAgents(name, cwd, scope = "both") {
83
- const d = discoverAgentsAll(cwd);
84
- const raw = name.trim();
85
- const sanitized = sanitizeName(raw);
86
- return allAgents(d).filter((a) => (scope === "both" || a.source === scope) && (a.name === raw || a.name === sanitized)).sort((a, b) => a.source.localeCompare(b.source));
106
+ function availableAgentNames(cwd) {
107
+ return availableAgentNamesFromDiscovery(discoverAgentsAll(cwd));
87
108
  }
88
- function findChains(name, cwd, scope = "both") {
109
+ function findAgentsInDiscovery(name, d, scope = "both") {
89
110
  const raw = name.trim();
90
111
  const sanitized = sanitizeName(raw);
91
- return discoverAgentsAll(cwd).chains.filter((c) => (scope === "both" || c.source === scope) && (c.name === raw || c.name === sanitized)).sort((a, b) => a.source.localeCompare(b.source));
112
+ const scoped = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
113
+ let resolved = resolveAgentName(raw, scoped);
114
+ if (!resolved.agent && !resolved.error && sanitized !== raw)
115
+ resolved = resolveAgentName(sanitized, scoped);
116
+ if (resolved.agent)
117
+ return scoped.filter((agent) => agent.name === resolved.agent.name).sort((a, b) => a.source.localeCompare(b.source));
118
+ return scoped.filter((agent) => Boolean(resolveAgentName(raw, [agent]).agent) || sanitized !== raw && Boolean(resolveAgentName(sanitized, [agent]).agent)).sort((a, b) => a.source.localeCompare(b.source));
119
+ }
120
+ function findAgents(name, cwd, scope = "both") {
121
+ return findAgentsInDiscovery(name, discoverAgentsAll(cwd), scope);
122
+ }
123
+ function diagnosticsForScope(diagnostics, scope) {
124
+ if (scope === "both")
125
+ return diagnostics;
126
+ const excludedSource = scope === "user" ? "project" : "user";
127
+ return diagnostics?.filter((diagnostic) => diagnostic.source !== excludedSource);
92
128
  }
93
- const AGENT_SOURCE_PRECEDENCE = { builtin: 0, package: 1, user: 2, project: 3 };
94
- function pickEffectiveAgent(d, name) {
129
+ const AGENT_SOURCE_PRECEDENCE = { builtin: 0, package: 1, user: 2, project: 3, runtime: 4 };
130
+ function resolveEffectiveAgent(d, name) {
95
131
  const raw = name.trim();
96
- const sanitized = sanitizeName(raw);
97
- const matches = allAgents(d).filter((a) => a.name === raw || a.name === sanitized);
98
- if (matches.length === 0)
99
- return;
100
- return matches.reduce((best, agent) => AGENT_SOURCE_PRECEDENCE[agent.source] > AGENT_SOURCE_PRECEDENCE[best.source] ? agent : best);
132
+ const candidates = allAgents(d);
133
+ let resolved = resolveAgentName(raw, candidates);
134
+ if (!resolved.agent && !resolved.error) {
135
+ const sanitized = sanitizeName(raw);
136
+ if (sanitized !== raw)
137
+ resolved = resolveAgentName(sanitized, candidates);
138
+ }
139
+ if (resolved.error)
140
+ return { error: resolved.error };
141
+ if (!resolved.agent)
142
+ return {};
143
+ const matches = candidates.filter((agent) => agent.name === resolved.agent.name);
144
+ return { agent: matches.reduce((best, agent) => AGENT_SOURCE_PRECEDENCE[agent.source] > AGENT_SOURCE_PRECEDENCE[best.source] ? agent : best) };
101
145
  }
102
146
  function nameExistsInScope(cwd, scope, name, excludePath) {
103
147
  const d = discoverAgentsAll(cwd);
@@ -105,43 +149,18 @@ function nameExistsInScope(cwd, scope, name, excludePath) {
105
149
  if (a.name === name && a.filePath !== excludePath)
106
150
  return true;
107
151
  }
108
- for (const c of d.chains) {
109
- if (c.source === scope && c.name === name && c.filePath !== excludePath)
110
- return true;
111
- }
112
152
  return false;
113
153
  }
114
154
  function isMutableSource(source) {
115
155
  return source === "user" || source === "project";
116
156
  }
117
- function unknownChainAgents(cwd, steps) {
118
- const d = discoverAgentsAll(cwd);
119
- const known = new Set(allAgents(d).map((a) => a.name));
120
- return [...new Set(steps.map((s) => s.agent).filter((a) => !known.has(a)))].sort((a, b) => a.localeCompare(b));
121
- }
122
- function chainStepWarnings(ctx, steps) {
123
- const warnings = [];
124
- const available = new Set(discoverAvailableSkills(ctx.cwd).map((s) => s.name));
125
- for (let i = 0;i < steps.length; i++) {
126
- const s = steps[i];
127
- if (s.model) {
128
- const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === s.model || m.id === s.model);
129
- if (!found)
130
- warnings.push(`Warning: step ${i + 1} (${s.agent}): model '${s.model}' is not in the current model registry.`);
131
- }
132
- if (Array.isArray(s.skills) && s.skills.length > 0) {
133
- const missing = s.skills.filter((sk) => !available.has(sk));
134
- if (missing.length)
135
- warnings.push(`Warning: step ${i + 1} (${s.agent}): skills not found: ${missing.join(", ")}.`);
136
- }
137
- }
138
- return warnings;
139
- }
140
157
  function modelWarning(ctx, model) {
141
158
  if (!model)
142
159
  return;
143
160
  const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === model || m.id === model);
144
- return found ? undefined : `Warning: model '${model}' is not in the current model registry.`;
161
+ if (found)
162
+ return;
163
+ return `Warning: model '${model}' is not in the current model registry. Run subagent({ action: "models" }) to list valid provider/id selectors, then use the exact provider/id form (bare ids resolve only when unique).`;
145
164
  }
146
165
  function fallbackModelsWarning(ctx, fallbackModels) {
147
166
  if (!fallbackModels || fallbackModels.length === 0)
@@ -150,35 +169,80 @@ function fallbackModelsWarning(ctx, fallbackModels) {
150
169
  const missing = fallbackModels.filter((model) => !available.has(model));
151
170
  return missing.length ? `Warning: fallback models not in the current model registry: ${missing.join(", ")}.` : undefined;
152
171
  }
153
- function skillsWarning(cwd, skills) {
154
- if (!skills || skills.length === 0)
172
+ function skillsWarning(cwd, agent) {
173
+ if (!agent.skills?.length)
155
174
  return;
156
- const available = new Set(discoverAvailableSkills(cwd).map((s) => s.name));
157
- const missing = skills.filter((s) => !available.has(s));
175
+ const { missing } = resolveSkills(agent.skills, cwd, agent.skillPath, agent.filePath ? path.dirname(agent.filePath) : cwd);
158
176
  return missing.length ? `Warning: skills not found: ${missing.join(", ")}.` : undefined;
159
177
  }
160
- function editableAgentConfig(agent) {
161
- const base = agent.override?.base;
162
- if (!base)
163
- return { ...agent };
178
+ function withDeclaredExtensionPaths(config, filePath) {
179
+ const { frontmatter } = parseFrontmatter(fs.readFileSync(filePath, "utf-8"));
180
+ const { extensions: _extensions, subagentOnlyExtensions: _subagentOnlyExtensions, ...withoutResolvedExtensions } = config;
164
181
  return {
165
- ...agent,
166
- model: base.model,
167
- fallbackModels: base.fallbackModels ? [...base.fallbackModels] : undefined,
168
- thinking: base.thinking,
182
+ ...withoutResolvedExtensions,
183
+ ...frontmatter.extensions !== undefined ? { extensions: parseFrontmatterList(frontmatter.extensions) } : {},
184
+ ...frontmatter.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: parseFrontmatterList(frontmatter.subagentOnlyExtensions) } : {}
185
+ };
186
+ }
187
+ export function editableAgentConfig(agent) {
188
+ const { extensions: _extensions, ...withoutExtensions } = agent;
189
+ const base = agent.override?.base;
190
+ const {
191
+ override: _override,
192
+ output: _output,
193
+ outputMode: _outputMode,
194
+ defaultReads: _defaultReads,
195
+ model: _model,
196
+ fallbackModels: _fallbackModels,
197
+ thinking: _thinking,
198
+ systemPromptMode: _systemPromptMode,
199
+ inheritProjectContext: _inheritProjectContext,
200
+ inheritGlobalContext: _inheritGlobalContext,
201
+ inheritSkills: _inheritSkills,
202
+ defaultContext: _defaultContext,
203
+ acceptanceRole: _acceptanceRole,
204
+ disabled: _disabled,
205
+ systemPrompt: _systemPrompt,
206
+ skills: _skills,
207
+ skillPath: _skillPath,
208
+ tools: _tools,
209
+ mcpDirectTools: _mcpDirectTools,
210
+ subagentOnlyExtensions: _subagentOnlyExtensions,
211
+ mutationTools: _mutationTools,
212
+ completionGuard: _completionGuard,
213
+ ...editable
214
+ } = withoutExtensions;
215
+ if (!base) {
216
+ return withDeclaredExtensionPaths({
217
+ ...withoutExtensions,
218
+ ...agent.extensionsFromDefault ? {} : agent.extensions !== undefined ? { extensions: [...agent.extensions] } : {}
219
+ }, agent.filePath);
220
+ }
221
+ return withDeclaredExtensionPaths({
222
+ ...editable,
223
+ ...base.output !== undefined ? { output: base.output } : {},
224
+ ...base.outputMode !== undefined ? { outputMode: base.outputMode } : {},
225
+ ...base.defaultReads !== undefined ? { defaultReads: [...base.defaultReads] } : {},
226
+ ...base.model !== undefined ? { model: base.model } : {},
227
+ ...base.fallbackModels !== undefined ? { fallbackModels: [...base.fallbackModels] } : {},
228
+ ...base.thinking !== undefined ? { thinking: base.thinking } : {},
169
229
  systemPromptMode: base.systemPromptMode,
170
230
  inheritProjectContext: base.inheritProjectContext,
231
+ inheritGlobalContext: base.inheritGlobalContext,
171
232
  inheritSkills: base.inheritSkills,
172
- defaultContext: base.defaultContext,
173
- disabled: base.disabled,
233
+ ...base.defaultContext !== undefined ? { defaultContext: base.defaultContext } : {},
234
+ ...base.acceptanceRole !== undefined ? { acceptanceRole: base.acceptanceRole } : {},
235
+ ...base.disabled !== undefined ? { disabled: base.disabled } : {},
174
236
  systemPrompt: base.systemPrompt,
175
- skills: base.skills ? [...base.skills] : undefined,
176
- tools: base.tools ? [...base.tools] : undefined,
177
- mcpDirectTools: base.mcpDirectTools ? [...base.mcpDirectTools] : undefined,
178
- subagentOnlyExtensions: base.subagentOnlyExtensions ? [...base.subagentOnlyExtensions] : undefined,
179
- completionGuard: base.completionGuard,
180
- override: undefined
181
- };
237
+ ...base.skills !== undefined ? { skills: [...base.skills] } : {},
238
+ ...base.skillPath !== undefined ? { skillPath: [...base.skillPath] } : {},
239
+ ...base.tools !== undefined ? { tools: [...base.tools] } : {},
240
+ ...base.mcpDirectTools !== undefined ? { mcpDirectTools: [...base.mcpDirectTools] } : {},
241
+ ...base.extensions !== undefined ? { extensions: [...base.extensions] } : {},
242
+ ...base.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...base.subagentOnlyExtensions] } : {},
243
+ ...base.mutationTools !== undefined ? { mutationTools: [...base.mutationTools] } : {},
244
+ ...base.completionGuard !== undefined ? { completionGuard: base.completionGuard } : {}
245
+ }, agent.filePath);
182
246
  }
183
247
  function readAgentFrontmatterFields(filePath) {
184
248
  try {
@@ -188,7 +252,7 @@ function readAgentFrontmatterFields(filePath) {
188
252
  return new Set;
189
253
  }
190
254
  }
191
- function preservedAgentFrontmatterFields(agent, cfg) {
255
+ export function preservedAgentFrontmatterFields(agent, cfg) {
192
256
  const fields = readAgentFrontmatterFields(agent.filePath);
193
257
  const changed = (...names) => {
194
258
  for (const name of names)
@@ -200,8 +264,12 @@ function preservedAgentFrontmatterFields(agent, cfg) {
200
264
  changed("package");
201
265
  if (hasKey(cfg, "description"))
202
266
  changed("description");
267
+ if (hasKey(cfg, "aliases"))
268
+ changed("alias", "aliases");
203
269
  if (hasKey(cfg, "systemPrompt"))
204
270
  changed("systemPrompt");
271
+ if (hasKey(cfg, "runner"))
272
+ changed("runner");
205
273
  if (hasKey(cfg, "model"))
206
274
  changed("model");
207
275
  if (hasKey(cfg, "fallbackModels"))
@@ -210,10 +278,14 @@ function preservedAgentFrontmatterFields(agent, cfg) {
210
278
  changed("tools");
211
279
  if (hasKey(cfg, "skills"))
212
280
  changed("skill", "skills");
281
+ if (hasKey(cfg, "skillPath"))
282
+ changed("skillPath");
213
283
  if (hasKey(cfg, "extensions"))
214
284
  changed("extensions");
215
285
  if (hasKey(cfg, "subagentOnlyExtensions"))
216
286
  changed("subagentOnlyExtensions");
287
+ if (hasKey(cfg, "mutationTools"))
288
+ changed("mutationTools");
217
289
  if (hasKey(cfg, "thinking")) {
218
290
  changed("thinking");
219
291
  if (cfg.thinking === "off")
@@ -227,14 +299,28 @@ function preservedAgentFrontmatterFields(agent, cfg) {
227
299
  changed("inheritProjectContext");
228
300
  fields.add("inheritProjectContext");
229
301
  }
302
+ if (hasKey(cfg, "inheritGlobalContext")) {
303
+ changed("inheritGlobalContext");
304
+ fields.add("inheritGlobalContext");
305
+ }
230
306
  if (hasKey(cfg, "inheritSkills")) {
231
307
  changed("inheritSkills");
232
308
  fields.add("inheritSkills");
233
309
  }
234
310
  if (hasKey(cfg, "defaultContext"))
235
311
  changed("defaultContext");
312
+ if (hasKey(cfg, "async"))
313
+ changed("async");
314
+ if (hasKey(cfg, "timeoutMs"))
315
+ changed("timeoutMs");
316
+ if (hasKey(cfg, "acceptance"))
317
+ changed("acceptance");
318
+ if (hasKey(cfg, "acceptanceRole"))
319
+ changed("acceptanceRole");
236
320
  if (hasKey(cfg, "output"))
237
321
  changed("output");
322
+ if (hasKey(cfg, "outputMode"))
323
+ changed("outputMode");
238
324
  if (hasKey(cfg, "reads"))
239
325
  changed("defaultReads");
240
326
  if (hasKey(cfg, "progress"))
@@ -250,96 +336,6 @@ function preservedAgentFrontmatterFields(agent, cfg) {
250
336
  changed("toolBudget");
251
337
  return fields;
252
338
  }
253
- function parseStepList(raw) {
254
- if (!Array.isArray(raw))
255
- return { error: "config.steps must be an array." };
256
- if (raw.length === 0)
257
- return { error: "config.steps must include at least one step." };
258
- const steps = [];
259
- for (let i = 0;i < raw.length; i++) {
260
- const item = raw[i];
261
- if (!item || typeof item !== "object" || Array.isArray(item))
262
- return { error: `config.steps[${i}] must be an object.` };
263
- const s = item;
264
- if (typeof s.agent !== "string" || !s.agent.trim())
265
- return { error: `config.steps[${i}].agent must be a non-empty string.` };
266
- const step = { agent: s.agent.trim(), task: typeof s.task === "string" ? s.task : "" };
267
- if (hasKey(s, "phase")) {
268
- if (typeof s.phase === "string")
269
- step.phase = s.phase;
270
- else
271
- return { error: `config.steps[${i}].phase must be a string.` };
272
- }
273
- if (hasKey(s, "label")) {
274
- if (typeof s.label === "string")
275
- step.label = s.label;
276
- else
277
- return { error: `config.steps[${i}].label must be a string.` };
278
- }
279
- if (hasKey(s, "as")) {
280
- if (typeof s.as === "string")
281
- step.as = s.as;
282
- else
283
- return { error: `config.steps[${i}].as must be a string.` };
284
- }
285
- if (hasKey(s, "outputSchema")) {
286
- if (typeof s.outputSchema === "string")
287
- step.outputSchema = s.outputSchema;
288
- else
289
- return { error: `config.steps[${i}].outputSchema must be a schema file path string for saved chains.` };
290
- }
291
- if (hasKey(s, "output")) {
292
- if (s.output === false)
293
- step.output = false;
294
- else if (typeof s.output === "string")
295
- step.output = s.output;
296
- else
297
- return { error: `config.steps[${i}].output must be a string or false.` };
298
- }
299
- if (hasKey(s, "outputMode")) {
300
- if (s.outputMode === "inline" || s.outputMode === "file-only")
301
- step.outputMode = s.outputMode;
302
- else
303
- return { error: `config.steps[${i}].outputMode must be 'inline' or 'file-only'.` };
304
- }
305
- if (hasKey(s, "reads")) {
306
- if (s.reads === false)
307
- step.reads = false;
308
- else if (Array.isArray(s.reads))
309
- step.reads = s.reads.filter((v) => typeof v === "string").map((v) => v.trim()).filter(Boolean);
310
- else
311
- return { error: `config.steps[${i}].reads must be an array or false.` };
312
- }
313
- if (hasKey(s, "model")) {
314
- if (typeof s.model === "string")
315
- step.model = s.model;
316
- else
317
- return { error: `config.steps[${i}].model must be a string.` };
318
- }
319
- if (hasKey(s, "skills")) {
320
- if (s.skills === false)
321
- step.skills = false;
322
- else if (Array.isArray(s.skills))
323
- step.skills = s.skills.filter((v) => typeof v === "string").map((v) => v.trim()).filter(Boolean);
324
- else
325
- return { error: `config.steps[${i}].skills must be an array or false.` };
326
- }
327
- if (hasKey(s, "progress")) {
328
- if (typeof s.progress === "boolean")
329
- step.progress = s.progress;
330
- else
331
- return { error: `config.steps[${i}].progress must be a boolean.` };
332
- }
333
- if (hasKey(s, "toolBudget")) {
334
- const validation = validateToolBudgetConfig(s.toolBudget, `config.steps[${i}].toolBudget`);
335
- if (validation.error)
336
- return { error: validation.error };
337
- step.toolBudget = s.toolBudget;
338
- }
339
- steps.push(step);
340
- }
341
- return { steps };
342
- }
343
339
  function parseTools(raw) {
344
340
  const tools = [];
345
341
  const mcpDirectTools = [];
@@ -351,9 +347,30 @@ function parseTools(raw) {
351
347
  } else
352
348
  tools.push(item);
353
349
  }
354
- return { tools: tools.length ? tools : undefined, mcpDirectTools: mcpDirectTools.length ? mcpDirectTools : undefined };
350
+ return {
351
+ ...tools.length ? { tools } : {},
352
+ ...mcpDirectTools.length ? { mcpDirectTools } : {}
353
+ };
355
354
  }
356
355
  function applyAgentConfig(target, cfg) {
356
+ if (hasKey(cfg, "aliases")) {
357
+ if (cfg.aliases === false || cfg.aliases === "")
358
+ delete target.aliases;
359
+ else if (typeof cfg.aliases === "string") {
360
+ const aliases = parseCsv(cfg.aliases).filter((alias) => alias !== target.name);
361
+ if (aliases.length)
362
+ target.aliases = aliases;
363
+ else
364
+ delete target.aliases;
365
+ } else if (Array.isArray(cfg.aliases) && cfg.aliases.every((entry) => typeof entry === "string")) {
366
+ const aliases = [...new Set(cfg.aliases.map((entry) => entry.trim()).filter(Boolean).filter((alias) => alias !== target.name))];
367
+ if (aliases.length)
368
+ target.aliases = aliases;
369
+ else
370
+ delete target.aliases;
371
+ } else
372
+ return "config.aliases must be a comma-separated string, string array, or false when provided.";
373
+ }
357
374
  if (hasKey(cfg, "systemPrompt")) {
358
375
  if (cfg.systemPrompt === false || cfg.systemPrompt === "")
359
376
  target.systemPrompt = "";
@@ -362,49 +379,103 @@ function applyAgentConfig(target, cfg) {
362
379
  else
363
380
  return "config.systemPrompt must be a string or false when provided.";
364
381
  }
382
+ if (hasKey(cfg, "runner")) {
383
+ if (cfg.runner === false || cfg.runner === "")
384
+ delete target.runner;
385
+ else if (cfg.runner && typeof cfg.runner === "object" && !Array.isArray(cfg.runner)) {
386
+ const runner = cfg.runner;
387
+ if (runner.type === "dm" && Object.keys(runner).every((key) => key === "type"))
388
+ target.runner = { type: "dm" };
389
+ else if (runner.type === "external-cli" && typeof runner.command === "string" && runner.command.trim() && (runner.args === undefined || Array.isArray(runner.args) && runner.args.every((arg) => typeof arg === "string")) && (runner.adapter === undefined || isCodeOwnedExternalCliAdapterId(runner.adapter)) && (runner.adapter === undefined || runner.args === undefined || runner.args.length === 0) && (runner.promptDelivery === undefined || runner.promptDelivery === "stdin") && Object.keys(runner).every((key) => ["type", "adapter", "command", "args", "promptDelivery"].includes(key))) {
390
+ const runnerArgs = Array.isArray(runner.args) ? runner.args.filter((arg) => typeof arg === "string") : undefined;
391
+ target.runner = { type: "external-cli", ...isCodeOwnedExternalCliAdapterId(runner.adapter) ? { adapter: runner.adapter } : {}, command: runner.command.trim(), ...runnerArgs?.length ? { args: runnerArgs } : {}, ...runner.promptDelivery ? { promptDelivery: "stdin" } : {} };
392
+ } else if (runner.type === "external-job" && typeof runner.provider === "string" && runner.provider.trim() === runner.provider && runner.provider && (runner.options === undefined || runner.options && typeof runner.options === "object" && !Array.isArray(runner.options) && isJsonSerializable(runner.options)) && Object.keys(runner).every((key) => ["type", "provider", "options"].includes(key))) {
393
+ target.runner = { type: "external-job", provider: runner.provider, ...runner.options ? { options: runner.options } : {} };
394
+ } else
395
+ return `config.runner must be { type: 'dm' }, { type: 'external-cli', adapter?: ${CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL}, command: string, args?: string[], promptDelivery?: 'stdin' }, or { type: 'external-job', provider: string, options?: object }.`;
396
+ } else
397
+ return "config.runner must be an object, false, or empty string when provided.";
398
+ }
365
399
  if (hasKey(cfg, "model")) {
366
400
  if (cfg.model === false || cfg.model === "")
367
- target.model = undefined;
368
- else if (typeof cfg.model === "string")
369
- target.model = cfg.model.trim() || undefined;
370
- else
401
+ delete target.model;
402
+ else if (typeof cfg.model === "string") {
403
+ const model = cfg.model.trim();
404
+ if (model)
405
+ target.model = model;
406
+ else
407
+ delete target.model;
408
+ } else
371
409
  return "config.model must be a string or false when provided.";
372
410
  }
373
411
  if (hasKey(cfg, "fallbackModels")) {
374
412
  if (cfg.fallbackModels === false || cfg.fallbackModels === "")
375
- target.fallbackModels = undefined;
413
+ delete target.fallbackModels;
376
414
  else if (typeof cfg.fallbackModels === "string") {
377
415
  const models = parseCsv(cfg.fallbackModels);
378
- target.fallbackModels = models.length ? models : undefined;
416
+ if (models.length)
417
+ target.fallbackModels = models;
418
+ else
419
+ delete target.fallbackModels;
379
420
  } else if (Array.isArray(cfg.fallbackModels)) {
380
421
  const models = cfg.fallbackModels.filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
381
- target.fallbackModels = models.length ? [...new Set(models)] : undefined;
422
+ if (models.length)
423
+ target.fallbackModels = [...new Set(models)];
424
+ else
425
+ delete target.fallbackModels;
382
426
  } else
383
427
  return "config.fallbackModels must be a comma-separated string, string array, or false when provided.";
384
428
  }
385
429
  if (hasKey(cfg, "tools")) {
386
430
  if (cfg.tools === false || cfg.tools === "") {
387
- target.tools = undefined;
388
- target.mcpDirectTools = undefined;
431
+ delete target.tools;
432
+ delete target.mcpDirectTools;
389
433
  } else if (typeof cfg.tools === "string") {
390
434
  const parsed = parseTools(cfg.tools);
391
- target.tools = parsed.tools;
392
- target.mcpDirectTools = parsed.mcpDirectTools;
435
+ if (parsed.tools)
436
+ target.tools = parsed.tools;
437
+ else
438
+ delete target.tools;
439
+ if (parsed.mcpDirectTools)
440
+ target.mcpDirectTools = parsed.mcpDirectTools;
441
+ else
442
+ delete target.mcpDirectTools;
393
443
  } else
394
444
  return "config.tools must be a comma-separated string or false when provided.";
395
445
  }
396
446
  if (hasKey(cfg, "skills")) {
397
447
  if (cfg.skills === false || cfg.skills === "")
398
- target.skills = undefined;
448
+ delete target.skills;
399
449
  else if (typeof cfg.skills === "string") {
400
450
  const skills = parseCsv(cfg.skills);
401
- target.skills = skills.length ? skills : undefined;
451
+ if (skills.length)
452
+ target.skills = skills;
453
+ else
454
+ delete target.skills;
402
455
  } else
403
456
  return "config.skills must be a comma-separated string or false when provided.";
404
457
  }
458
+ if (hasKey(cfg, "skillPath")) {
459
+ if (cfg.skillPath === false || cfg.skillPath === "")
460
+ delete target.skillPath;
461
+ else if (typeof cfg.skillPath === "string") {
462
+ const skillPath = parseCsv(cfg.skillPath);
463
+ if (skillPath.length)
464
+ target.skillPath = skillPath;
465
+ else
466
+ delete target.skillPath;
467
+ } else if (Array.isArray(cfg.skillPath) && cfg.skillPath.every((entry) => typeof entry === "string")) {
468
+ const skillPath = [...new Set(cfg.skillPath.map((entry) => entry.trim()).filter(Boolean))];
469
+ if (skillPath.length)
470
+ target.skillPath = skillPath;
471
+ else
472
+ delete target.skillPath;
473
+ } else
474
+ return "config.skillPath must be a comma-separated string, string array, or false when provided.";
475
+ }
405
476
  if (hasKey(cfg, "extensions")) {
406
477
  if (cfg.extensions === false)
407
- target.extensions = undefined;
478
+ delete target.extensions;
408
479
  else if (cfg.extensions === "")
409
480
  target.extensions = [];
410
481
  else if (typeof cfg.extensions === "string")
@@ -414,7 +485,7 @@ function applyAgentConfig(target, cfg) {
414
485
  }
415
486
  if (hasKey(cfg, "subagentOnlyExtensions")) {
416
487
  if (cfg.subagentOnlyExtensions === false)
417
- target.subagentOnlyExtensions = undefined;
488
+ delete target.subagentOnlyExtensions;
418
489
  else if (cfg.subagentOnlyExtensions === "")
419
490
  target.subagentOnlyExtensions = [];
420
491
  else if (typeof cfg.subagentOnlyExtensions === "string")
@@ -422,12 +493,26 @@ function applyAgentConfig(target, cfg) {
422
493
  else
423
494
  return "config.subagentOnlyExtensions must be a comma-separated string, empty string, or false when provided.";
424
495
  }
496
+ if (hasKey(cfg, "mutationTools")) {
497
+ if (cfg.mutationTools === false)
498
+ delete target.mutationTools;
499
+ else if (cfg.mutationTools === "")
500
+ target.mutationTools = [];
501
+ else if (typeof cfg.mutationTools === "string")
502
+ target.mutationTools = parseCsv(cfg.mutationTools);
503
+ else
504
+ return "config.mutationTools must be a comma-separated string, empty string, or false when provided.";
505
+ }
425
506
  if (hasKey(cfg, "thinking")) {
426
507
  if (cfg.thinking === false || cfg.thinking === "")
427
- target.thinking = undefined;
428
- else if (typeof cfg.thinking === "string")
429
- target.thinking = cfg.thinking.trim() || undefined;
430
- else
508
+ delete target.thinking;
509
+ else if (typeof cfg.thinking === "string") {
510
+ const thinking = cfg.thinking.trim();
511
+ if (thinking)
512
+ target.thinking = thinking;
513
+ else
514
+ delete target.thinking;
515
+ } else
431
516
  return "config.thinking must be a string or false when provided.";
432
517
  }
433
518
  if (hasKey(cfg, "systemPromptMode")) {
@@ -441,6 +526,11 @@ function applyAgentConfig(target, cfg) {
441
526
  return "config.inheritProjectContext must be a boolean when provided.";
442
527
  target.inheritProjectContext = cfg.inheritProjectContext;
443
528
  }
529
+ if (hasKey(cfg, "inheritGlobalContext")) {
530
+ if (typeof cfg.inheritGlobalContext !== "boolean")
531
+ return "config.inheritGlobalContext must be a boolean when provided.";
532
+ target.inheritGlobalContext = cfg.inheritGlobalContext;
533
+ }
444
534
  if (hasKey(cfg, "inheritSkills")) {
445
535
  if (typeof cfg.inheritSkills !== "boolean")
446
536
  return "config.inheritSkills must be a boolean when provided.";
@@ -448,26 +538,69 @@ function applyAgentConfig(target, cfg) {
448
538
  }
449
539
  if (hasKey(cfg, "defaultContext")) {
450
540
  if (cfg.defaultContext === false || cfg.defaultContext === "")
451
- target.defaultContext = undefined;
541
+ delete target.defaultContext;
452
542
  else if (cfg.defaultContext === "fresh" || cfg.defaultContext === "fork")
453
543
  target.defaultContext = cfg.defaultContext;
454
544
  else
455
545
  return "config.defaultContext must be 'fresh', 'fork', or false when provided.";
456
546
  }
547
+ if (hasKey(cfg, "async")) {
548
+ if (cfg.async === "")
549
+ delete target.defaultAsync;
550
+ else if (typeof cfg.async === "boolean")
551
+ target.defaultAsync = cfg.async;
552
+ else
553
+ return "config.async must be a boolean or empty string when provided.";
554
+ }
555
+ if (hasKey(cfg, "timeoutMs")) {
556
+ if (cfg.timeoutMs === false || cfg.timeoutMs === "")
557
+ delete target.defaultTimeoutMs;
558
+ else if (typeof cfg.timeoutMs === "number" && Number.isInteger(cfg.timeoutMs) && cfg.timeoutMs > 0)
559
+ target.defaultTimeoutMs = cfg.timeoutMs;
560
+ else
561
+ return "config.timeoutMs must be a positive integer or false when provided.";
562
+ }
563
+ if (hasKey(cfg, "acceptance")) {
564
+ if (cfg.acceptance === "")
565
+ delete target.defaultAcceptance;
566
+ else {
567
+ const errors = validateAcceptanceInput(cfg.acceptance, "config.acceptance");
568
+ if (errors.length > 0)
569
+ return errors.join(" ");
570
+ target.defaultAcceptance = cfg.acceptance;
571
+ }
572
+ }
573
+ if (hasKey(cfg, "acceptanceRole")) {
574
+ if (cfg.acceptanceRole === false || cfg.acceptanceRole === "")
575
+ delete target.acceptanceRole;
576
+ else if (cfg.acceptanceRole === "read-only" || cfg.acceptanceRole === "writer")
577
+ target.acceptanceRole = cfg.acceptanceRole;
578
+ else
579
+ return "config.acceptanceRole must be 'read-only', 'writer', or false when provided.";
580
+ }
457
581
  if (hasKey(cfg, "output")) {
458
582
  if (cfg.output === false || cfg.output === "")
459
- target.output = undefined;
583
+ delete target.output;
460
584
  else if (typeof cfg.output === "string")
461
585
  target.output = cfg.output;
462
586
  else
463
587
  return "config.output must be a string or false when provided.";
464
588
  }
589
+ if (hasKey(cfg, "outputMode")) {
590
+ if (cfg.outputMode === "inline" || cfg.outputMode === "file-only")
591
+ target.outputMode = cfg.outputMode;
592
+ else
593
+ return "config.outputMode must be 'inline' or 'file-only' when provided.";
594
+ }
465
595
  if (hasKey(cfg, "reads")) {
466
596
  if (cfg.reads === false || cfg.reads === "")
467
- target.defaultReads = undefined;
597
+ delete target.defaultReads;
468
598
  else if (typeof cfg.reads === "string") {
469
599
  const reads = parseCsv(cfg.reads);
470
- target.defaultReads = reads.length ? reads : undefined;
600
+ if (reads.length)
601
+ target.defaultReads = reads;
602
+ else
603
+ delete target.defaultReads;
471
604
  } else
472
605
  return "config.reads must be a comma-separated string or false when provided.";
473
606
  }
@@ -478,7 +611,7 @@ function applyAgentConfig(target, cfg) {
478
611
  }
479
612
  if (hasKey(cfg, "maxSubagentDepth")) {
480
613
  if (cfg.maxSubagentDepth === false || cfg.maxSubagentDepth === "")
481
- target.maxSubagentDepth = undefined;
614
+ delete target.maxSubagentDepth;
482
615
  else if (typeof cfg.maxSubagentDepth === "number" && Number.isInteger(cfg.maxSubagentDepth) && cfg.maxSubagentDepth >= 0) {
483
616
  target.maxSubagentDepth = cfg.maxSubagentDepth;
484
617
  } else
@@ -491,7 +624,7 @@ function applyAgentConfig(target, cfg) {
491
624
  }
492
625
  if (hasKey(cfg, "toolBudget")) {
493
626
  if (cfg.toolBudget === false || cfg.toolBudget === "")
494
- target.toolBudget = undefined;
627
+ delete target.toolBudget;
495
628
  else {
496
629
  const validation = validateToolBudgetConfig(cfg.toolBudget, "config.toolBudget");
497
630
  if (validation.error)
@@ -499,51 +632,241 @@ function applyAgentConfig(target, cfg) {
499
632
  target.toolBudget = cfg.toolBudget;
500
633
  }
501
634
  }
635
+ if (target.runner?.type === "external-cli" || target.runner?.type === "external-job") {
636
+ const unsupported = [
637
+ target.tools?.length || target.mcpDirectTools?.length ? "tools" : undefined,
638
+ target.model ? "model" : undefined,
639
+ target.fallbackModels?.length ? "fallbackModels" : undefined,
640
+ target.thinking ? "thinking" : undefined,
641
+ target.extensions?.length ? "extensions" : undefined,
642
+ target.subagentOnlyExtensions?.length ? "subagentOnlyExtensions" : undefined,
643
+ target.mutationTools?.length ? "mutationTools" : undefined,
644
+ target.skills?.length || target.skillPath?.length ? "skills" : undefined,
645
+ target.maxSubagentDepth !== undefined ? "maxSubagentDepth" : undefined,
646
+ target.completionGuard !== undefined ? "completionGuard" : undefined,
647
+ target.toolBudget ? "toolBudget" : undefined
648
+ ].filter((field) => Boolean(field));
649
+ if (unsupported.length > 0)
650
+ return `config.runner type '${target.runner.type}' does not support DM-only fields: ${unsupported.join(", ")}.`;
651
+ }
502
652
  return;
503
653
  }
504
- function resolveTarget(kind, name, matches, cwd, scopeHint) {
505
- const mutable = matches.filter((m) => isMutableSource(m.source));
654
+ function resolveTarget(name, matches, cwd, scopeHint) {
655
+ const distinctNames = [...new Set(matches.map((match) => match.name))];
656
+ if (distinctNames.length > 1)
657
+ return result(`Ambiguous agent alias or name '${name}': ${distinctNames.sort((a, b) => a.localeCompare(b)).join(", ")}`, true);
658
+ const mutable = matches.filter((match) => isMutableSource(match.source));
506
659
  if (mutable.length === 0) {
507
- if (matches.length > 0) {
508
- return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is read-only and cannot be modified. Create a same-named ${kind} in user or project scope to override it.`, true);
509
- }
510
- const available = availableNames(cwd, kind);
511
- return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found. Available: ${available.join(", ") || "none"}.`, true);
660
+ if (matches.length > 0)
661
+ return result(`Agent '${name}' is read-only and cannot be modified. Create a same-named agent in user or project scope to override it.`, true);
662
+ return result(`Agent '${name}' not found. Available: ${availableAgentNames(cwd).join(", ") || "none"}.`, true);
512
663
  }
513
664
  if (mutable.length === 1)
514
665
  return mutable[0];
515
666
  const scope = asDisambiguationScope(scopeHint);
516
667
  if (!scope) {
517
- const paths = mutable.map((m) => `${m.source}: ${m.filePath}`).join(`
668
+ const paths = mutable.map((match) => `${match.source}: ${match.filePath}`).join(`
518
669
  `);
519
- return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' exists in both scopes. Specify agentScope: 'user' or 'project'.
670
+ return result(`Agent '${name}' exists in both scopes. Specify agentScope: 'user' or 'project'.
520
671
  ${paths}`, true);
521
672
  }
522
- const scoped = mutable.filter((m) => m.source === scope);
673
+ const scoped = mutable.filter((match) => match.source === scope);
523
674
  if (scoped.length === 0)
524
- return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found in scope '${scope}'.`, true);
675
+ return result(`Agent '${name}' not found in scope '${scope}'.`, true);
525
676
  if (scoped.length > 1)
526
- return result(`Multiple ${kind}s named '${name}' found in scope '${scope}': ${scoped.map((m) => m.filePath).join(", ")}`, true);
677
+ return result(`Multiple agents named '${name}' found in scope '${scope}': ${scoped.map((match) => match.filePath).join(", ")}`, true);
527
678
  return scoped[0];
528
679
  }
529
- function renamePath(kind, currentPath, newName, scope, cwd) {
680
+ function renamePath(currentPath, newName, scope, cwd) {
530
681
  if (nameExistsInScope(cwd, scope, newName, currentPath))
531
682
  return { error: `Name '${newName}' already exists in ${scope} scope.` };
532
- const ext = kind === "agent" ? ".md" : currentPath.endsWith(".chain.json") ? ".chain.json" : ".chain.md";
533
- const filePath = path.join(path.dirname(currentPath), `${newName}${ext}`);
534
- if (fs.existsSync(filePath) && filePath !== currentPath) {
535
- return { error: `File already exists at ${filePath} but is not a valid ${kind} definition. Remove or rename it first.` };
536
- }
683
+ const filePath = path.join(path.dirname(currentPath), `${newName}.md`);
684
+ if (fs.existsSync(filePath) && filePath !== currentPath)
685
+ return { error: `File already exists at ${filePath} but is not a valid agent definition. Remove or rename it first.` };
537
686
  fs.renameSync(currentPath, filePath);
538
687
  return { filePath };
539
688
  }
689
+ function packageSourceLabel(agent) {
690
+ if (!agent.packageSourceName)
691
+ return agent.source;
692
+ return agent.packageSourceVersion ? `${agent.packageSourceName}@${agent.packageSourceVersion}` : agent.packageSourceName;
693
+ }
694
+ function errorMessage(error) {
695
+ return error instanceof Error ? error.message : String(error);
696
+ }
697
+ function registeredExternalJobProviderStatus() {
698
+ try {
699
+ return { ok: true, names: new Set(listExternalJobProviders().map((provider) => provider.name)) };
700
+ } catch (error) {
701
+ return { ok: false, error: errorMessage(error) };
702
+ }
703
+ }
704
+ function externalJobProviderSuffix(provider, names) {
705
+ if (!names)
706
+ return "?";
707
+ return names.has(provider) ? "✓" : "missing";
708
+ }
709
+ function runnerListBadge(agent, providerNames) {
710
+ if (agent.runner?.type === "external-job")
711
+ return `external-job:${agent.runner.provider} ${externalJobProviderSuffix(agent.runner.provider, providerNames)}`;
712
+ if (agent.runner?.type === "external-cli")
713
+ return "external-cli";
714
+ return;
715
+ }
716
+ function agentListMetadata(agent, providerNames) {
717
+ const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
718
+ return [
719
+ source,
720
+ runnerListBadge(agent, providerNames),
721
+ agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
722
+ agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined
723
+ ].filter((part) => Boolean(part)).join(", ");
724
+ }
725
+ function formatAgentListLine(agent, providerNames) {
726
+ return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
727
+ }
728
+ function formatAgentCapabilitiesLine(agent, providerNames) {
729
+ const declaredTools = [
730
+ ...agent.tools ?? [],
731
+ ...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`)
732
+ ];
733
+ let tools = "none";
734
+ if (agent.tools === undefined && agent.mcpDirectTools === undefined) {
735
+ tools = "default/ambient";
736
+ } else if (declaredTools.length > 0) {
737
+ tools = declaredTools.join(", ");
738
+ }
739
+ let model = "inherits current session";
740
+ if (agent.model !== undefined) {
741
+ model = agent.model;
742
+ if (agent.modelProvider && !agent.model.includes("/"))
743
+ model = `${agent.modelProvider}/${agent.model}`;
744
+ }
745
+ const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
746
+ return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
747
+ }
748
+ const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false };
749
+ const PI_AGENT_RUNNER = { type: "dm" };
750
+ function listOrEmpty(values) {
751
+ return values ?? [];
752
+ }
753
+ function agentCapabilityRunner(agent, providerNames) {
754
+ const runner = agent.runner;
755
+ if (!runner || runner.type === "dm")
756
+ return PI_AGENT_RUNNER;
757
+ if (runner.type === "external-cli")
758
+ return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
759
+ return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
760
+ }
761
+ function agentCapabilityTools(agent) {
762
+ return {
763
+ ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
764
+ names: listOrEmpty(agent.tools),
765
+ mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
766
+ mutationTools: agent.mutationTools
767
+ };
768
+ }
769
+ function agentCapabilityRow(agent, options) {
770
+ return {
771
+ name: agent.name,
772
+ description: previewDisplayText(agent.description, 1000),
773
+ source: agent.source,
774
+ executable: options.executable,
775
+ restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
776
+ aliases: agent.aliases ? [...agent.aliases] : undefined,
777
+ runner: agentCapabilityRunner(agent, options.providerNames),
778
+ tools: agentCapabilityTools(agent),
779
+ model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
780
+ execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
781
+ output: presentDetails({ path: agent.output, mode: agent.outputMode }),
782
+ extensions: presentDetails({ names: agent.extensions, subagentOnly: agent.subagentOnlyExtensions, skills: agent.skills })
783
+ };
784
+ }
785
+ function agentCapabilitiesSnapshot(input) {
786
+ return {
787
+ agents: [
788
+ ...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
789
+ ...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources }))
790
+ ],
791
+ restrictedCount: input.restrictedAgents.length,
792
+ ...input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}
793
+ };
794
+ }
795
+ function providerNames(status) {
796
+ return status.ok ? status.names : undefined;
797
+ }
798
+ function appendRestrictedAgentLines(input) {
799
+ if (input.agents.length === 0)
800
+ return;
801
+ input.lines.push("", `Restricted agents (not executable in this session${input.sources?.length ? `; capability ceiling: ${input.sources.join(", ")}` : ""}):`, ...input.agents.map((agent) => input.formatLine(agent, input.providerNames)));
802
+ }
803
+ function appendExternalJobRegistryLine(lines, agents, status) {
804
+ if (status.ok || !agents.some((agent) => agent.runner?.type === "external-job"))
805
+ return;
806
+ lines.push("", `External-job provider registry unavailable: ${status.error}`);
807
+ }
808
+ function appendAgentDiagnosticLines(lines, diagnostics) {
809
+ if (!diagnostics?.length)
810
+ return;
811
+ lines.push("", "Invalid agent definitions:", ...diagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`));
812
+ }
813
+ function agentCapabilityDetails(input) {
814
+ if (!input.capabilityMode)
815
+ return;
816
+ return {
817
+ agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
818
+ agents: input.agents,
819
+ restrictedAgents: input.restrictedAgents,
820
+ providerNames: input.providerNames,
821
+ restrictedSources: input.restrictedSources
822
+ }))
823
+ };
824
+ }
825
+ function formatAgentListSections(agents, providerNames, formatLine = formatAgentListLine) {
826
+ if (agents.length === 0)
827
+ return ["- (none)"];
828
+ const sections = [
829
+ ["package", "Package agents"],
830
+ ["user", "User agents"],
831
+ ["project", "Project agents"],
832
+ ["runtime", "Runtime agents"],
833
+ ["builtin", "Builtin agents"]
834
+ ];
835
+ const lines = [];
836
+ for (const [source, label] of sections) {
837
+ const matches = agents.filter((agent) => agent.source === source);
838
+ if (matches.length === 0)
839
+ continue;
840
+ if (lines.length > 0)
841
+ lines.push("");
842
+ lines.push(label, ...matches.map((agent) => formatLine(agent, providerNames)));
843
+ }
844
+ return lines;
845
+ }
846
+ function formatRunnerDetail(agent, providerNames) {
847
+ if (!agent.runner)
848
+ return;
849
+ if (agent.runner.type === "external-job")
850
+ return `Runner: external-job via ${agent.runner.provider} ${externalJobProviderSuffix(agent.runner.provider, providerNames)}`;
851
+ if (agent.runner.type === "external-cli")
852
+ return `Runner: external-cli ${agent.runner.command}`;
853
+ return `Runner: ${JSON.stringify(agent.runner)}`;
854
+ }
540
855
  function formatAgentDetail(agent) {
541
856
  const tools = [...agent.tools ?? [], ...(agent.mcpDirectTools ?? []).map((t) => `mcp:${t}`)];
857
+ const providerStatus = registeredExternalJobProviderStatus();
542
858
  const lines = [`Agent: ${agent.name} (${agent.source})`, `Path: ${agent.filePath}`, `Description: ${agent.description}`];
859
+ if (agent.source === "package" && agent.packageSourceName) {
860
+ lines.push(`Source package: ${packageSourceLabel(agent)}`);
861
+ if (agent.packageSourceRoot)
862
+ lines.push(`Package root: ${agent.packageSourceRoot}`);
863
+ }
543
864
  if (agent.packageName) {
544
865
  lines.push(`Local name: ${frontmatterNameForConfig(agent)}`);
545
866
  lines.push(`Package: ${agent.packageName}`);
546
867
  }
868
+ if (agent.aliases?.length)
869
+ lines.push(`Aliases: ${agent.aliases.join(", ")}`);
547
870
  if (agent.model)
548
871
  lines.push(`Model: ${agent.model}`);
549
872
  if (agent.fallbackModels?.length)
@@ -552,21 +875,44 @@ function formatAgentDetail(agent) {
552
875
  lines.push(`Tools: ${tools.join(", ")}`);
553
876
  if (agent.skills?.length)
554
877
  lines.push(`Skills: ${agent.skills.join(", ")}`);
878
+ if (agent.skillPath?.length)
879
+ lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
555
880
  lines.push(`System prompt mode: ${agent.systemPromptMode}`);
881
+ const runnerDetail = formatRunnerDetail(agent, providerStatus.ok ? providerStatus.names : undefined);
882
+ if (runnerDetail) {
883
+ lines.push(runnerDetail);
884
+ if (agent.runner?.type === "external-job" && !providerStatus.ok)
885
+ lines.push(`External-job provider registry unavailable: ${providerStatus.error}`);
886
+ if (agent.runner?.type === "external-job" && agent.runner.options)
887
+ lines.push(`Runner options: ${JSON.stringify(agent.runner.options)}`);
888
+ }
556
889
  lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
890
+ lines.push(`Inherit global context: ${agent.inheritGlobalContext ? "true" : "false"}`);
557
891
  lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
558
892
  if (agent.defaultContext)
559
893
  lines.push(`Default context: ${agent.defaultContext}`);
894
+ if (agent.defaultAsync !== undefined)
895
+ lines.push(`Async: ${agent.defaultAsync ? "true" : "false"}`);
896
+ if (agent.defaultTimeoutMs !== undefined)
897
+ lines.push(`Timeout: ${agent.defaultTimeoutMs}ms`);
898
+ if (agent.defaultAcceptance !== undefined)
899
+ lines.push(`Acceptance: ${typeof agent.defaultAcceptance === "object" ? JSON.stringify(agent.defaultAcceptance) : String(agent.defaultAcceptance)}`);
900
+ if (agent.acceptanceRole)
901
+ lines.push(`Acceptance role: ${agent.acceptanceRole}`);
560
902
  if (agent.source === "builtin")
561
903
  lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
562
904
  if (agent.extensions !== undefined)
563
905
  lines.push(`Extensions: ${agent.extensions.length ? agent.extensions.join(", ") : "(none)"}`);
564
906
  if (agent.subagentOnlyExtensions !== undefined)
565
907
  lines.push(`Subagent-only extensions: ${agent.subagentOnlyExtensions.length ? agent.subagentOnlyExtensions.join(", ") : "(none)"}`);
908
+ if (agent.mutationTools !== undefined)
909
+ lines.push(`Mutation tools: ${agent.mutationTools.length ? agent.mutationTools.join(", ") : "(none)"}`);
566
910
  if (agent.thinking)
567
911
  lines.push(`Thinking: ${agent.thinking}`);
568
912
  if (agent.output)
569
913
  lines.push(`Output: ${agent.output}`);
914
+ if (agent.outputMode)
915
+ lines.push(`Output mode: ${agent.outputMode}`);
570
916
  if (agent.defaultReads?.length)
571
917
  lines.push(`Reads: ${agent.defaultReads.join(", ")}`);
572
918
  if (agent.defaultProgress)
@@ -584,103 +930,51 @@ function formatAgentDetail(agent) {
584
930
  return lines.join(`
585
931
  `);
586
932
  }
587
- function formatChainStepDetail(step, index) {
588
- const lines = [];
589
- if (step.expand || step.collect) {
590
- const parallel = step.parallel && !Array.isArray(step.parallel) && typeof step.parallel === "object" ? step.parallel : undefined;
591
- const expand = step.expand && typeof step.expand === "object" ? step.expand : undefined;
592
- const collect = step.collect && typeof step.collect === "object" ? step.collect : undefined;
593
- lines.push(`${index + 1}. Dynamic fanout${typeof collect?.as === "string" ? ` -> ${collect.as}` : ""}`);
594
- if (expand?.from)
595
- lines.push(` Expand: ${String(expand.from.output ?? "?")}${String(expand.from.path ?? "")}`);
596
- if (typeof expand?.item === "string")
597
- lines.push(` Item variable: ${expand.item}`);
598
- if (typeof expand?.key === "string")
599
- lines.push(` Key: ${expand.key}`);
600
- if (typeof expand?.maxItems === "number")
601
- lines.push(` Max items: ${expand.maxItems}`);
602
- if (typeof expand?.onEmpty === "string")
603
- lines.push(` On empty: ${expand.onEmpty}`);
604
- if (parallel?.agent)
605
- lines.push(` Agent: ${String(parallel.agent)}`);
606
- if (typeof parallel?.label === "string")
607
- lines.push(` Label: ${parallel.label}`);
608
- if (typeof parallel?.task === "string" && parallel.task.trim())
609
- lines.push(` Task: ${parallel.task}`);
610
- if (parallel?.outputSchema)
611
- lines.push(" Structured output: true");
612
- if (parallel && "toolBudget" in parallel)
613
- lines.push(` Tool budget: ${JSON.stringify(parallel.toolBudget)}`);
614
- if (collect?.outputSchema)
615
- lines.push(" Collect schema: true");
616
- if (step.concurrency !== undefined)
617
- lines.push(` Concurrency: ${step.concurrency}`);
618
- if (step.failFast !== undefined)
619
- lines.push(` Fail fast: ${step.failFast ? "true" : "false"}`);
620
- return lines;
621
- }
622
- lines.push(`${index + 1}. ${step.agent}`);
623
- if (step.task?.trim())
624
- lines.push(` Task: ${step.task}`);
625
- if (step.output === false)
626
- lines.push(" Output: false");
627
- else if (step.output)
628
- lines.push(` Output: ${step.output}`);
629
- if (step.outputMode)
630
- lines.push(` Output mode: ${step.outputMode}`);
631
- if (step.toolBudget)
632
- lines.push(` Tool budget: ${JSON.stringify(step.toolBudget)}`);
633
- if (step.reads === false)
634
- lines.push(" Reads: false");
635
- else if (Array.isArray(step.reads) && step.reads.length > 0)
636
- lines.push(` Reads: ${step.reads.join(", ")}`);
637
- if (step.model)
638
- lines.push(` Model: ${step.model}`);
639
- if (step.skills === false)
640
- lines.push(" Skills: false");
641
- else if (Array.isArray(step.skills) && step.skills.length > 0)
642
- lines.push(` Skills: ${step.skills.join(", ")}`);
643
- if (step.progress !== undefined)
644
- lines.push(` Progress: ${step.progress ? "true" : "false"}`);
645
- return lines;
646
- }
647
- function formatChainDetail(chain) {
648
- const lines = [`Chain: ${chain.name} (${chain.source})`, `Path: ${chain.filePath}`, `Description: ${chain.description}`];
649
- if (chain.packageName) {
650
- lines.push(`Local name: ${frontmatterNameForConfig(chain)}`);
651
- lines.push(`Package: ${chain.packageName}`);
652
- }
653
- lines.push("", "Steps:");
654
- for (let i = 0;i < chain.steps.length; i++) {
655
- lines.push(...formatChainStepDetail(chain.steps[i], i));
656
- }
657
- return lines.join(`
658
- `);
659
- }
660
933
  export function handleList(params, ctx) {
661
934
  const scope = normalizeListScope(params.agentScope) ?? "both";
662
- const d = discoverAgentsAll(ctx.cwd);
663
- const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === "package" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
664
- const agents = scopedAgents.filter((a) => !a.disabled);
665
- const chains = d.chains.filter((c) => scope === "both" || c.source === "package" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
666
- const diagnostics = d.chainDiagnostics.filter((entry) => scope === "both" || entry.source === scope);
935
+ const d = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
936
+ let scopedAgents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
937
+ if (ctx.runtimeAgentOwner && listRuntimeAgentConfigs(ctx.runtimeAgentOwner).length > 0) {
938
+ const configuredAgents = [
939
+ ...d.builtin,
940
+ ...d.package,
941
+ ...d.user,
942
+ ...d.project
943
+ ];
944
+ scopedAgents = mergeRuntimeAgents(ctx.runtimeAgentOwner, { agents: scopedAgents }, configuredAgents).agents;
945
+ }
946
+ scopedAgents = scopedAgents.sort((a, b) => a.name.localeCompare(b.name));
947
+ const capabilityCeiling = resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
948
+ const visibleAgents = scopedAgents.filter((a) => !a.disabled);
949
+ const agents = visibleAgents.filter((a) => isAgentAllowedByCapabilityCeiling(a.name, capabilityCeiling));
950
+ const restrictedAgents = visibleAgents.filter((a) => !isAgentAllowedByCapabilityCeiling(a.name, capabilityCeiling));
951
+ const restrictedSources = capabilityCeilingAgentRestrictionSources(capabilityCeiling);
667
952
  const proactiveSuggestions = buildProactiveSkillSubagentRecommendationLines({
668
953
  agents,
669
- chains,
670
- config: ctx.config?.proactiveSkillSubagents,
954
+ ...ctx.config?.proactiveSkillSubagents !== undefined ? { config: ctx.config.proactiveSkillSubagents } : {},
671
955
  discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd)
672
956
  });
957
+ const providerStatus = registeredExternalJobProviderStatus();
958
+ const providerNameSet = providerNames(providerStatus);
959
+ const capabilityMode = params.capabilities === true;
960
+ const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
673
961
  const lines = [
674
- "Executable agents:",
675
- ...agents.length ? agents.map((a) => `- ${a.name} (${a.source}${a.defaultContext ? `, context: ${a.defaultContext}` : ""}): ${a.description}`) : ["- (none)"],
676
- "",
677
- "Chains:",
678
- ...chains.length ? chains.map((c) => `- ${c.name} (${c.source}): ${c.description}`) : ["- (none)"],
679
- ...proactiveSuggestions.length ? ["", ...proactiveSuggestions] : [],
680
- ...diagnostics.length ? ["", "Chain diagnostics:", ...diagnostics.map((entry) => `- ${entry.filePath}: ${entry.error}`)] : []
962
+ capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
963
+ ...formatAgentListSections(agents, providerNameSet, formatLine)
681
964
  ];
965
+ appendRestrictedAgentLines({ lines, agents: restrictedAgents, sources: restrictedSources, providerNames: providerNameSet, formatLine });
966
+ appendExternalJobRegistryLine(lines, [...agents, ...restrictedAgents], providerStatus);
967
+ appendAgentDiagnosticLines(lines, d.agentDiagnostics);
968
+ if (proactiveSuggestions.length)
969
+ lines.push("", ...proactiveSuggestions);
682
970
  return result(lines.join(`
683
- `));
971
+ `), false, agentCapabilityDetails({
972
+ capabilityMode,
973
+ agents,
974
+ restrictedAgents,
975
+ providerNames: providerNameSet,
976
+ restrictedSources
977
+ }));
684
978
  }
685
979
  function formatModelSource(agent, currentModel) {
686
980
  if (agent.override && agent.model !== agent.override.base.model) {
@@ -700,14 +994,15 @@ function handleModels(params, ctx) {
700
994
  if (requestedAgent && !BUILTIN_AGENT_NAMES.includes(requestedAgent)) {
701
995
  return result(`Builtin agent '${requestedAgent}' not found. Available: ${BUILTIN_AGENT_NAMES.join(", ")}.`, true);
702
996
  }
703
- const discovered = discoverAgentsAll(ctx.cwd);
997
+ const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
704
998
  const builtinByName = new Map(discovered.builtin.map((agent) => [agent.name, agent]));
999
+ const resolveBuiltinModelAgent = (name) => builtinByName.get(name) ?? resolveAgentName(name, discovered.builtin).agent;
705
1000
  const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
706
1001
  const currentModel = ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined;
707
1002
  const preferredProvider = ctx.model?.provider;
708
1003
  const names = requestedAgent ? [requestedAgent] : [...BUILTIN_AGENT_NAMES];
709
1004
  if (requestedAgent) {
710
- const agent = builtinByName.get(requestedAgent);
1005
+ const agent = resolveBuiltinModelAgent(requestedAgent);
711
1006
  if (!agent)
712
1007
  return result(`Builtin agent '${requestedAgent}' not found.`, true);
713
1008
  const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, preferredProvider);
@@ -742,7 +1037,7 @@ function handleModels(params, ctx) {
742
1037
  ""
743
1038
  ];
744
1039
  for (const name of names) {
745
- const agent = builtinByName.get(name);
1040
+ const agent = resolveBuiltinModelAgent(name);
746
1041
  if (!agent) {
747
1042
  lines.push(name);
748
1043
  lines.push(" model:");
@@ -759,50 +1054,52 @@ function handleModels(params, ctx) {
759
1054
  lines.push(` source: ${source}`);
760
1055
  lines.push("");
761
1056
  }
1057
+ const availableFullIds = availableModels.map((m) => m.fullId).sort();
1058
+ if (availableFullIds.length > 0) {
1059
+ lines.push("Available models in this session's registry (copy an exact provider/id when passing model):");
1060
+ lines.push("");
1061
+ const shown = availableFullIds.slice(0, 80);
1062
+ for (const fullId of shown)
1063
+ lines.push(` ${fullId}`);
1064
+ if (availableFullIds.length > shown.length)
1065
+ lines.push(` ... and ${availableFullIds.length - shown.length} more`);
1066
+ lines.push("");
1067
+ lines.push("Use an exact provider/id from this list when you pass model; bare ids resolve only when unique in the registry.");
1068
+ }
762
1069
  return result(lines.join(`
763
1070
  `));
764
1071
  }
765
1072
  function handleGet(params, ctx) {
766
- if (!params.agent && !params.chainName)
767
- return result("Specify 'agent' or 'chainName' for get.", true);
768
- const hasBoth = Boolean(params.agent && params.chainName);
769
- const blocks = [];
770
- let anyFound = false;
771
- if (params.agent) {
772
- const matches = findAgents(params.agent, ctx.cwd, "both");
773
- if (!matches.length) {
774
- const msg = `Agent '${params.agent}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`;
775
- if (!hasBoth)
776
- return result(msg, true);
777
- blocks.push(msg);
778
- } else {
779
- anyFound = true;
780
- blocks.push(...matches.map(formatAgentDetail));
781
- }
782
- }
783
- if (params.chainName) {
784
- const matches = findChains(params.chainName, ctx.cwd, "both");
785
- if (!matches.length) {
786
- const msg = `Chain '${params.chainName}' not found. Available: ${availableNames(ctx.cwd, "chain").join(", ") || "none"}.`;
787
- if (!hasBoth)
788
- return result(msg, true);
789
- blocks.push(msg);
790
- } else {
791
- anyFound = true;
792
- blocks.push(...matches.map(formatChainDetail));
793
- }
794
- }
795
- return result(blocks.join(`
1073
+ if (!params.agent)
1074
+ return result("Specify 'agent' for get.", true);
1075
+ const scope = normalizeListScope(params.agentScope);
1076
+ if (!scope)
1077
+ return result("agentScope must be 'user', 'project', or 'both' for get.", true);
1078
+ const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
1079
+ const matches = findAgentsInDiscovery(params.agent, discovered, scope);
1080
+ const diagnostics = diagnosticsForScope(discovered.agentDiagnostics, scope);
1081
+ const rawName = params.agent.trim();
1082
+ const normalizedName = sanitizeName(rawName);
1083
+ const diagnostic = findBlockingAgentDiagnostic(rawName, matches, diagnostics) ?? (normalizedName !== rawName ? findBlockingAgentDiagnostic(normalizedName, matches, diagnostics) : undefined);
1084
+ if (diagnostic)
1085
+ return result(`Agent '${params.agent}' has invalid configuration: ${diagnostic.error}`, true);
1086
+ const distinctNames = [...new Set(matches.map((agent) => agent.name))];
1087
+ if (distinctNames.length > 1)
1088
+ return result(`Ambiguous agent alias or name '${params.agent}': ${distinctNames.sort((a, b) => a.localeCompare(b)).join(", ")}`, true);
1089
+ if (!matches.length) {
1090
+ return result(`Agent '${params.agent}' not found. Available: ${availableAgentNamesFromDiscovery(discovered).join(", ") || "none"}.`, true);
1091
+ }
1092
+ return result(matches.map(formatAgentDetail).join(`
796
1093
 
797
- `), !anyFound);
1094
+ `));
798
1095
  }
799
1096
  export function handleCreate(params, ctx) {
800
1097
  const parsedConfig = configObject(params.config);
801
- if (parsedConfig.error)
802
- return result(parsedConfig.error, true);
803
- const cfg = parsedConfig.value;
804
- if (!cfg)
1098
+ if (parsedConfig.status === "error")
1099
+ return result(parsedConfig.message, true);
1100
+ if (parsedConfig.status === "missing")
805
1101
  return result("config required for create.", true);
1102
+ const cfg = parsedConfig.value;
806
1103
  if (typeof cfg.name !== "string" || !cfg.name.trim())
807
1104
  return result("config.name is required and must be a non-empty string.", true);
808
1105
  if (typeof cfg.description !== "string" || !cfg.description.trim())
@@ -818,54 +1115,46 @@ export function handleCreate(params, ctx) {
818
1115
  if (scopeRaw !== "user" && scopeRaw !== "project")
819
1116
  return result("config.scope must be 'user' or 'project'.", true);
820
1117
  const scope = scopeRaw;
821
- const isChain = hasKey(cfg, "steps");
1118
+ if (hasKey(cfg, "steps"))
1119
+ return result("Durable chain definitions were removed; use workflowScript or /prompt-workflow for repeatable workflows.", true);
822
1120
  const d = discoverAgentsAll(ctx.cwd);
823
1121
  const projectConfigDir = getProjectConfigDir(ctx.cwd);
824
- const targetDir = isChain ? scope === "user" ? d.userChainDir : d.projectChainDir ?? path.join(projectConfigDir, "chains") : scope === "user" ? d.userDir : d.projectDir ?? path.join(projectConfigDir, "agents");
1122
+ const targetDir = scope === "user" ? d.userDir : d.projectDir ?? path.join(projectConfigDir, "agents");
825
1123
  fs.mkdirSync(targetDir, { recursive: true });
826
1124
  if (nameExistsInScope(ctx.cwd, scope, runtimeName))
827
1125
  return result(`Name '${runtimeName}' already exists in ${scope} scope. Use update instead.`, true);
828
- const targetPath = path.join(targetDir, isChain ? `${runtimeName}.chain.md` : `${runtimeName}.md`);
1126
+ const targetPath = path.join(targetDir, `${runtimeName}.md`);
829
1127
  if (fs.existsSync(targetPath))
830
- return result(`File already exists at ${targetPath} but is not a valid ${isChain ? "chain" : "agent"} definition. Remove or rename it first.`, true);
1128
+ return result(`File already exists at ${targetPath} but is not a valid agent definition. Remove or rename it first.`, true);
831
1129
  const warnings = [];
832
- if (!isChain && d.builtin.some((a) => a.name === runtimeName))
1130
+ if (d.builtin.some((a) => a.name === runtimeName))
833
1131
  warnings.push(`Note: this shadows the builtin agent '${runtimeName}'.`);
834
- if (isChain) {
835
- const parsed = parseStepList(cfg.steps);
836
- if (parsed.error)
837
- return result(parsed.error, true);
838
- const chain = { name: runtimeName, localName: name, packageName: parsedPackage.packageName, description: cfg.description.trim(), source: scope, filePath: targetPath, steps: parsed.steps };
839
- fs.writeFileSync(targetPath, serializeChain(chain), "utf-8");
840
- const missing = unknownChainAgents(ctx.cwd, chain.steps);
841
- if (missing.length)
842
- warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
843
- warnings.push(...chainStepWarnings(ctx, chain.steps));
844
- return result([`Created chain '${runtimeName}' at ${targetPath}.`, ...warnings].join(`
845
- `));
846
- }
847
1132
  const agent = {
848
1133
  name: runtimeName,
849
1134
  localName: name,
850
- packageName: parsedPackage.packageName,
1135
+ ...parsedPackage.packageName !== undefined ? { packageName: parsedPackage.packageName } : {},
851
1136
  description: cfg.description.trim(),
852
1137
  source: scope,
853
1138
  filePath: targetPath,
854
1139
  systemPrompt: "",
855
1140
  systemPromptMode: defaultSystemPromptMode(name),
856
1141
  inheritProjectContext: defaultInheritProjectContext(name),
1142
+ inheritGlobalContext: false,
857
1143
  inheritSkills: defaultInheritSkills()
858
1144
  };
859
1145
  const applyError = applyAgentConfig(agent, cfg);
860
1146
  if (applyError)
861
1147
  return result(applyError, true);
1148
+ const profileError = validateCodeOwnedProfileRunner(agent);
1149
+ if (profileError)
1150
+ return result(profileError, true);
862
1151
  const mw = modelWarning(ctx, agent.model);
863
1152
  if (mw)
864
1153
  warnings.push(mw);
865
1154
  const fmw = fallbackModelsWarning(ctx, agent.fallbackModels);
866
1155
  if (fmw)
867
1156
  warnings.push(fmw);
868
- const sw = skillsWarning(ctx.cwd, agent.skills);
1157
+ const sw = skillsWarning(ctx.cwd, agent);
869
1158
  if (sw)
870
1159
  warnings.push(sw);
871
1160
  fs.writeFileSync(targetPath, serializeAgent(agent), "utf-8");
@@ -873,88 +1162,31 @@ export function handleCreate(params, ctx) {
873
1162
  `));
874
1163
  }
875
1164
  export function handleUpdate(params, ctx) {
876
- if (!params.agent && !params.chainName)
877
- return result("Specify 'agent' or 'chainName' for update.", true);
878
- if (params.agent && params.chainName)
879
- return result("Specify either 'agent' or 'chainName', not both.", true);
1165
+ if (!params.agent)
1166
+ return result("Specify 'agent' for update.", true);
880
1167
  const parsedConfig = configObject(params.config);
881
- if (parsedConfig.error)
882
- return result(parsedConfig.error, true);
883
- const cfg = parsedConfig.value;
884
- if (!cfg)
1168
+ if (parsedConfig.status === "error")
1169
+ return result(parsedConfig.message, true);
1170
+ if (parsedConfig.status === "missing")
885
1171
  return result("config required for update.", true);
1172
+ const cfg = parsedConfig.value;
1173
+ if (hasKey(cfg, "steps"))
1174
+ return result("Durable chain definitions were removed; use workflowScript or /prompt-workflow for repeatable workflows.", true);
886
1175
  const warnings = [];
887
- if (params.agent) {
888
- const scopeHint = asDisambiguationScope(params.agentScope);
889
- const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
890
- if ("content" in targetOrError)
891
- return targetOrError;
892
- const target = targetOrError;
893
- const updated = editableAgentConfig(target);
894
- const oldName = target.name;
895
- if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim()))
896
- return result("config.name must be a non-empty string when provided.", true);
897
- if (hasKey(cfg, "description") && (typeof cfg.description !== "string" || !cfg.description.trim()))
898
- return result("config.description must be a non-empty string when provided.", true);
899
- let newLocalName = target.localName ?? frontmatterNameForConfig(target);
900
- if (hasKey(cfg, "name")) {
901
- newLocalName = sanitizeName(cfg.name);
902
- if (!newLocalName)
903
- return result("config.name is invalid after sanitization.", true);
904
- }
905
- let newPackageName = target.packageName;
906
- if (hasKey(cfg, "package")) {
907
- const parsedPackage = parsePackageConfig(cfg.package);
908
- if (parsedPackage.error)
909
- return result(parsedPackage.error, true);
910
- newPackageName = parsedPackage.packageName;
911
- }
912
- const applyError = applyAgentConfig(updated, cfg);
913
- if (applyError)
914
- return result(applyError, true);
915
- const preserveFrontmatterFields = preservedAgentFrontmatterFields(target, cfg);
916
- updated.localName = newLocalName;
917
- updated.packageName = newPackageName;
918
- updated.name = buildRuntimeName(newLocalName, newPackageName);
919
- if (hasKey(cfg, "description"))
920
- updated.description = cfg.description.trim();
921
- if (hasKey(cfg, "model")) {
922
- const mw = modelWarning(ctx, updated.model);
923
- if (mw)
924
- warnings.push(mw);
925
- }
926
- if (hasKey(cfg, "fallbackModels")) {
927
- const fmw = fallbackModelsWarning(ctx, updated.fallbackModels);
928
- if (fmw)
929
- warnings.push(fmw);
930
- }
931
- if (hasKey(cfg, "skills")) {
932
- const sw = skillsWarning(ctx.cwd, updated.skills);
933
- if (sw)
934
- warnings.push(sw);
935
- }
936
- if (updated.name !== oldName) {
937
- const renamed = renamePath("agent", target.filePath, updated.name, target.source, ctx.cwd);
938
- if (renamed.error)
939
- return result(renamed.error, true);
940
- updated.filePath = renamed.filePath;
941
- }
942
- fs.writeFileSync(updated.filePath, serializeAgent(updated, { preserveFrontmatterFields }), "utf-8");
943
- if (updated.name !== oldName) {
944
- const refs = discoverAgentsAll(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === oldName)).map((c) => `${c.name} (${c.source})`);
945
- if (refs.length)
946
- warnings.push(`Warning: chains still reference '${oldName}': ${refs.join(", ")}.`);
947
- }
948
- const headline = updated.name === oldName ? `Updated agent '${updated.name}' at ${updated.filePath}.` : `Updated agent '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
949
- return result([headline, ...warnings].join(`
950
- `));
951
- }
952
1176
  const scopeHint = asDisambiguationScope(params.agentScope);
953
- const targetOrError = resolveTarget("chain", params.chainName, findChains(params.chainName, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
1177
+ const targetOrError = resolveTarget(params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
954
1178
  if ("content" in targetOrError)
955
1179
  return targetOrError;
956
1180
  const target = targetOrError;
957
- const updated = { ...target, steps: [...target.steps] };
1181
+ if (target.source !== "user" && target.source !== "project")
1182
+ return result(`Cannot update ${target.source} agent '${target.name}'. Eject it to user or project scope first.`, true);
1183
+ let updated;
1184
+ try {
1185
+ updated = editableAgentConfig(target);
1186
+ } catch (error) {
1187
+ const message = error instanceof Error ? error.message : String(error);
1188
+ return result(`Could not reread agent definition ${target.filePath} before updating '${target.name}': ${message}`, true);
1189
+ }
958
1190
  const oldName = target.name;
959
1191
  if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim()))
960
1192
  return result("config.name must be a non-empty string when provided.", true);
@@ -973,61 +1205,57 @@ export function handleUpdate(params, ctx) {
973
1205
  return result(parsedPackage.error, true);
974
1206
  newPackageName = parsedPackage.packageName;
975
1207
  }
976
- let parsedSteps;
977
- if (hasKey(cfg, "steps")) {
978
- const parsed = parseStepList(cfg.steps);
979
- if (parsed.error)
980
- return result(parsed.error, true);
981
- parsedSteps = parsed.steps;
982
- }
1208
+ const applyError = applyAgentConfig(updated, cfg);
1209
+ if (applyError)
1210
+ return result(applyError, true);
1211
+ const preserveFrontmatterFields = preservedAgentFrontmatterFields(target, cfg);
983
1212
  updated.localName = newLocalName;
984
- updated.packageName = newPackageName;
1213
+ if (newPackageName !== undefined)
1214
+ updated.packageName = newPackageName;
1215
+ else
1216
+ delete updated.packageName;
985
1217
  updated.name = buildRuntimeName(newLocalName, newPackageName);
1218
+ const profileError = validateCodeOwnedProfileRunner(updated);
1219
+ if (profileError)
1220
+ return result(profileError, true);
986
1221
  if (hasKey(cfg, "description"))
987
1222
  updated.description = cfg.description.trim();
988
- if (parsedSteps) {
989
- updated.steps = parsedSteps;
990
- const missing = unknownChainAgents(ctx.cwd, updated.steps);
991
- if (missing.length)
992
- warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
993
- warnings.push(...chainStepWarnings(ctx, updated.steps));
1223
+ if (hasKey(cfg, "model")) {
1224
+ const mw = modelWarning(ctx, updated.model);
1225
+ if (mw)
1226
+ warnings.push(mw);
1227
+ }
1228
+ if (hasKey(cfg, "fallbackModels")) {
1229
+ const fmw = fallbackModelsWarning(ctx, updated.fallbackModels);
1230
+ if (fmw)
1231
+ warnings.push(fmw);
1232
+ }
1233
+ if (hasKey(cfg, "skills") || hasKey(cfg, "skillPath")) {
1234
+ const sw = skillsWarning(ctx.cwd, updated);
1235
+ if (sw)
1236
+ warnings.push(sw);
994
1237
  }
995
1238
  if (updated.name !== oldName) {
996
- const renamed = renamePath("chain", target.filePath, updated.name, target.source, ctx.cwd);
1239
+ const renamed = renamePath(target.filePath, updated.name, target.source, ctx.cwd);
997
1240
  if (renamed.error)
998
1241
  return result(renamed.error, true);
999
1242
  updated.filePath = renamed.filePath;
1000
1243
  }
1001
- fs.writeFileSync(updated.filePath, updated.filePath.endsWith(".chain.json") ? serializeJsonChain(updated) : serializeChain(updated), "utf-8");
1002
- const headline = updated.name === oldName ? `Updated chain '${updated.name}' at ${updated.filePath}.` : `Updated chain '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
1244
+ fs.writeFileSync(updated.filePath, serializeAgent(updated, { preserveFrontmatterFields }), "utf-8");
1245
+ const headline = updated.name === oldName ? `Updated agent '${updated.name}' at ${updated.filePath}.` : `Updated agent '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
1003
1246
  return result([headline, ...warnings].join(`
1004
1247
  `));
1005
1248
  }
1006
1249
  function handleDelete(params, ctx) {
1007
- if (!params.agent && !params.chainName)
1008
- return result("Specify 'agent' or 'chainName' for delete.", true);
1009
- if (params.agent && params.chainName)
1010
- return result("Specify either 'agent' or 'chainName', not both.", true);
1250
+ if (!params.agent)
1251
+ return result("Specify 'agent' for delete.", true);
1011
1252
  const scopeHint = asDisambiguationScope(params.agentScope);
1012
- if (params.agent) {
1013
- const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
1014
- if ("content" in targetOrError)
1015
- return targetOrError;
1016
- const target = targetOrError;
1017
- fs.unlinkSync(target.filePath);
1018
- const refs = discoverAgentsAll(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === target.name)).map((c) => `${c.name} (${c.source})`);
1019
- const lines = [`Deleted agent '${target.name}' at ${target.filePath}.`];
1020
- if (refs.length)
1021
- lines.push(`Warning: chains reference deleted agent '${target.name}': ${refs.join(", ")}.`);
1022
- return result(lines.join(`
1023
- `));
1024
- }
1025
- const targetOrError = resolveTarget("chain", params.chainName, findChains(params.chainName, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
1253
+ const targetOrError = resolveTarget(params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
1026
1254
  if ("content" in targetOrError)
1027
1255
  return targetOrError;
1028
1256
  const target = targetOrError;
1029
1257
  fs.unlinkSync(target.filePath);
1030
- return result(`Deleted chain '${target.name}' at ${target.filePath}.`);
1258
+ return result(`Deleted agent '${target.name}' at ${target.filePath}.`);
1031
1259
  }
1032
1260
  function handleEject(params, ctx) {
1033
1261
  if (!params.agent)
@@ -1041,7 +1269,7 @@ function handleEject(params, ctx) {
1041
1269
  const d = discoverAgentsAll(ctx.cwd);
1042
1270
  const source = [...d.package, ...d.builtin].find((a) => a.name === raw || a.name === sanitized);
1043
1271
  if (!source) {
1044
- return result(`Agent '${raw}' not found or is not a bundled/package agent. eject copies a builtin or package agent to ${scope} scope so it can be customized. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`, true);
1272
+ return result(`Agent '${raw}' not found or is not a bundled/package agent. eject copies a builtin or package agent to ${scope} scope so it can be customized. Available: ${availableAgentNames(ctx.cwd).join(", ") || "none"}.`, true);
1045
1273
  }
1046
1274
  const runtimeName = source.name;
1047
1275
  const existingCustom = (scope === "user" ? d.user : d.project).find((a) => a.name === runtimeName);
@@ -1049,7 +1277,7 @@ function handleEject(params, ctx) {
1049
1277
  return result(`Agent '${runtimeName}' is already a custom ${scope} agent at ${existingCustom.filePath}. Edit it with { action: "update", agent: "${runtimeName}" } or delete it first.`, true);
1050
1278
  }
1051
1279
  if (nameExistsInScope(ctx.cwd, scope, runtimeName)) {
1052
- return result(`An agent or chain named '${runtimeName}' already exists in ${scope} scope. Remove or rename it first.`, true);
1280
+ return result(`An agent named '${runtimeName}' already exists in ${scope} scope. Remove or rename it first.`, true);
1053
1281
  }
1054
1282
  const projectConfigDir = getProjectConfigDir(ctx.cwd);
1055
1283
  const targetDir = scope === "user" ? d.userDir : d.projectDir ?? path.join(projectConfigDir, "agents");
@@ -1078,15 +1306,17 @@ function handleDisable(params, ctx) {
1078
1306
  const scope = parsedScope.scope;
1079
1307
  const d = discoverAgentsAll(ctx.cwd);
1080
1308
  if (scope === "project" && d.projectSettingsPath === null) {
1081
- return result("Project override is not available here: no DM project config root was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1309
+ return result("Project override is not available here: no project config root (.dm or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1082
1310
  }
1083
- const effective = pickEffectiveAgent(d, raw);
1084
- if (!effective) {
1085
- return result(`Agent '${raw}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`, true);
1311
+ const effective = resolveEffectiveAgent(d, raw);
1312
+ if (effective.error)
1313
+ return result(effective.error, true);
1314
+ if (!effective.agent) {
1315
+ return result(`Agent '${raw}' not found. Available: ${availableAgentNames(ctx.cwd).join(", ") || "none"}.`, true);
1086
1316
  }
1087
- const runtimeName = effective.name;
1317
+ const runtimeName = effective.agent.name;
1088
1318
  const settingsPath = mergeBuiltinAgentOverride(ctx.cwd, runtimeName, scope, { disabled: true });
1089
- const after = pickEffectiveAgent(discoverAgentsAll(ctx.cwd), raw);
1319
+ const after = resolveEffectiveAgent(discoverAgentsAll(ctx.cwd), raw).agent;
1090
1320
  if (after?.disabled === true) {
1091
1321
  return result(`Disabled agent '${runtimeName}' via ${scope} settings override at ${settingsPath}. It is now hidden from runtime discovery and { action: "list" }.`);
1092
1322
  }
@@ -1102,15 +1332,17 @@ function handleEnable(params, ctx) {
1102
1332
  const scope = parsedScope.scope;
1103
1333
  const d = discoverAgentsAll(ctx.cwd);
1104
1334
  if (scope === "project" && d.projectSettingsPath === null) {
1105
- return result("Project override is not available here: no DM project config root was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1335
+ return result("Project override is not available here: no project config root (.dm or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1106
1336
  }
1107
- const effective = pickEffectiveAgent(d, raw);
1108
- if (!effective) {
1109
- return result(`Agent '${raw}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`, true);
1337
+ const effective = resolveEffectiveAgent(d, raw);
1338
+ if (effective.error)
1339
+ return result(effective.error, true);
1340
+ if (!effective.agent) {
1341
+ return result(`Agent '${raw}' not found. Available: ${availableAgentNames(ctx.cwd).join(", ") || "none"}.`, true);
1110
1342
  }
1111
- const runtimeName = effective.name;
1343
+ const runtimeName = effective.agent.name;
1112
1344
  const { path: settingsPath, removed } = removeBuiltinAgentOverrideFields(ctx.cwd, runtimeName, scope, ["disabled"]);
1113
- const after = pickEffectiveAgent(discoverAgentsAll(ctx.cwd), raw);
1345
+ const after = resolveEffectiveAgent(discoverAgentsAll(ctx.cwd), raw).agent;
1114
1346
  if (after && after.disabled !== true) {
1115
1347
  if (removed)
1116
1348
  return result(`Enabled agent '${runtimeName}' (removed disabled override at ${settingsPath}).`);
@@ -1132,7 +1364,7 @@ function handleReset(params, ctx) {
1132
1364
  const scope = parsedScope.scope;
1133
1365
  const d = discoverAgentsAll(ctx.cwd);
1134
1366
  if (scope === "project" && d.projectSettingsPath === null) {
1135
- return result("Project override is not available here: no DM project config root was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1367
+ return result("Project override is not available here: no project config root (.dm or .agents) was found above the cwd. Use agentScope: 'user' or run from inside a project.", true);
1136
1368
  }
1137
1369
  const bundled = [...d.package, ...d.builtin].find((a) => a.name === raw || a.name === sanitized);
1138
1370
  if (!bundled) {
@@ -1140,7 +1372,7 @@ function handleReset(params, ctx) {
1140
1372
  if (custom) {
1141
1373
  return result(`Agent '${raw}' has no bundled default to reset to. Use { action: "delete", agent: "${custom.name}" } to remove the custom ${custom.source} agent.`, true);
1142
1374
  }
1143
- return result(`Agent '${raw}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`, true);
1375
+ return result(`Agent '${raw}' not found. Available: ${availableAgentNames(ctx.cwd).join(", ") || "none"}.`, true);
1144
1376
  }
1145
1377
  const runtimeName = bundled.name;
1146
1378
  const custom = (scope === "user" ? d.user : d.project).find((a) => a.name === raw || a.name === sanitized);