@duckmind/dm-windows-x64 0.60.6 → 0.60.8
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.
- package/dm.exe +0 -0
- package/extensions/.dm-extensions.json +70 -123
- package/extensions/dm-9router-ext/src/index.js +410 -5
- package/extensions/dm-caveman/extensions/caveman.js +283 -12
- package/extensions/dm-cliproxy/index.js +182 -2
- package/extensions/dm-cliproxy/scripts/check-config-migration.js +66 -8
- package/extensions/dm-cliproxy/src/apply.js +228 -1
- package/extensions/dm-cliproxy/src/cache.js +42 -1
- package/extensions/dm-cliproxy/src/commands.js +50 -2
- package/extensions/dm-cliproxy/src/compat.js +81 -1
- package/extensions/dm-cliproxy/src/config.js +192 -2
- package/extensions/dm-cliproxy/src/conflicts.js +46 -1
- package/extensions/dm-cliproxy/src/fetch-models.js +190 -1
- package/extensions/dm-cliproxy/src/fetch-usage.js +41 -1
- package/extensions/dm-cliproxy/src/log.js +23 -1
- package/extensions/dm-cliproxy/src/status-quota.js +77 -1
- package/extensions/dm-cliproxy/src/ui-frame.js +50 -1
- package/extensions/dm-cliproxy/src/ui-hub/hub.js +199 -2
- package/extensions/dm-cliproxy/src/ui-hub/index.js +26 -2
- package/extensions/dm-cliproxy/src/ui-hub/shell.js +46 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-diagnostics.js +69 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-models.js +379 -1
- package/extensions/dm-cliproxy/src/ui-hub/view-usage.js +106 -1
- package/extensions/dm-cliproxy/src/ui-picker/catalog.js +41 -1
- package/extensions/dm-cliproxy/src/ui-picker/mutate.js +115 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-confirm.js +35 -1
- package/extensions/dm-cliproxy/src/ui-picker/prompt-name.js +62 -1
- package/extensions/dm-cliproxy/src/ui-picker/providers.js +45 -1
- package/extensions/dm-cliproxy/src/ui-picker/render-text.js +34 -1
- package/extensions/dm-cliproxy/src/ui-picker/rows.js +57 -1
- package/extensions/dm-cliproxy/src/ui-setup.js +260 -2
- package/extensions/dm-cliproxy/src/ui-usage.js +151 -1
- package/extensions/dm-cliproxy/src/usage-shared-cache.js +97 -1
- package/extensions/dm-context/src/context.js +144 -1
- package/extensions/dm-context/src/index.js +339 -7
- package/extensions/dm-context/src/utils.js +9 -1
- package/extensions/dm-cua/bin/browser-cua.mjs +73 -8
- package/extensions/dm-cua/index.js +75 -6
- package/extensions/dm-cua/src/browser-cua-lib.mjs +490 -6
- package/extensions/dm-cua/src/browser-install.mjs +331 -2
- package/extensions/dm-fff/src/index.js +688 -12
- package/extensions/dm-fff/src/query.js +60 -1
- package/extensions/dm-goal/src/goal.js +894 -23
- package/extensions/dm-image2/index.js +103 -9
- package/extensions/dm-image2/src/image-lib.mjs +1275 -8
- package/extensions/dm-subagents/install.mjs +62 -8
- package/extensions/dm-subagents/src/agents/agent-management.js +1190 -36
- package/extensions/dm-subagents/src/agents/agent-memory.js +216 -6
- package/extensions/dm-subagents/src/agents/agent-scope.js +5 -1
- package/extensions/dm-subagents/src/agents/agent-selection.js +20 -1
- package/extensions/dm-subagents/src/agents/agent-serializer.js +120 -5
- package/extensions/dm-subagents/src/agents/agents.js +1139 -11
- package/extensions/dm-subagents/src/agents/chain-serializer.js +299 -11
- package/extensions/dm-subagents/src/agents/frontmatter.js +65 -6
- package/extensions/dm-subagents/src/agents/identity.js +29 -1
- package/extensions/dm-subagents/src/agents/proactive-skills.js +141 -1
- package/extensions/dm-subagents/src/agents/skills.js +614 -6
- package/extensions/dm-subagents/src/extension/config.js +35 -2
- package/extensions/dm-subagents/src/extension/control-notices.js +69 -4
- package/extensions/dm-subagents/src/extension/doctor.js +172 -15
- package/extensions/dm-subagents/src/extension/fanout-child.js +158 -231
- package/extensions/dm-subagents/src/extension/index.js +521 -359
- package/extensions/dm-subagents/src/extension/rpc.js +266 -7
- package/extensions/dm-subagents/src/extension/schemas.js +275 -1
- package/extensions/dm-subagents/src/extension/tool-description.js +111 -6
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +126 -4
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +452 -5
- package/extensions/dm-subagents/src/intercom/result-intercom.js +319 -3
- package/extensions/dm-subagents/src/profiles/profiles.js +458 -3
- package/extensions/dm-subagents/src/runs/background/async-execution.js +834 -40
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +435 -14
- package/extensions/dm-subagents/src/runs/background/async-resume.js +334 -8
- package/extensions/dm-subagents/src/runs/background/async-status.js +313 -12
- package/extensions/dm-subagents/src/runs/background/chain-append.js +245 -2
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +136 -1
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +94 -1
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +54 -1
- package/extensions/dm-subagents/src/runs/background/control-channel.js +190 -1
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +483 -17
- package/extensions/dm-subagents/src/runs/background/notify.js +129 -3
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +34 -1
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +236 -6
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +76 -4
- package/extensions/dm-subagents/src/runs/background/run-status.js +427 -23
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +487 -4
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +306 -9
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +2849 -73
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +5 -1
- package/extensions/dm-subagents/src/runs/background/wait.js +206 -11
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +1013 -12
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +980 -101
- package/extensions/dm-subagents/src/runs/foreground/execution.js +1165 -45
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +3157 -222
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +835 -3
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +104 -1
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +116 -3
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +208 -1
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +90 -1
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +282 -1
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +148 -1
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +305 -1
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +194 -1
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +65 -1
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +851 -8
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +41 -1
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +105 -1
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +81 -4
- package/extensions/dm-subagents/src/runs/shared/run-history.js +51 -4
- package/extensions/dm-subagents/src/runs/shared/single-output.js +149 -8
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +58 -1
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +166 -5
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +329 -13
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +73 -1
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +47 -4
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +196 -1
- package/extensions/dm-subagents/src/runs/shared/worktree.js +435 -3
- package/extensions/dm-subagents/src/shared/artifacts.js +92 -2
- package/extensions/dm-subagents/src/shared/atomic-json.js +55 -1
- package/extensions/dm-subagents/src/shared/child-transcript.js +167 -5
- package/extensions/dm-subagents/src/shared/file-coalescer.js +25 -1
- package/extensions/dm-subagents/src/shared/fork-context.js +147 -4
- package/extensions/dm-subagents/src/shared/formatters.js +98 -7
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +56 -2
- package/extensions/dm-subagents/src/shared/model-info.js +62 -1
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +68 -1
- package/extensions/dm-subagents/src/shared/session-identity.js +6 -1
- package/extensions/dm-subagents/src/shared/session-tokens.js +39 -2
- package/extensions/dm-subagents/src/shared/settings.js +198 -11
- package/extensions/dm-subagents/src/shared/status-format.js +53 -1
- package/extensions/dm-subagents/src/shared/types.js +184 -6
- package/extensions/dm-subagents/src/shared/utils.js +462 -2
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +288 -1
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +297 -7
- package/extensions/dm-subagents/src/slash/slash-bridge.js +118 -1
- package/extensions/dm-subagents/src/slash/slash-commands.js +1287 -31
- package/extensions/dm-subagents/src/slash/slash-live-state.js +240 -4
- package/extensions/dm-subagents/src/tui/render-helpers.js +64 -1
- package/extensions/dm-subagents/src/tui/render.js +1542 -4
- package/extensions/dm-usage/index.js +1294 -9
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +40 -9
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +5 -2
- package/extensions/greedysearch-dm/bin/cdp.mjs +896 -30
- package/extensions/greedysearch-dm/bin/gschrome.mjs +30 -2
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +7 -2
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +13 -2
- package/extensions/greedysearch-dm/bin/launch.mjs +282 -10
- package/extensions/greedysearch-dm/bin/mcp.mjs +386 -361
- package/extensions/greedysearch-dm/bin/search.mjs +620 -540
- package/extensions/greedysearch-dm/bin/visible.mjs +22 -2
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +329 -579
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +301 -583
- package/extensions/greedysearch-dm/extractors/common.mjs +408 -32
- package/extensions/greedysearch-dm/extractors/consensus.mjs +376 -365
- package/extensions/greedysearch-dm/extractors/consent.mjs +303 -14
- package/extensions/greedysearch-dm/extractors/gemini.mjs +228 -592
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +78 -499
- package/extensions/greedysearch-dm/extractors/logically.mjs +270 -347
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +243 -581
- package/extensions/greedysearch-dm/extractors/selectors.mjs +32 -1
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +130 -317
- package/extensions/greedysearch-dm/index.js +123 -23
- package/extensions/greedysearch-dm/src/fetcher.mjs +576 -2
- package/extensions/greedysearch-dm/src/formatters/results.js +95 -10
- package/extensions/greedysearch-dm/src/formatters/sources.js +57 -1
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +49 -1
- package/extensions/greedysearch-dm/src/github.mjs +222 -7
- package/extensions/greedysearch-dm/src/reddit.mjs +145 -14
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +340 -9
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +112 -4
- package/extensions/greedysearch-dm/src/search/chrome.mjs +486 -285
- package/extensions/greedysearch-dm/src/search/constants.mjs +109 -8
- package/extensions/greedysearch-dm/src/search/defaults.mjs +10 -1
- package/extensions/greedysearch-dm/src/search/engines.mjs +79 -9
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +441 -349
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +29 -7
- package/extensions/greedysearch-dm/src/search/minimize.mjs +86 -1
- package/extensions/greedysearch-dm/src/search/output.mjs +51 -5
- package/extensions/greedysearch-dm/src/search/paths.mjs +48 -1
- package/extensions/greedysearch-dm/src/search/pdf.mjs +63 -2
- package/extensions/greedysearch-dm/src/search/port-pid.mjs +69 -1
- package/extensions/greedysearch-dm/src/search/progress.mjs +109 -2
- package/extensions/greedysearch-dm/src/search/query.mjs +21 -1
- package/extensions/greedysearch-dm/src/search/recovery.mjs +49 -1
- package/extensions/greedysearch-dm/src/search/research.mjs +2227 -458
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +61 -11
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +396 -805
- package/extensions/greedysearch-dm/src/search/sources.mjs +412 -1
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +127 -12
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +202 -12
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +209 -23
- package/extensions/greedysearch-dm/src/tools/shared.js +226 -10
- package/extensions/greedysearch-dm/src/utils/content.mjs +35 -4
- package/extensions/greedysearch-dm/src/utils/helpers.js +22 -1
- package/extensions/greedysearch-dm/src/utils/node-runtime.mjs +10 -1
- package/extensions/greedysearch-dm/src/utils/system-cmds.mjs +61 -1
- package/package.json +1 -1
|
@@ -1,38 +1,1294 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`),Y=V.match(/^([ \t]+)/m)?.[1]??"",z=Y?V.replace(new RegExp(`^${T0(Y)}`,"gm"),"").replace(/^\n/,""):V;J[X]=z}return{frontmatter:J,body:Q}}import{Compile as MW}from"typebox/compile";function w$($,J="outputSchema"){if(!$||typeof $!=="object"||Array.isArray($))throw Error(`${J} must be a JSON Schema object.`)}class w extends Error{}var i1=/^[A-Za-z_][A-Za-z0-9_]*$/,a1=/^[A-Za-z_][A-Za-z0-9_]*$/,F$=/\{([A-Za-z_][A-Za-z0-9_]*)(?:\.([^{}]+))?\}/g,r1=new Set(["task","previous","chain_dir","outputs"]),D0=new Set(["expand","parallel","collect","concurrency","failFast","phase","label","acceptance"]),t1=new Set([...D0,"effectiveAcceptance","sessionFiles","thinkingOverrides"]),e1=new Set(["from","item","key","maxItems","onEmpty"]),$J=new Set(["output","path"]),S0=new Set(["agent","task","phase","label","outputSchema","cwd","output","outputMode","reads","progress","skill","model","toolBudget","acceptance"]),JJ=new Set([...S0,"outputName","structured","inheritProjectContext","inheritSkills","skills","outputPath","maxSubagentDepth","structuredOutput","structuredOutputSchema","tools","extensions","subagentOnlyExtensions","mcpDirectTools","completionGuard","systemPrompt","systemPromptMode","thinking","modelCandidates","sessionFile","effectiveAcceptance","parentSessionId"]),BJ=new Set(["as","outputSchema"]);function E0($){return i1.test($)}function I0($,J){if($==="")return;if(!$.startsWith("/"))throw new w(`${J} must be a JSON Pointer starting with '/'.`);for(let B of $.slice(1).split("/"))if(/~(?![01])/.test(B))throw new w(`${J} contains invalid JSON Pointer escape.`)}function G$($,J,B){if(!$||typeof $!=="object"||Array.isArray($))throw new w(`${B} must be an object.`);for(let W of Object.keys($))if(!J.has(W))throw new w(`${B} does not support field '${W}'.`)}function WJ($,J,B){for(let W of $.matchAll(/\{([^{}]*)\}/g)){let Z=W[0],Q=W[1];if(Q===J||Q.startsWith(`${J}.`)){if(!F$.test(Z)||Q===`${J}.`||Q.includes(".."))throw new w(`Invalid item reference '${Z}' in ${B}.`);F$.lastIndex=0;continue}F$.lastIndex=0;let U=Q.match(/^[A-Za-z_][A-Za-z0-9_]*/)?.[0];if(U===J)throw new w(`Invalid item reference '${Z}' in ${B}.`);if(U&&r1.has(U))continue;if(U)throw new w(`Unsupported template reference '${Z}' in ${B}.`)}if(F$.lastIndex=0,$.includes(`{${J}.}`)||new RegExp(`\\{${J}(?:\\.|$)[^}]*$`).test($))throw new w(`Invalid item reference in ${B}.`)}function y0($){return!!$&&typeof $==="object"&&!Array.isArray($)&&(Object.prototype.hasOwnProperty.call($,"expand")||Object.prototype.hasOwnProperty.call($,"collect"))}function k0($,J,B={}){let W=`Dynamic chain step ${J+1}`;if(G$($,B.allowRunnerFields?t1:D0,W),!$.expand||!$.expand.from)throw new w(`${W} requires expand.from.`);if(G$($.expand,e1,`${W} expand`),G$($.expand.from,$J,`${W} expand.from`),!E0($.expand.from.output))throw new w(`${W} has invalid expand.from.output '${$.expand.from.output}'.`);if(I0($.expand.from.path,`${W} expand.from.path`),$.expand.key!==void 0)I0($.expand.key,`${W} expand.key`);let Z=$.expand.item??"item";if(!a1.test(Z))throw new w(`${W} has invalid expand.item '${Z}'.`);if($.expand.maxItems===void 0&&B.maxItems===void 0)throw new w(`${W} requires expand.maxItems or config.chain.dynamicFanout.maxItems.`);if($.expand.maxItems!==void 0&&(!Number.isInteger($.expand.maxItems)||$.expand.maxItems<0))throw new w(`${W} expand.maxItems must be an integer >= 0.`);if(B.maxItems!==void 0&&(!Number.isInteger(B.maxItems)||B.maxItems<0))throw new w("config.chain.dynamicFanout.maxItems must be an integer >= 0.");if(!$.parallel||Array.isArray($.parallel))throw new w(`${W} requires a single parallel template object and cannot mix dynamic expand/collect with static parallel arrays.`);if(G$($.parallel,B.allowRunnerFields?JJ:S0,`${W} parallel`),"expand"in $.parallel)throw new w(`${W} does not support nested dynamic fanout.`);if(!$.parallel.agent)throw new w(`${W} parallel.agent is required.`);if(!$.collect?.as||!E0($.collect.as))throw new w(`${W} requires collect.as with a safe output name.`);G$($.collect,BJ,`${W} collect`);for(let[Q,U]of[["parallel.task",$.parallel.task],["parallel.label",$.parallel.label]])if(U)WJ(U,Z,`${W} ${Q}`)}var ZJ=/\{outputs\.([^}]*)\}/g,b0=/^[A-Za-z_][A-Za-z0-9_]*$/;class f extends Error{}function f0($){if(i($))return $.parallel.map((B)=>B.as).filter((B)=>Boolean(B));if(d($))return[$.collect.as];let J=$.as;return J?[J]:[]}function QJ($){if(i($))return $.parallel.map((J)=>J.task??"{previous}");if(d($))return[$.parallel.task??"{previous}",$.parallel.label??""].filter(Boolean);return[$.task??"{previous}"]}function P0($,J={}){UJ($,J)}function UJ($,J={},B={}){let W=[...B.priorOutputNames??[]],Z=new Set(W),Q=new Set(W);for(let U=0;U<$.length;U++){let X=(B.startStepIndex??0)+U+1,G=$[U];if(y0(G)){if(!d(G))throw new f(`Dynamic chain step ${X} requires expand, a single parallel template object, and collect; dynamic expand/collect cannot be mixed with static parallel arrays.`);try{k0(G,X-1,J)}catch(H){if(H instanceof w)throw new f(H.message);throw H}if(!Z.has(G.expand.from.output))throw new f(`Dynamic chain step ${X} references unknown output '${G.expand.from.output}'. Named outputs are only available after producing step/group completes.`)}for(let H of f0(G)){if(!b0.test(H))throw new f(`Invalid chain output name '${H}' at step ${X}. Use /^[A-Za-z_][A-Za-z0-9_]*$/.`);if(Q.has(H))throw new f(`Duplicate chain output name '${H}'. Each as name must be unique.`);Q.add(H)}for(let H of QJ(G))for(let V of H.matchAll(ZJ)){let K=V[0],Y=V[1];if(!b0.test(Y))throw new f(`Invalid chain output reference '${K}' at step ${X}. Use {outputs.name} with /^[A-Za-z_][A-Za-z0-9_]*$/ names.`);if(!Z.has(Y))throw new f(`Unknown chain output reference '${K}' at step ${X}. Named outputs are only available after producing step/group completes.`)}for(let H of f0(G))Z.add(H)}}var x0=new Set(["auto","none","attested","checked","verified","reviewed"]),h0=new Set(["changed-files","tests-added","commands-run","validation-output","residual-risks","no-staged-files","diff-summary","review-findings","manual-notes"]),XJ=new Set(["level","criteria","evidence","verify","review","stopRules","reason"]),GJ=new Set(["id","must","evidence","severity"]),YJ=new Set(["id","command","timeoutMs","cwd","env","allowFailure"]),VJ=new Set(["agent","focus","required"]);function e($,J="acceptance"){let B=[];if($===void 0)return B;if($===!1)return B;if(typeof $==="string"){if(!x0.has($))B.push(`${J} has invalid level '${$}'.`);return B}if(!$||typeof $!=="object"||Array.isArray($))return B.push(`${J} must be a string level, false, or an object.`),B;let W=$;for(let Z of Object.keys(W))if(!XJ.has(Z))B.push(`${J}.${Z} is not supported.`);if(W.level!==void 0&&(typeof W.level!=="string"||!x0.has(W.level)))B.push(`${J}.level must be one of auto, none, attested, checked, verified, reviewed.`);if(W.level==="none"&&(typeof W.reason!=="string"||!W.reason.trim()))B.push(`${J}.reason is required when level is none.`);if(W.reason!==void 0&&typeof W.reason!=="string")B.push(`${J}.reason must be a string.`);if(W.criteria!==void 0&&!Array.isArray(W.criteria))B.push(`${J}.criteria must be an array.`);if(Array.isArray(W.criteria))for(let[Z,Q]of W.criteria.entries()){if(typeof Q==="string")continue;let U=`${J}.criteria[${Z}]`;if(!Q||typeof Q!=="object"||Array.isArray(Q)){B.push(`${U} must be a string or an object.`);continue}let X=Q;for(let G of Object.keys(X))if(!GJ.has(G))B.push(`${U}.${G} is not supported.`);if(typeof X.id!=="string"||!X.id.trim())B.push(`${U}.id is required.`);if(typeof X.must!=="string"||!X.must.trim())B.push(`${U}.must is required.`);if(X.evidence!==void 0&&!Array.isArray(X.evidence))B.push(`${U}.evidence must be an array.`);if(Array.isArray(X.evidence)){for(let[G,H]of X.evidence.entries())if(typeof H!=="string"||!h0.has(H))B.push(`${U}.evidence[${G}] is not a supported evidence kind.`)}if(X.severity!==void 0&&X.severity!=="required"&&X.severity!=="recommended")B.push(`${U}.severity must be required or recommended.`)}if(Array.isArray(W.evidence)){for(let[Z,Q]of W.evidence.entries())if(typeof Q!=="string"||!h0.has(Q))B.push(`${J}.evidence[${Z}] is not a supported evidence kind.`)}else if(W.evidence!==void 0)B.push(`${J}.evidence must be an array.`);if(W.verify!==void 0&&!Array.isArray(W.verify))B.push(`${J}.verify must be an array.`);if(Array.isArray(W.verify))for(let[Z,Q]of W.verify.entries()){if(!Q||typeof Q!=="object"||Array.isArray(Q)){B.push(`${J}.verify[${Z}] must be an object.`);continue}let U=Q;for(let X of Object.keys(U))if(!YJ.has(X))B.push(`${J}.verify[${Z}].${X} is not supported.`);if(typeof U.id!=="string"||!U.id.trim())B.push(`${J}.verify[${Z}].id is required.`);if(typeof U.command!=="string"||!U.command.trim())B.push(`${J}.verify[${Z}].command is required.`);if(U.timeoutMs!==void 0&&(typeof U.timeoutMs!=="number"||!Number.isInteger(U.timeoutMs)||U.timeoutMs<1))B.push(`${J}.verify[${Z}].timeoutMs must be an integer >= 1.`);if(U.cwd!==void 0&&typeof U.cwd!=="string")B.push(`${J}.verify[${Z}].cwd must be a string.`);if(U.env!==void 0){if(!U.env||typeof U.env!=="object"||Array.isArray(U.env))B.push(`${J}.verify[${Z}].env must be an object.`);else for(let[X,G]of Object.entries(U.env))if(typeof G!=="string")B.push(`${J}.verify[${Z}].env.${X} must be a string.`)}if(U.allowFailure!==void 0&&typeof U.allowFailure!=="boolean")B.push(`${J}.verify[${Z}].allowFailure must be a boolean.`)}if(W.review!==void 0&&W.review!==!1)if(!W.review||typeof W.review!=="object"||Array.isArray(W.review))B.push(`${J}.review must be false or an object.`);else{let Z=W.review;for(let Q of Object.keys(Z))if(!VJ.has(Q))B.push(`${J}.review.${Q} is not supported.`);if(Z.agent!==void 0&&typeof Z.agent!=="string")B.push(`${J}.review.agent must be a string.`);if(Z.focus!==void 0&&typeof Z.focus!=="string")B.push(`${J}.review.focus must be a string.`);if(Z.required!==void 0&&typeof Z.required!=="boolean")B.push(`${J}.review.required must be a boolean.`)}if(W.stopRules!==void 0&&!Array.isArray(W.stopRules))B.push(`${J}.stopRules must be an array.`);if(Array.isArray(W.stopRules)){for(let[Z,Q]of W.stopRules.entries())if(typeof Q!=="string")B.push(`${J}.stopRules[${Z}] must be a string.`)}return B}var HJ=["read","grep","find","ls"];function KJ($){if($==="*")return"*";if($===void 0)return[...HJ];return[...new Set($.map((J)=>J.trim()).filter(Boolean))]}function d$($,J="toolBudget"){if($===void 0)return{};if(!$||typeof $!=="object"||Array.isArray($))return{error:`${J} must be an object with hard and optional soft/block.`};let B=$;if(typeof B.hard!=="number"||!Number.isInteger(B.hard)||B.hard<1)return{error:`${J}.hard must be an integer >= 1.`};if(B.soft!==void 0&&(typeof B.soft!=="number"||!Number.isInteger(B.soft)||B.soft<1))return{error:`${J}.soft must be an integer >= 1 when provided.`};if(B.soft!==void 0&&B.soft>B.hard)return{error:`${J}.soft must be <= ${J}.hard.`};if(B.block!==void 0&&B.block!=="*"){if(!Array.isArray(B.block))return{error:`${J}.block must be "*" or an array of tool names.`};if(B.block.length===0)return{error:`${J}.block must contain at least one tool name.`};for(let W of B.block)if(typeof W!=="string"||!W.trim())return{error:`${J}.block must contain non-empty tool names.`}}return{budget:{hard:B.hard,...B.soft!==void 0?{soft:B.soft}:{},block:KJ(B.block)}}}function zJ($,J){let B=J.split(`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
`)){let X=U.match(/^\s*([\w-]+):\s*(.*)$/);if(!X)continue;J.set(X[1],c0(X[2]))}let Z=J.get("scope"),Q=J.get("path");if(Z!=="project"&&Z!=="user")return;if(!Q)return;return{scope:Z,path:Q}}var s$=["context-builder","delegate","oracle","planner","researcher","reviewer","scout","worker"];function _J($){return $==="delegate"?"append":"replace"}function AJ($){return $==="delegate"}function jJ(){return!1}var M$={overrides:{}},T$=new WeakMap;function NJ(){return C.join(T(),"chains")}var V$=null;function OJ($){try{return JSON.parse(F.readFileSync($,"utf-8"))}catch{return null}}function wJ($){try{return JSON.parse(F.readFileSync($,"utf-8"))}catch(J){if((typeof J==="object"&&J!==null&&"code"in J?J.code:void 0)==="ENOENT")return null;throw J}}function l$($){return $.length>0&&!C.isAbsolute($)&&$.split(/[\\/]/).every((J)=>J.length>0&&J!=="."&&J!=="..")}function FJ($){let J=$.slice(4).trim();if(!J)return;let W=J.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/)?.[1]??J;return l$(W)?W:void 0}function LJ($){let J=$.indexOf("@"),B=$.indexOf("#"),W=[J,B].filter((Z)=>Z>=0).sort((Z,Q)=>Z-Q)[0];return W===void 0?$:$.slice(0,W)}function RJ($){let J=$.slice(4).trim();if(!J)return;let B="",W="",Z=J.match(/^git@([^:]+):(.+)$/);if(Z)B=Z[1]??"",W=Z[2]??"";else if(/^[a-z][a-z0-9+.-]*:\/\//i.test(J))try{let U=new URL(J);B=U.hostname,W=U.pathname.replace(/^\/+/,"")}catch{return}else{let U=J.indexOf("/");if(U<0)return;B=J.slice(0,U),W=J.slice(U+1)}let Q=LJ(W).replace(/\.git$/,"").replace(/^\/+/,"");if(!B||!l$(B)||!l$(Q)||Q.split(/[\\/]/).length<2)return;return{host:B,repoPath:Q}}function MJ($,J){let B=$.trim();if(!B)return;if(B.startsWith("git:")){let Z=RJ(B);return Z?C.join(J,"git",Z.host,Z.repoPath):void 0}if(B.startsWith("npm:")){let Z=FJ(B);return Z?C.join(J,"npm","node_modules",Z):void 0}let W=B.startsWith("file:")?B.slice(5):B;if(W==="~")return H$.homedir();if(W.startsWith("~/"))return C.join(H$.homedir(),W.slice(2));if(C.isAbsolute(W))return W;if(W==="."||W===".."||W.startsWith("./")||W.startsWith("../"))return C.resolve(J,W);return}function TJ(){if(V$!==null)return V$;try{return V$=F.realpathSync(qJ("npm root -g",{encoding:"utf-8",timeout:5000}).trim()),V$}catch{return V$="",null}}function n0($){if(!Array.isArray($))return[];return $.filter((J)=>typeof J==="string"&&J.trim().length>0)}function EJ($){let J=C.join($,"package.json"),B=OJ(J);if(!B||typeof B!=="object"||Array.isArray(B))return{agents:[],chains:[]};let W=[],Z=B["dm-subagents"];if(Z&&typeof Z==="object"&&!Array.isArray(Z))W.push(Z);let Q=B.pi;if(Q&&typeof Q==="object"&&!Array.isArray(Q)){let G=Q.subagents;if(G&&typeof G==="object"&&!Array.isArray(G))W.push(G)}let U=[],X=[];for(let G of W){for(let H of n0(G.agents))U.push(C.resolve($,H));for(let H of n0(G.chains))X.push(C.resolve($,H))}return{agents:U,chains:X}}function p$($){let J=[];if(!F.existsSync($))return J;let B;try{B=F.readdirSync($,{withFileTypes:!0})}catch{return J}for(let W of B){if(W.name.startsWith("."))continue;if(!W.isDirectory()&&!W.isSymbolicLink())continue;if(W.name.startsWith("@")){let Z=C.join($,W.name),Q;try{Q=F.readdirSync(Z,{withFileTypes:!0})}catch{continue}for(let U of Q){if(U.name.startsWith("."))continue;if(!U.isDirectory()&&!U.isSymbolicLink())continue;J.push(C.join(Z,U.name))}continue}J.push(C.join($,W.name))}return J}function p0($,J){let B=wJ($);if(!B||typeof B!=="object"||Array.isArray(B))return[];let W=B.packages;if(!Array.isArray(W))return[];let Z=[];for(let Q of W){let U=typeof Q==="string"?Q:typeof Q==="object"&&Q!==null&&typeof Q.source==="string"?Q.source:void 0;if(!U)continue;let X=MJ(U,J);if(X)Z.push(X)}return Z}function s0($,J={includeUser:!0,includeProject:!0}){let B=T(),W=Y$($)??$,Z=[W];if(J.includeProject){let V=b(W);Z.push(...p$(C.join(V,"npm","node_modules")),...p0(C.join(V,"settings.json"),V))}if(J.includeUser)Z.push(...p$(C.join(B,"npm","node_modules")),...p0(C.join(B,"settings.json"),B));if(J.includeUser){let V=TJ();if(V)Z.push(...p$(V))}let Q=new Set,U=new Set,X=new Set,G=[],H=[];for(let V of Z){let K=C.resolve(V);if(Q.has(K))continue;Q.add(K);let Y=EJ(K);for(let z of Y.agents){if(U.has(z))continue;U.add(z),G.push(z)}for(let z of Y.chains){if(X.has(z))continue;X.add(z),H.push(z)}}return{agents:G,chains:H}}function i0($){let J=[],B=[];for(let W of $??[])if(W.startsWith("mcp:"))J.push(W.slice(4));else B.push(W);return{...B.length>0?{tools:B}:{},...J.length>0?{mcpDirectTools:J}:{}}}function i$($){return{model:$.model,fallbackModels:$.fallbackModels?[...$.fallbackModels]:void 0,thinking:$.thinking,systemPromptMode:$.systemPromptMode,inheritProjectContext:$.inheritProjectContext,inheritSkills:$.inheritSkills,defaultContext:$.defaultContext,disabled:$.disabled,systemPrompt:$.systemPrompt,skills:$.skills?[...$.skills]:void 0,tools:$.tools?[...$.tools]:void 0,mcpDirectTools:$.mcpDirectTools?[...$.mcpDirectTools]:void 0,subagentOnlyExtensions:$.subagentOnlyExtensions?[...$.subagentOnlyExtensions]:void 0,completionGuard:$.completionGuard,toolBudget:$.toolBudget}}function Y$($){let J=$;while(!0){if(K$(b(J))||K$(C.join(J,".agents")))return J;let B=C.dirname(J);if(B===J)return null;J=B}}function a0(){return C.join(T(),"settings.json")}function r0($){let J=Y$($);return J?C.join(b(J),"settings.json"):null}function IJ($){if(!F.existsSync($))return{};let J;try{J=F.readFileSync($,"utf-8")}catch(W){let Z=W instanceof Error?W.message:String(W);throw Error(`Failed to read settings file '${$}': ${Z}`,{cause:W})}let B;try{B=JSON.parse(J)}catch(W){let Z=W instanceof Error?W.message:String(W);throw Error(`Failed to parse settings file '${$}': ${Z}`,{cause:W})}if(!B||typeof B!=="object"||Array.isArray(B))throw Error(`Settings file '${$}' must contain a JSON object.`);return B}function L$($,J){if($===void 0)return;if($===!1)return!1;if(!Array.isArray($))throw Error(`Builtin override '${J.name}' in '${J.filePath}' has invalid '${J.field}'; expected an array of strings or false.`);let B=[];for(let W of $){if(typeof W!=="string")throw Error(`Builtin override '${J.name}' in '${J.filePath}' has invalid '${J.field}'; expected an array of strings or false.`);let Z=W.trim();if(Z)B.push(Z)}return B}function DJ($,J,B){if(!J||typeof J!=="object"||Array.isArray(J))throw Error(`Builtin override '${$}' in '${B}' must be an object.`);let W=J,Z={};if("model"in W)if(typeof W.model==="string"||W.model===!1)Z.model=W.model;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'model'; expected a string or false.`);if("thinking"in W)if(typeof W.thinking==="string"||W.thinking===!1)Z.thinking=W.thinking;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'thinking'; expected a string or false.`);if("systemPromptMode"in W)if(W.systemPromptMode==="append"||W.systemPromptMode==="replace")Z.systemPromptMode=W.systemPromptMode;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if("inheritProjectContext"in W)if(typeof W.inheritProjectContext==="boolean")Z.inheritProjectContext=W.inheritProjectContext;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'inheritProjectContext'; expected a boolean.`);if("inheritSkills"in W)if(typeof W.inheritSkills==="boolean")Z.inheritSkills=W.inheritSkills;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'inheritSkills'; expected a boolean.`);if("defaultContext"in W)if(W.defaultContext==="fresh"||W.defaultContext==="fork"||W.defaultContext===!1)Z.defaultContext=W.defaultContext;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if("disabled"in W)if(typeof W.disabled==="boolean")Z.disabled=W.disabled;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'disabled'; expected a boolean.`);if("completionGuard"in W)if(typeof W.completionGuard==="boolean")Z.completionGuard=W.completionGuard;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'completionGuard'; expected a boolean.`);if("toolBudget"in W)if(W.toolBudget===!1)Z.toolBudget=!1;else if(W.toolBudget&&typeof W.toolBudget==="object"&&!Array.isArray(W.toolBudget))Z.toolBudget=W.toolBudget;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'toolBudget'; expected an object or false.`);if("systemPrompt"in W)if(typeof W.systemPrompt==="string")Z.systemPrompt=W.systemPrompt;else throw Error(`Builtin override '${$}' in '${B}' has invalid 'systemPrompt'; expected a string.`);let Q=L$(W.fallbackModels,{filePath:B,name:$,field:"fallbackModels"});if(Q!==void 0)Z.fallbackModels=Q;let U=L$(W.skills,{filePath:B,name:$,field:"skills"});if(U!==void 0)Z.skills=U;let X=L$(W.tools,{filePath:B,name:$,field:"tools"});if(X!==void 0)Z.tools=X;let G=L$(W.subagentOnlyExtensions,{filePath:B,name:$,field:"subagentOnlyExtensions"});if(G!==void 0)Z.subagentOnlyExtensions=G;return Object.keys(Z).length>0?Z:void 0}function E$($){if(!$)return M$;let B=IJ($).subagents;if(!B||typeof B!=="object"||Array.isArray(B))return M$;let W=B,Z;if("disableBuiltins"in W)if(typeof W.disableBuiltins==="boolean")Z=W.disableBuiltins;else throw Error(`Subagent settings in '${$}' have invalid 'disableBuiltins'; expected a boolean.`);let Q;if("disableThinking"in W)if(typeof W.disableThinking==="boolean")Q=W.disableThinking;else throw Error(`Subagent settings in '${$}' have invalid 'disableThinking'; expected a boolean.`);let U;if("defaultModel"in W)if(typeof W.defaultModel==="string"&&W.defaultModel.trim())U=W.defaultModel.trim();else throw Error(`Subagent settings in '${$}' have invalid 'defaultModel'; expected a non-empty string.`);let X=m0(W.modelScope,{filePath:$}),G={},H=W.agentOverrides;if(!H||typeof H!=="object"||Array.isArray(H))return{overrides:G,defaultModel:U,disableBuiltins:Z,disableThinking:Q,modelScope:X};for(let[V,K]of Object.entries(H)){let Y=DJ(V,K,$);if(Y)G[V]=Y}return{overrides:G,defaultModel:U,disableBuiltins:Z,disableThinking:Q,modelScope:X}}function t0($,J,B,W){if(W&&J.defaultModel!==void 0)return{type:"subagents.defaultModel",scope:"project",path:W,model:J.defaultModel};return $.defaultModel!==void 0?{type:"subagents.defaultModel",scope:"user",path:B,model:$.defaultModel}:void 0}function m($,J){if(!J)return $;return $.map((B)=>{if(B.model!==void 0)return B;let W={...B,model:J.model,modelSource:J},Z=T$.get(B);if(Z)T$.set(W,Z);return W})}function R$($,J,B){let W={...$,override:{...B,base:i$($)}};if(J.model!==void 0)W.model=J.model===!1?void 0:J.model;if(J.fallbackModels!==void 0)W.fallbackModels=J.fallbackModels===!1?void 0:[...J.fallbackModels];if(J.thinking!==void 0)W.thinking=J.thinking===!1?void 0:J.thinking;if(J.systemPromptMode!==void 0)W.systemPromptMode=J.systemPromptMode;if(J.inheritProjectContext!==void 0)W.inheritProjectContext=J.inheritProjectContext;if(J.inheritSkills!==void 0)W.inheritSkills=J.inheritSkills;if(J.defaultContext!==void 0)W.defaultContext=J.defaultContext===!1?void 0:J.defaultContext;if(J.disabled!==void 0)W.disabled=J.disabled;if(J.systemPrompt!==void 0)W.systemPrompt=J.systemPrompt;if(J.skills!==void 0)W.skills=J.skills===!1?void 0:[...J.skills];if(J.tools!==void 0){let{tools:Z,mcpDirectTools:Q}=i0(J.tools===!1?[]:J.tools);W.tools=Z,W.mcpDirectTools=Q}if(J.subagentOnlyExtensions!==void 0)W.subagentOnlyExtensions=J.subagentOnlyExtensions===!1?void 0:[...J.subagentOnlyExtensions];if(J.completionGuard!==void 0)W.completionGuard=J.completionGuard;if(J.toolBudget!==void 0)W.toolBudget=J.toolBudget===!1?void 0:J.toolBudget;return W}function SJ($,J){if($.thinking===void 0)return $;return{...$,thinking:void 0,override:$.override??{...J,base:i$($)}}}function e0($,J,B,W,Z){let Q=B.disableBuiltins===!0&&Z!==null,U=B.disableBuiltins===void 0&&J.disableBuiltins===!0,X=B.disableThinking!==void 0&&Z!==null,G=X?B.disableThinking===!0:J.disableThinking===!0,H=X?{scope:"project",path:Z}:{scope:"user",path:W},V=(K,Y)=>{if(!G||Y)return K;return SJ(K,H)};return $.map((K)=>{let Y=B.overrides[K.name];if(Y&&Z)return V(R$(K,Y,{scope:"project",path:Z}),Y.thinking!==void 0);if(Q&&Z)return V(R$(K,{disabled:!0},{scope:"project",path:Z}),!1);let z=J.overrides[K.name];if(z)return V(R$(K,z,{scope:"user",path:W}),!X&&z.thinking!==void 0);if(U)return V(R$(K,{disabled:!0},{scope:"user",path:W}),!1);return V(K,!1)})}function o0($,...J){let B=T$.get($);return B?J.some((W)=>B.has(W)):!1}function l0($,J,B){let W,Z=!1,Q=()=>{return W??={...$},W},U=(X,G,H)=>{if(o0($,...G))return;Q()[X]=H,Z=!0};if(J.model!==void 0)U("model",["model"],J.model===!1?void 0:J.model);if(J.fallbackModels!==void 0)U("fallbackModels",["fallbackModels"],J.fallbackModels===!1?void 0:[...J.fallbackModels]);if(J.thinking!==void 0)U("thinking",["thinking"],J.thinking===!1?void 0:J.thinking);if(J.systemPromptMode!==void 0)U("systemPromptMode",["systemPromptMode"],J.systemPromptMode);if(J.inheritProjectContext!==void 0)U("inheritProjectContext",["inheritProjectContext"],J.inheritProjectContext);if(J.inheritSkills!==void 0)U("inheritSkills",["inheritSkills"],J.inheritSkills);if(J.defaultContext!==void 0)U("defaultContext",["defaultContext"],J.defaultContext===!1?void 0:J.defaultContext);if(J.disabled!==void 0&&$.disabled===void 0)Q().disabled=J.disabled,Z=!0;if(J.skills!==void 0)U("skills",["skill","skills"],J.skills===!1?void 0:[...J.skills]);if(J.tools!==void 0&&!o0($,"tools")){let{tools:X,mcpDirectTools:G}=i0(J.tools===!1?[]:J.tools),H=Q();H.tools=X,H.mcpDirectTools=G,Z=!0}if(J.subagentOnlyExtensions!==void 0)U("subagentOnlyExtensions",["subagentOnlyExtensions"],J.subagentOnlyExtensions===!1?void 0:[...J.subagentOnlyExtensions]);if(J.completionGuard!==void 0)U("completionGuard",["completionGuard"],J.completionGuard);if(J.toolBudget!==void 0)U("toolBudget",["toolBudget"],J.toolBudget===!1?void 0:J.toolBudget);if(!Z||!W)return $;return W.override={...B,base:i$($)},W}function $$($,J,B,W,Z){return $.map((Q)=>{let U=B.overrides[Q.name];if(U&&Z)return l0(Q,U,{scope:"project",path:Z});let X=J.overrides[Q.name];if(X)return l0(Q,X,{scope:"user",path:W});return Q})}function a$($,J){let B=[];if(!F.existsSync($))return B;let W;try{W=F.readdirSync($,{withFileTypes:!0}).sort((Z,Q)=>Z.name.localeCompare(Q.name))}catch{return B}for(let Z of W){let Q=C.join($,Z.name);if(Z.isDirectory()){B.push(...a$(Q,J));continue}if(!Z.isFile()&&!Z.isSymbolicLink())continue;if(!J(Z.name))continue;B.push(Q)}return B}function yJ($,J){let W=C.relative($,J).split(C.sep).map((Z)=>Z.toLowerCase());if(C.basename($).toLowerCase()===".agents")W.unshift(".agents");return W.some((Z,Q)=>Z===".agents"&&W[Q+1]==="skills")}function D($,J){let B=[];for(let W of a$($,(Z)=>Z.endsWith(".md")&&!Z.endsWith(".chain.md"))){if(yJ($,W))continue;let Z;try{Z=F.readFileSync(W,"utf-8")}catch{continue}let{frontmatter:Q,body:U}=t(Z);if(!Q.name||!Q.description)continue;let X=Q.name,G=a(Q.package,`Agent '${X}' package`);if(G.error)continue;let H=G.packageName,V=r(X,H),K=Q.tools?.split(",").map((_)=>_.trim()).filter(Boolean),Y=[],z=[];if(K)for(let _ of K)if(_.startsWith("mcp:"))Y.push(_.slice(4));else z.push(_);let q=Q.defaultReads?.split(",").map((_)=>_.trim()).filter(Boolean),j=(Q.skill||Q.skills)?.split(",").map((_)=>_.trim()).filter(Boolean),O=Q.fallbackModels?.split(",").map((_)=>_.trim()).filter(Boolean),N=Q.systemPromptMode==="replace"?"replace":Q.systemPromptMode==="append"?"append":_J(X),L=Q.inheritProjectContext==="true"?!0:Q.inheritProjectContext==="false"?!1:AJ(X),h=Q.inheritSkills==="true"?!0:Q.inheritSkills==="false"?!1:jJ(),R=Q.defaultContext==="fork"?"fork":Q.defaultContext==="fresh"?"fresh":void 0,k;if(Q.extensions!==void 0)k=Q.extensions.split(",").map((_)=>_.trim()).filter(Boolean);let c;if(Q.subagentOnlyExtensions!==void 0)c=Q.subagentOnlyExtensions.split(",").map((_)=>_.trim()).filter(Boolean);let p={};for(let[_,u]of Object.entries(Q))if(!M0.has(_))p[_]=u;let B$=Number(Q.maxSubagentDepth),A$;if(Q.toolBudget!==void 0&&Q.toolBudget.trim()){let _=JSON.parse(Q.toolBudget);if(!_||typeof _!=="object"||Array.isArray(_))throw Error(`Agent '${X}' has invalid toolBudget frontmatter; expected a JSON object.`);A$=_}let c$=Q.completionGuard==="false"?!1:Q.completionGuard==="true"?!0:void 0,E={name:V,localName:X,packageName:H,description:Q.description,tools:z.length>0?z:void 0,mcpDirectTools:Y.length>0?Y:void 0,model:Q.model,fallbackModels:O&&O.length>0?O:void 0,thinking:Q.thinking,systemPromptMode:N,inheritProjectContext:L,inheritSkills:h,defaultContext:R,systemPrompt:U,source:J,filePath:W,skills:j&&j.length>0?j:void 0,extensions:k,subagentOnlyExtensions:c,output:Q.output,defaultReads:q&&q.length>0?q:void 0,defaultProgress:Q.defaultProgress==="true",interactive:Q.interactive==="true",maxSubagentDepth:Number.isInteger(B$)&&B$>=0?B$:void 0,completionGuard:c$,toolBudget:A$,memory:d0(Q.memory),extraFields:Object.keys(p).length>0?p:void 0};T$.set(E,new Set(Object.keys(Q))),B.push(E)}return B}function o$($,J){let B=new Map,W=[];for(let Z of a$($,(Q)=>Q.endsWith(".chain.md")||Q.endsWith(".chain.json"))){let Q;try{Q=F.readFileSync(Z,"utf-8")}catch{continue}try{let U=Z.endsWith(".chain.json")?g0(Q,J,Z):v0(Q,J,Z),X=B.get(U.name);if(X&&X.filePath.endsWith(".chain.json")&&Z.endsWith(".chain.md"))continue;B.set(U.name,U)}catch(U){W.push({source:J,filePath:Z,error:U instanceof Error?U.message:String(U)});continue}}return{chains:Array.from(B.values()),diagnostics:W}}function K$($){try{return F.statSync($).isDirectory()}catch{return!1}}function $1($){let J=Y$($);if(!J)return{readDirs:[],preferredDir:null};let B=C.join(J,".agents"),W=C.join(b(J),"agents"),Z=[];if(K$(B))Z.push(B);if(K$(W))Z.push(W);return{readDirs:Z,preferredDir:W}}function kJ($){let J=Y$($);if(!J)return{readDirs:[],preferredDir:null};let B=C.join(b(J),"chains");return{readDirs:K$(B)?[B]:[],preferredDir:B}}var J1=C.resolve(C.dirname(CJ(import.meta.url)),"..","..","agents"),bJ="DM_SUBAGENT_EXTRA_AGENT_DIRS";function B1(){let $=process.env[bJ];if(!$)return[];return $.split(C.delimiter).map((J)=>J.trim()).filter((J)=>J.length>0)}function z$($,J){let B=C.join(T(),"agents"),W=C.join(H$.homedir(),".agents"),{readDirs:Z,preferredDir:Q}=$1($),U=a0(),X=r0($),G=J==="project"?M$:E$(U),H=J==="user"?M$:E$(X),V=t0(G,H,U,X),K=H.modelScope??G.modelScope,Y=s0($,{includeUser:J!=="project",includeProject:J!=="user"}),z=e0(m(D(J1,"builtin"),V),G,H,U,X),q=J==="project"?[]:B1().flatMap((R)=>D(R,"user")),A=J==="project"?[]:D(B,"user"),j=J==="project"?[]:D(W,"user"),O=$$(m([...q,...A,...j],V),G,H,U,X),N=$$(m(J==="user"?[]:Z.flatMap((R)=>D(R,"project")),V),G,H,U,X),L=$$(m(Y.agents.flatMap((R)=>D(R,"package")),V),G,H,U,X);return{agents:u0(J,O,N,z,L).filter((R)=>R.disabled!==!0),projectAgentsDir:Q,modelScope:K}}function W1($){let J=C.join(T(),"agents"),B=C.join(H$.homedir(),".agents"),W=NJ(),{readDirs:Z,preferredDir:Q}=$1($),{readDirs:U,preferredDir:X}=kJ($),G=a0(),H=r0($),V=E$(G),K=E$(H),Y=t0(V,K,G,H),z=s0($),q=e0(m(D(J1,"builtin"),Y),V,K,G,H),A=$$(m([...B1().flatMap((E)=>D(E,"user")),...D(J,"user"),...D(B,"user")],Y),V,K,G,H),j=new Map;for(let E of z.agents)for(let _ of D(E,"package"))if(!j.has(_.name))j.set(_.name,_);let O=$$(m(Array.from(j.values()),Y),V,K,G,H),N=new Map;for(let E of Z)for(let _ of D(E,"project"))N.set(_.name,_);let L=$$(m(Array.from(N.values()),Y),V,K,G,H),h=new Map,R=[],k=new Map;for(let E of z.chains){let _=o$(E,"package");R.push(..._.diagnostics);for(let u of _.chains)if(!k.has(u.name))k.set(u.name,u)}let c=[];for(let E of U){let _=o$(E,"project");c.push(..._.diagnostics);for(let u of _.chains)h.set(u.name,u)}let p=o$(W,"user"),B$=[...Array.from(k.values()),...p.chains,...Array.from(h.values())],A$=[...R,...p.diagnostics,...c],c$=process.env.DM_CODING_AGENT_DIR?J:F.existsSync(B)?B:J;return{builtin:q,package:O,user:A,project:L,chains:B$,chainDiagnostics:A$,userDir:c$,projectDir:Q,userChainDir:W,projectChainDir:X,userSettingsPath:G,projectSettingsPath:H}}import*as M from"node:fs";import*as G1 from"node:os";import*as P from"node:path";var q$=7;function $0($){let J=M.readFileSync($,"utf-8"),B=JSON.parse(J);if(!B||typeof B!=="object"||Array.isArray(B))throw Error(`File '${$}' must contain a JSON object.`);return B}function I$($,J){M.mkdirSync(P.dirname($),{recursive:!0}),M.writeFileSync($,`${JSON.stringify(J,null,2)}
|
|
11
|
-
|
|
12
|
-
`).trim();if(W.code===0)return{status:"ok",message:Z||"Probe succeeded."};return{status:lJ(U,W.killed===!0),message:U||`Probe exited with code ${W.code??"unknown"}.`}}function e$($,J){if($<=1)return 0;return Math.max(0,Math.min($-1,Math.round(($-1)*J)))}function sJ($){return $==="quota"?{cheap:0,medium:0.3333333333333333,strong:0.6666666666666666}:{cheap:0.3333333333333333,medium:0.6666666666666666,strong:1}}function Z1($,J){if($.length===0)throw Error("No provider models are available for profile generation.");let B=J==="quota"&&$.length>1?$.slice(0,-1):$,W=sJ(J);return{cheap:B[e$(B.length,W.cheap)].fullId,medium:B[e$(B.length,W.medium)].fullId,strong:B[e$(B.length,W.strong)].fullId}}function Q1($){return B0($.observed.cost)}function iJ($,J){let B=Q1($),W=Q1(J);if(B===void 0||W===void 0)return!1;if(B>W)return!1;if($.derived.profileRank<J.derived.profileRank)return!1;if(($.observed.reasoning===!0?1:0)<(J.observed.reasoning===!0?1:0))return!1;if(($.observed.contextWindow??0)<(J.observed.contextWindow??0))return!1;if(($.observed.maxTokens??0)<(J.observed.maxTokens??0))return!1;return B<W||$.derived.profileRank>J.derived.profileRank||$.observed.reasoning===!0&&J.observed.reasoning!==!0||($.observed.contextWindow??0)>(J.observed.contextWindow??0)||($.observed.maxTokens??0)>(J.observed.maxTokens??0)}function aJ($){return $.filter((J,B)=>!$.some((W,Z)=>Z!==B&&iJ(W,J)))}function U1($,J){return{subagents:{agentOverrides:{scout:{model:J.cheap},delegate:{model:J.cheap},planner:{model:J.medium},"context-builder":{model:J.medium},researcher:{model:J.medium},worker:{model:J.strong},reviewer:{model:J.strong},oracle:{model:J.strong}}}}}function rJ($){return $.observed.availableInRegistry&&$.observed.probe.status!=="unavailable"&&$.observed.probe.status!=="auth"&&$.observed.probe.status!=="timeout"&&$.observed.probe.status!=="error"}function K1($){return $.derived.classificationSources.includes("heuristic-name")&&!$.derived.classificationSources.includes("official-metadata")}function tJ(){return"Classification fell back to name heuristics."}function X1($){return $.models.filter(K1).length}function eJ($){let J=W0();return P.join(J,`${PJ($)}.json`)}function z1(){return P.join(T(),"profiles","dm-subagents")}function $B(){return z1()}function W0(){let $=$B();return M.mkdirSync($,{recursive:!0}),$}function JB(){return P.join(z1(),"providers")}function BB(){let $=JB();return M.mkdirSync($,{recursive:!0}),$}function q1($){return P.join(BB(),`${J0($)}.models.json`)}function D$(){let $=W0();return M.readdirSync($,{withFileTypes:!0}).filter((J)=>J.isFile()&&J.name.endsWith(".json")).map((J)=>J.name.slice(0,-5)).sort((J,B)=>J.localeCompare(B))}function S$($){let J=eJ($);if(!M.existsSync(J))throw Error(`Profile not found: ${$}`);let B=$0(J);return{filePath:J,profile:xJ(J,B)}}function C1($){let{filePath:J,profile:B}=S$($),W=hJ(),Z=vJ(W);return Z.subagents=B.subagents,I$(W,Z),{filePath:J,settingsPath:W}}function WB($){let J=q1($);if(!M.existsSync(J))return null;return $0(J)}function ZB($,J=q$){let B=Date.parse($.refreshedAt);if(!Number.isFinite(B))return!0;let W=J*24*60*60*1000;return Date.now()-B>W}async function Z0($,J,B,W={}){let Z=J0(B),Q=W.maxAgeDays??q$,U=q1(Z);if(!W.force){let Y=WB(Z);if(Y&&!ZB(Y,Q))return{filePath:U,catalog:Y,reused:!0,heuristicFallbackCount:X1(Y)}}let X=J.modelRegistry.getAvailable().filter((Y)=>Y.provider===Z);if(X.length===0)throw Error(`No models found in the current registry for provider '${Z}'.`);let G=[];for(let Y of X){let z=Y,q=`${z.provider}/${z.id}`,A=W.probe===!1?{status:"skipped",message:"Live probing disabled."}:await H1($,J,q);G.push({rawModel:Y,modelRecord:z,fullId:q,probe:A})}let H=mJ(G.map(({modelRecord:Y})=>({id:Y.id,...typeof Y.name==="string"?{name:Y.name}:{},...typeof Y.reasoning==="boolean"?{reasoning:Y.reasoning}:{},...typeof Y.contextWindow==="number"?{contextWindow:Y.contextWindow}:{},...typeof Y.maxTokens==="number"?{maxTokens:Y.maxTokens}:{},...Y.cost&&typeof Y.cost==="object"?{cost:Y.cost}:{}}))),V=[];for(let{rawModel:Y,modelRecord:z,fullId:q,probe:A}of G){let j=oJ({id:z.id,...typeof z.name==="string"?{name:z.name}:{},...typeof z.reasoning==="boolean"?{reasoning:z.reasoning}:{},...typeof z.contextWindow==="number"?{contextWindow:z.contextWindow}:{},...typeof z.maxTokens==="number"?{maxTokens:z.maxTokens}:{},...z.cost&&typeof z.cost==="object"?{cost:z.cost}:{}},H),O=j.classificationSources.includes("heuristic-name")&&!j.classificationSources.includes("official-metadata")?[tJ()]:[];V.push({id:z.id,fullId:q,observed:{availableInRegistry:!0,...typeof z.name==="string"?{name:z.name}:{},...typeof z.reasoning==="boolean"?{reasoning:z.reasoning}:{},thinkingLevels:O0(Z$(Y)).map((N)=>N),...typeof z.contextWindow==="number"?{contextWindow:z.contextWindow}:{},...typeof z.maxTokens==="number"?{maxTokens:z.maxTokens}:{},...z.cost&&typeof z.cost==="object"?{cost:z.cost}:{},probe:{status:A.status,checkedAt:new Date().toISOString(),...A.message?{message:A.message}:{}}},derived:j,warnings:O,notes:[]})}V.sort((Y,z)=>Y.derived.profileRank-z.derived.profileRank||Y.fullId.localeCompare(z.fullId));let K={provider:Z,refreshedAt:new Date().toISOString(),maxAgeDays:Q,sources:["runtime-registry",...W.probe===!1?[]:["live-probe"],"heuristic-classifier"],models:V};return I$(U,K),{filePath:U,catalog:K,reused:!1,heuristicFallbackCount:X1(K)}}async function _1($,J,B,W={}){let Z=J0(B),{filePath:Q,catalog:U,heuristicFallbackCount:X}=await Z0($,J,Z,{maxAgeDays:W.maxAgeDays,force:W.forceRefresh,probe:W.probe}),G=U.models.filter(rJ),H=aJ(G);if(H.length===0)throw Error(`Provider '${Z}' has no usable models after filtering.`);let V=Z1(H,"quota"),K=Z1(H,"quality"),Y=W0(),z=P.join(Y,`${Z}.quota.json`),q=P.join(Y,`${Z}.quality.json`);I$(z,U1("quota",V)),I$(q,U1("quality",K));let A=new Set([...Object.values(V),...Object.values(K)]),j=H.filter((O)=>A.has(O.fullId)&&K1(O)).length;return{quotaPath:z,qualityPath:q,catalogPath:Q,quotaModels:V,qualityModels:K,heuristicFallbackCount:X,selectedHeuristicFallbackCount:j}}async function A1($,J,B){let{filePath:W,profile:Z}=S$(B),Q=J.modelRegistry.getAvailable().map(Z$),U=Object.entries(Z.subagents.agentOverrides).filter(([,H])=>typeof H?.model==="string"&&H.model.trim()).map(([H,V])=>({agent:H,model:V.model.trim()})),X=new Map,G=[];for(let H of U){let V=O$(H.model,Q),{thinkingSuffix:K}=Q$(H.model),Y=V?`${V.fullId}${K}`:H.model,z=X.get(Y);if(!z)z=await H1($,J,Y),X.set(Y,z);G.push({agent:H.agent,model:H.model,inRegistry:V!==void 0,probe:z})}return{profileName:B,filePath:W,results:G}}import*as k$ from"node:fs";import*as x from"node:path";import{fileURLToPath as QB}from"node:url";var UB=new Set(["chain-prompts","prompt-workflow","run","chain","parallel","run-chain","subagents-doctor","subagents-models"]);function XB(){return x.resolve(x.dirname(QB(import.meta.url)),"..","..","prompts")}function GB($){return[XB(),x.join(T(),"prompts"),x.join(b($),"prompts")]}function YB($){let J=[];for(let B of GB($)){let W;try{W=k$.readdirSync(B,{withFileTypes:!0})}catch{continue}for(let Z of W)if(Z.isFile()&&Z.name.endsWith(".md"))J.push(x.join(B,Z.name))}return J}function VB($){return $.split(/\r?\n/).map((J)=>J.trim()).find(Boolean)??"Prompt workflow"}function J$($,J){let B=$[J]?.trim();return B?B:void 0}function y$($,J){let B=$[J]?.trim().toLowerCase();if(B==="true"||B==="yes"||B==="1")return!0;if(B==="false"||B==="no"||B==="0")return!1;return}function HB($){if(!$)return;if($==="false")return!1;let J=$.split(",").map((B)=>B.trim()).filter(Boolean);return J.length>1?J:J[0]}function KB($){let J=J$($,"subagent");if(!J||J==="true")return"delegate";return J}function zB($){let J=k$.readFileSync($,"utf-8"),{frontmatter:B,body:W}=t(J),Z=x.basename($,".md");if(!Z||UB.has(Z))return;let Q=J$(B,"model"),U=HB(J$(B,"skill")),X=J$(B,"cwd"),G=J$(B,"chain");return{name:Z,description:J$(B,"description")??VB(W),body:W,filePath:$,agent:KB(B),...y$(B,"inheritContext")===!0||y$(B,"fork")===!0?{context:"fork"}:{},...y$(B,"fresh")===!0?{context:"fresh"}:{},...Q?{model:Q}:{},...U!==void 0?{skill:U}:{},...X?{cwd:X}:{},...y$(B,"worktree")===!0?{worktree:!0}:{},...G?{chain:G}:{}}}function j1($){let J=new Map;for(let B of YB($)){let W=zB(B);if(W)J.set(W.name,W)}return[...J.values()].sort((B,W)=>B.name.localeCompare(W.name))}function N1($){let J=[],B="",W,Z=!1;for(let Q of $){if(Z){B+=Q,Z=!1;continue}if(Q==="\\"){Z=!0;continue}if(W){if(Q===W)W=void 0;else B+=Q;continue}if(Q==="'"||Q==='"'){W=Q;continue}if(/\s/.test(Q)){if(B)J.push(B),B="";continue}B+=Q}if(B)J.push(B);return J}function qB($,J){let B=J.join(" ");return $.replace(/\$ARGUMENTS/g,B).replace(/\$@/g,B).replace(/\$\{(\d+):-([^}]*)\}/g,(W,Z,Q)=>J[Number(Z)-1]||Q).replace(/\$(\d+)/g,(W,Z)=>J[Number(Z)-1]??"")}function O1($){let J=[],B,W=!1,Z=!1,Q=!1,U=!1;for(let X=0;X<$.length;X++){let G=$[X];if(G==="--fork"){W=!0;continue}if(G==="--fresh"){Z=!0;continue}if(G==="--worktree"){Q=!0;continue}if(G==="--bg"||G==="--async"){U=!0;continue}if(G==="--subagent"){B=$[++X];continue}let H=G.match(/^--subagent(?:=|:)(.+)$/);if(H){B=H[1];continue}J.push(G)}return{args:J,agentOverride:B,fork:W,fresh:Z,worktree:Q,bg:U}}function CB($){let J=$.indexOf(" -- ");if(J===-1)return{declaration:$.trim(),argsText:""};return{declaration:$.slice(0,J).trim(),argsText:$.slice(J+4).trim()}}function w1($){return $.split(" -> ").map((J)=>J.trim()).filter(Boolean)}function R1($,J,B){let W=qB($.body,J).trim(),Z=B.fork?"fork":B.fresh?"fresh":$.context;return{agent:B.agentOverride??$.agent,task:W,clarify:!1,agentScope:"both",...Z?{context:Z}:{},...$.model?{model:$.model}:{},...$.skill!==void 0?{skill:$.skill}:{},...$.cwd?{cwd:$.cwd}:{},...B.worktree||$.worktree?{worktree:!0}:{},...B.bg?{async:!0}:{}}}function F1($,J,B){let W=R1($,J,B);return{agent:W.agent??"delegate",task:W.task,...W.model?{model:W.model}:{},...W.skill!==void 0?{skill:W.skill}:{},...W.cwd?{cwd:W.cwd}:{}}}function Q0($,J){return $.find((B)=>B.name===J)}function L1($){if($.length===0)return"No prompt workflows found in package, user, or project prompts.";return["Prompt workflows:",...$.map((J)=>`- ${J.name}: ${J.description} (${J.filePath})`)].join(`
|
|
13
|
-
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { keyText } from "@duckmind/dm-coding-agent";
|
|
5
|
+
import { Key, matchesKey } from "@duckmind/dm-tui";
|
|
6
|
+
import { BUILTIN_AGENT_NAMES, discoverAgents, discoverAgentsAll } from "../agents/agents.js";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS,
|
|
9
|
+
applySubagentProfile,
|
|
10
|
+
checkSubagentProfile,
|
|
11
|
+
generateProfilesForProvider,
|
|
12
|
+
listSubagentProfiles,
|
|
13
|
+
readSubagentProfile,
|
|
14
|
+
refreshProviderModelCatalog
|
|
15
|
+
} from "../profiles/profiles.js";
|
|
16
|
+
import { isDynamicParallelStep, isParallelStep } from "../shared/settings.js";
|
|
17
|
+
import { findModelInfo, toModelInfo } from "../shared/model-info.js";
|
|
18
|
+
import { formatTokens } from "../shared/formatters.js";
|
|
19
|
+
import { assertJsonSchemaObject } from "../runs/shared/structured-output.js";
|
|
20
|
+
import { validateAcceptanceInput } from "../runs/shared/acceptance.js";
|
|
21
|
+
import { registerPromptWorkflowCommands } from "./prompt-workflows.js";
|
|
22
|
+
import {
|
|
23
|
+
applySlashUpdate,
|
|
24
|
+
buildSlashInitialResult,
|
|
25
|
+
failSlashResult,
|
|
26
|
+
finalizeSlashResult,
|
|
27
|
+
resolveSlashMessageDetails
|
|
28
|
+
} from "./slash-live-state.js";
|
|
29
|
+
import {
|
|
30
|
+
SLASH_RESULT_TYPE,
|
|
31
|
+
SLASH_TEXT_RESULT_TYPE,
|
|
32
|
+
SLASH_SUBAGENT_CANCEL_EVENT,
|
|
33
|
+
SLASH_SUBAGENT_REQUEST_EVENT,
|
|
34
|
+
SLASH_SUBAGENT_RESPONSE_EVENT,
|
|
35
|
+
SLASH_SUBAGENT_STARTED_EVENT,
|
|
36
|
+
SLASH_SUBAGENT_UPDATE_EVENT
|
|
37
|
+
} from "../shared/types.js";
|
|
38
|
+
const parseInlineConfig = (raw) => {
|
|
39
|
+
const config = {};
|
|
40
|
+
for (const part of raw.split(",")) {
|
|
41
|
+
const trimmed = part.trim();
|
|
42
|
+
if (!trimmed)
|
|
43
|
+
continue;
|
|
44
|
+
const eq = trimmed.indexOf("=");
|
|
45
|
+
if (eq === -1) {
|
|
46
|
+
if (trimmed === "progress")
|
|
47
|
+
config.progress = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const key = trimmed.slice(0, eq).trim();
|
|
51
|
+
const val = trimmed.slice(eq + 1).trim();
|
|
52
|
+
switch (key) {
|
|
53
|
+
case "output":
|
|
54
|
+
config.output = val === "false" ? false : val;
|
|
55
|
+
break;
|
|
56
|
+
case "outputMode":
|
|
57
|
+
if (val === "inline" || val === "file-only")
|
|
58
|
+
config.outputMode = val;
|
|
59
|
+
break;
|
|
60
|
+
case "reads":
|
|
61
|
+
config.reads = val === "false" ? false : val.split("+").filter(Boolean);
|
|
62
|
+
break;
|
|
63
|
+
case "model":
|
|
64
|
+
config.model = val || undefined;
|
|
65
|
+
break;
|
|
66
|
+
case "skill":
|
|
67
|
+
case "skills":
|
|
68
|
+
config.skill = val === "false" ? false : val.split("+").filter(Boolean);
|
|
69
|
+
break;
|
|
70
|
+
case "progress":
|
|
71
|
+
config.progress = val !== "false";
|
|
72
|
+
break;
|
|
73
|
+
case "as":
|
|
74
|
+
config.as = val || undefined;
|
|
75
|
+
break;
|
|
76
|
+
case "label":
|
|
77
|
+
config.label = val || undefined;
|
|
78
|
+
break;
|
|
79
|
+
case "phase":
|
|
80
|
+
config.phase = val || undefined;
|
|
81
|
+
break;
|
|
82
|
+
case "cwd":
|
|
83
|
+
config.cwd = val || undefined;
|
|
84
|
+
break;
|
|
85
|
+
case "count": {
|
|
86
|
+
const n = Number(val);
|
|
87
|
+
if (Number.isInteger(n) && n > 0)
|
|
88
|
+
config.count = n;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "outputSchema":
|
|
92
|
+
config.outputSchema = val || undefined;
|
|
93
|
+
break;
|
|
94
|
+
case "acceptance":
|
|
95
|
+
config.acceptance = val || undefined;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return config;
|
|
100
|
+
};
|
|
101
|
+
const parseAgentToken = (token) => {
|
|
102
|
+
const bracket = token.indexOf("[");
|
|
103
|
+
if (bracket === -1)
|
|
104
|
+
return { name: token, config: {} };
|
|
105
|
+
const end = token.lastIndexOf("]");
|
|
106
|
+
return { name: token.slice(0, bracket), config: parseInlineConfig(token.slice(bracket + 1, end !== -1 ? end : undefined)) };
|
|
107
|
+
};
|
|
108
|
+
const extractExecutionFlags = (rawArgs) => {
|
|
109
|
+
let args = rawArgs.trim();
|
|
110
|
+
let bg = false;
|
|
111
|
+
let fork = false;
|
|
112
|
+
while (true) {
|
|
113
|
+
if (args.endsWith(" --bg") || args === "--bg") {
|
|
114
|
+
bg = true;
|
|
115
|
+
args = args === "--bg" ? "" : args.slice(0, -5).trim();
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (args.endsWith(" --fork") || args === "--fork") {
|
|
119
|
+
fork = true;
|
|
120
|
+
args = args === "--fork" ? "" : args.slice(0, -7).trim();
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
return { args, bg, fork };
|
|
126
|
+
};
|
|
127
|
+
const makeAgentCompletions = (state, multiAgent) => (prefix) => {
|
|
128
|
+
if (!state.baseCwd)
|
|
129
|
+
return null;
|
|
130
|
+
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
131
|
+
if (!multiAgent) {
|
|
132
|
+
if (prefix.includes(" "))
|
|
133
|
+
return null;
|
|
134
|
+
return agents.filter((a) => a.name.startsWith(prefix)).map((a) => ({ value: a.name, label: a.name }));
|
|
135
|
+
}
|
|
136
|
+
let inSingle = false, inDouble = false, depth = 0, segStart = 0;
|
|
137
|
+
for (let i = 0;i < prefix.length; i++) {
|
|
138
|
+
const ch = prefix[i];
|
|
139
|
+
if (inSingle) {
|
|
140
|
+
if (ch === "'")
|
|
141
|
+
inSingle = false;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (inDouble) {
|
|
145
|
+
if (ch === '"')
|
|
146
|
+
inDouble = false;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (ch === "'") {
|
|
150
|
+
inSingle = true;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (ch === '"') {
|
|
154
|
+
inDouble = true;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (ch === "(") {
|
|
158
|
+
if (!prefix.slice(segStart, i).includes(" -- ")) {
|
|
159
|
+
depth++;
|
|
160
|
+
segStart = i + 1;
|
|
161
|
+
}
|
|
162
|
+
} else if (ch === ")") {
|
|
163
|
+
if (depth > 0) {
|
|
164
|
+
depth--;
|
|
165
|
+
segStart = i + 1;
|
|
166
|
+
}
|
|
167
|
+
} else if (ch === "|" && depth > 0)
|
|
168
|
+
segStart = i + 1;
|
|
169
|
+
else if (ch === ">" && prefix[i - 1] === "-" && depth === 0)
|
|
170
|
+
segStart = i + 1;
|
|
171
|
+
}
|
|
172
|
+
if (inSingle || inDouble)
|
|
173
|
+
return null;
|
|
174
|
+
const segment = prefix.slice(segStart);
|
|
175
|
+
if (segment.includes(" -- ") || segment.includes('"') || segment.includes("'"))
|
|
176
|
+
return null;
|
|
177
|
+
const lastWord = (segment.match(/(\S*)$/) || ["", ""])[1];
|
|
178
|
+
let beforeLastWord = prefix.slice(0, prefix.length - lastWord.length);
|
|
179
|
+
if (lastWord === "" && /[>|]$/.test(beforeLastWord))
|
|
180
|
+
beforeLastWord = `${beforeLastWord} `;
|
|
181
|
+
return agents.filter((a) => a.name.startsWith(lastWord)).map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
|
|
182
|
+
};
|
|
183
|
+
const discoverSavedChains = (cwd) => {
|
|
184
|
+
const chainsByName = new Map;
|
|
185
|
+
for (const chain of discoverAgentsAll(cwd).chains) {
|
|
186
|
+
chainsByName.set(chain.name, chain);
|
|
187
|
+
}
|
|
188
|
+
return Array.from(chainsByName.values());
|
|
189
|
+
};
|
|
190
|
+
const makeChainCompletions = (state) => (prefix) => {
|
|
191
|
+
if (prefix.includes(" ") || !state.baseCwd)
|
|
192
|
+
return null;
|
|
193
|
+
return discoverSavedChains(state.baseCwd).filter((chain) => chain.name.startsWith(prefix)).map((chain) => ({ value: chain.name, label: chain.name }));
|
|
194
|
+
};
|
|
195
|
+
const makeBuiltinAgentNameCompletions = () => (prefix) => {
|
|
196
|
+
if (prefix.includes(" "))
|
|
197
|
+
return null;
|
|
198
|
+
return BUILTIN_AGENT_NAMES.filter((name) => name.startsWith(prefix)).map((name) => ({ value: name, label: name }));
|
|
199
|
+
};
|
|
200
|
+
const makeProviderCompletions = (state) => (prefix) => {
|
|
201
|
+
if (prefix.includes(" "))
|
|
202
|
+
return null;
|
|
203
|
+
const available = state.lastUiContext?.modelRegistry?.getAvailable?.();
|
|
204
|
+
if (!Array.isArray(available))
|
|
205
|
+
return null;
|
|
206
|
+
const providers = [...new Set(available.map((model) => typeof model?.provider === "string" ? model.provider : "").filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
207
|
+
return providers.filter((provider) => provider.startsWith(prefix)).map((provider) => ({ value: provider, label: provider }));
|
|
208
|
+
};
|
|
209
|
+
function sendSlashText(pi, text) {
|
|
210
|
+
pi.sendMessage({ customType: SLASH_TEXT_RESULT_TYPE, content: text, display: true });
|
|
211
|
+
}
|
|
212
|
+
async function withSlashStatus(ctx, text, run) {
|
|
213
|
+
if (ctx.hasUI)
|
|
214
|
+
ctx.ui.setStatus("subagent-slash-text", text);
|
|
215
|
+
try {
|
|
216
|
+
return await run();
|
|
217
|
+
} finally {
|
|
218
|
+
if (ctx.hasUI)
|
|
219
|
+
ctx.ui.setStatus("subagent-slash-text", undefined);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function emptyUsage() {
|
|
223
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
224
|
+
}
|
|
225
|
+
function addUsage(target, source) {
|
|
226
|
+
target.input += source.input;
|
|
227
|
+
target.output += source.output;
|
|
228
|
+
target.cacheRead += source.cacheRead;
|
|
229
|
+
target.cacheWrite += source.cacheWrite;
|
|
230
|
+
target.cost += source.cost;
|
|
231
|
+
target.turns += source.turns;
|
|
232
|
+
}
|
|
233
|
+
function usageHasValue(usage) {
|
|
234
|
+
return usage.input !== 0 || usage.output !== 0 || usage.cacheRead !== 0 || usage.cacheWrite !== 0 || usage.cost !== 0 || usage.turns !== 0;
|
|
235
|
+
}
|
|
236
|
+
function assistantUsageFromMessage(message) {
|
|
237
|
+
if (!message || typeof message !== "object")
|
|
238
|
+
return;
|
|
239
|
+
const msg = message;
|
|
240
|
+
if (msg.role !== "assistant" || !msg.usage || typeof msg.usage !== "object")
|
|
241
|
+
return;
|
|
242
|
+
const usage = msg.usage;
|
|
243
|
+
return {
|
|
244
|
+
input: typeof usage.input === "number" ? usage.input : 0,
|
|
245
|
+
output: typeof usage.output === "number" ? usage.output : 0,
|
|
246
|
+
cacheRead: typeof usage.cacheRead === "number" ? usage.cacheRead : 0,
|
|
247
|
+
cacheWrite: typeof usage.cacheWrite === "number" ? usage.cacheWrite : 0,
|
|
248
|
+
cost: typeof usage.cost?.total === "number" ? usage.cost.total : 0,
|
|
249
|
+
turns: 1
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function isSubagentDetails(value) {
|
|
253
|
+
if (!value || typeof value !== "object")
|
|
254
|
+
return false;
|
|
255
|
+
const details = value;
|
|
256
|
+
return typeof details.mode === "string" && Array.isArray(details.results);
|
|
257
|
+
}
|
|
258
|
+
function detailsFromSessionEntry(entry) {
|
|
259
|
+
if (!entry || typeof entry !== "object")
|
|
260
|
+
return;
|
|
261
|
+
const record = entry;
|
|
262
|
+
if (record.type === "custom_message" && record.customType === SLASH_RESULT_TYPE) {
|
|
263
|
+
const details = resolveSlashMessageDetails(record.details)?.result.details;
|
|
264
|
+
return isSubagentDetails(details) ? details : undefined;
|
|
265
|
+
}
|
|
266
|
+
if (record.type !== "message" || !record.message || typeof record.message !== "object")
|
|
267
|
+
return;
|
|
268
|
+
const message = record.message;
|
|
269
|
+
if (message.role !== "toolResult" || message.toolName !== "subagent")
|
|
270
|
+
return;
|
|
271
|
+
return isSubagentDetails(message.details) ? message.details : undefined;
|
|
272
|
+
}
|
|
273
|
+
function formatCostUsage(label, usage) {
|
|
274
|
+
const extras = [
|
|
275
|
+
usage.cacheRead ? `cache read ${formatTokens(usage.cacheRead)}` : "",
|
|
276
|
+
usage.cacheWrite ? `cache write ${formatTokens(usage.cacheWrite)}` : "",
|
|
277
|
+
usage.turns ? `${usage.turns} turn${usage.turns === 1 ? "" : "s"}` : ""
|
|
278
|
+
].filter(Boolean);
|
|
279
|
+
return `${label}: ↑${formatTokens(usage.input)} ↓${formatTokens(usage.output)} $${usage.cost.toFixed(4)}${extras.length ? ` (${extras.join(", ")})` : ""}`;
|
|
280
|
+
}
|
|
281
|
+
function buildSubagentCostReport(ctx) {
|
|
282
|
+
const parent = emptyUsage();
|
|
283
|
+
const childTotal = emptyUsage();
|
|
284
|
+
const total = emptyUsage();
|
|
285
|
+
const children = [];
|
|
286
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
287
|
+
const message = entry.type === "message" ? entry.message : undefined;
|
|
288
|
+
const parentUsage = assistantUsageFromMessage(message);
|
|
289
|
+
if (parentUsage)
|
|
290
|
+
addUsage(parent, parentUsage);
|
|
291
|
+
const details = detailsFromSessionEntry(entry);
|
|
292
|
+
if (!details)
|
|
293
|
+
continue;
|
|
294
|
+
for (const result of details.results) {
|
|
295
|
+
if (!usageHasValue(result.usage))
|
|
296
|
+
continue;
|
|
297
|
+
const usage = { ...result.usage };
|
|
298
|
+
children.push({
|
|
299
|
+
label: `Child ${children.length + 1} (${result.agent})`,
|
|
300
|
+
usage,
|
|
301
|
+
...result.sessionFile ? { sessionFile: result.sessionFile } : {}
|
|
302
|
+
});
|
|
303
|
+
addUsage(childTotal, usage);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
addUsage(total, parent);
|
|
307
|
+
addUsage(total, childTotal);
|
|
308
|
+
const lines = [
|
|
309
|
+
"Subagent cost",
|
|
310
|
+
"",
|
|
311
|
+
formatCostUsage("Parent", parent)
|
|
312
|
+
];
|
|
313
|
+
if (children.length === 0) {
|
|
314
|
+
lines.push("No subagent child usage found in this session.");
|
|
315
|
+
} else {
|
|
316
|
+
for (const child of children) {
|
|
317
|
+
lines.push(formatCostUsage(child.label, child.usage));
|
|
318
|
+
if (child.sessionFile)
|
|
319
|
+
lines.push(` Session: ${child.sessionFile}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
lines.push("────────────────────────────", formatCostUsage("Children", childTotal), formatCostUsage("Total", total));
|
|
323
|
+
return lines.join(`
|
|
324
|
+
`);
|
|
325
|
+
}
|
|
326
|
+
function parseSingleRequiredArg(args, usage) {
|
|
327
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
328
|
+
if (parts.length !== 1)
|
|
329
|
+
return { ok: false, message: usage };
|
|
330
|
+
return { ok: true, value: parts[0] };
|
|
331
|
+
}
|
|
332
|
+
function getProfileWorkerModel(profile) {
|
|
333
|
+
const model = profile.subagents?.agentOverrides?.worker?.model;
|
|
334
|
+
return typeof model === "string" && model.trim() ? model.trim() : undefined;
|
|
335
|
+
}
|
|
336
|
+
function loadSavedOutputSchema(chain, stepAgent, outputSchema) {
|
|
337
|
+
if (outputSchema === undefined)
|
|
338
|
+
return;
|
|
339
|
+
if (typeof outputSchema === "string") {
|
|
340
|
+
const schemaPath = path.isAbsolute(outputSchema) ? outputSchema : path.join(path.dirname(chain.filePath), outputSchema);
|
|
341
|
+
const parsed = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
|
342
|
+
assertJsonSchemaObject(parsed, `outputSchema for chain '${chain.name}' step '${stepAgent}' (${schemaPath})`);
|
|
343
|
+
return parsed;
|
|
344
|
+
}
|
|
345
|
+
assertJsonSchemaObject(outputSchema, `outputSchema for chain '${chain.name}' step '${stepAgent}'`);
|
|
346
|
+
return outputSchema;
|
|
347
|
+
}
|
|
348
|
+
const mapSavedChainSteps = (chain, worktree = false) => {
|
|
349
|
+
return chain.steps.map((step) => {
|
|
350
|
+
if (isParallelStep(step)) {
|
|
351
|
+
const parallel = step.parallel.map((task) => {
|
|
352
|
+
const { outputSchema: rawOutputSchema, ...rest } = task;
|
|
353
|
+
const outputSchema = loadSavedOutputSchema(chain, task.agent, rawOutputSchema);
|
|
354
|
+
return { ...rest, ...outputSchema ? { outputSchema } : {} };
|
|
355
|
+
});
|
|
356
|
+
return { ...step, parallel, ...worktree ? { worktree: true } : {} };
|
|
357
|
+
}
|
|
358
|
+
if (isDynamicParallelStep(step)) {
|
|
359
|
+
const { outputSchema: rawOutputSchema, ...parallelRest } = step.parallel;
|
|
360
|
+
const outputSchema = loadSavedOutputSchema(chain, step.parallel.agent, rawOutputSchema);
|
|
361
|
+
const collectSchema = loadSavedOutputSchema(chain, `${step.collect.as} collection`, step.collect.outputSchema);
|
|
362
|
+
return {
|
|
363
|
+
...step,
|
|
364
|
+
parallel: { ...parallelRest, ...outputSchema ? { outputSchema } : {} },
|
|
365
|
+
collect: { ...step.collect, ...collectSchema ? { outputSchema: collectSchema } : {} }
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const outputSchema = loadSavedOutputSchema(chain, step.agent, step.outputSchema);
|
|
369
|
+
return {
|
|
370
|
+
agent: step.agent,
|
|
371
|
+
task: step.task || undefined,
|
|
372
|
+
...step.phase ? { phase: step.phase } : {},
|
|
373
|
+
...step.label ? { label: step.label } : {},
|
|
374
|
+
...step.as ? { as: step.as } : {},
|
|
375
|
+
...outputSchema ? { outputSchema } : {},
|
|
376
|
+
...step.acceptance !== undefined ? { acceptance: step.acceptance } : {},
|
|
377
|
+
output: step.output,
|
|
378
|
+
outputMode: step.outputMode,
|
|
379
|
+
reads: step.reads,
|
|
380
|
+
progress: step.progress,
|
|
381
|
+
skill: step.skill ?? step.skills,
|
|
382
|
+
model: step.model
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
async function requestSlashRun(pi, ctx, requestId, params) {
|
|
387
|
+
return new Promise((resolve, reject) => {
|
|
388
|
+
let done = false;
|
|
389
|
+
let started = false;
|
|
390
|
+
const startTimeoutMs = 15000;
|
|
391
|
+
const startTimeout = setTimeout(() => {
|
|
392
|
+
finish(() => reject(new Error("Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.")));
|
|
393
|
+
}, startTimeoutMs);
|
|
394
|
+
const onStarted = (data) => {
|
|
395
|
+
if (done || !data || typeof data !== "object")
|
|
396
|
+
return;
|
|
397
|
+
if (data.requestId !== requestId)
|
|
398
|
+
return;
|
|
399
|
+
started = true;
|
|
400
|
+
clearTimeout(startTimeout);
|
|
401
|
+
if (ctx.hasUI)
|
|
402
|
+
ctx.ui.setStatus("subagent-slash", "running...");
|
|
403
|
+
};
|
|
404
|
+
const onResponse = (data) => {
|
|
405
|
+
if (done || !data || typeof data !== "object")
|
|
406
|
+
return;
|
|
407
|
+
const response = data;
|
|
408
|
+
if (response.requestId !== requestId)
|
|
409
|
+
return;
|
|
410
|
+
clearTimeout(startTimeout);
|
|
411
|
+
finish(() => resolve(response));
|
|
412
|
+
};
|
|
413
|
+
const onUpdate = (data) => {
|
|
414
|
+
if (done || !data || typeof data !== "object")
|
|
415
|
+
return;
|
|
416
|
+
const update = data;
|
|
417
|
+
if (update.requestId !== requestId)
|
|
418
|
+
return;
|
|
419
|
+
applySlashUpdate(requestId, update);
|
|
420
|
+
if (!ctx.hasUI)
|
|
421
|
+
return;
|
|
422
|
+
const tool = update.currentTool ? ` ${update.currentTool}` : "";
|
|
423
|
+
const count = update.toolCount ?? 0;
|
|
424
|
+
const liveDetailKey = keyText("app.tools.expand");
|
|
425
|
+
ctx.ui.setStatus("subagent-slash", `${count} tools${tool} | ${liveDetailKey} live detail`);
|
|
426
|
+
};
|
|
427
|
+
const onTerminalInput = ctx.hasUI ? ctx.ui.onTerminalInput((input) => {
|
|
428
|
+
if (!matchesKey(input, Key.escape))
|
|
429
|
+
return;
|
|
430
|
+
pi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });
|
|
431
|
+
finish(() => reject(new Error("Cancelled")));
|
|
432
|
+
return { consume: true };
|
|
433
|
+
}) : undefined;
|
|
434
|
+
const unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);
|
|
435
|
+
const unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);
|
|
436
|
+
const unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);
|
|
437
|
+
const finish = (next) => {
|
|
438
|
+
if (done)
|
|
439
|
+
return;
|
|
440
|
+
done = true;
|
|
441
|
+
clearTimeout(startTimeout);
|
|
442
|
+
unsubStarted();
|
|
443
|
+
unsubResponse();
|
|
444
|
+
unsubUpdate();
|
|
445
|
+
onTerminalInput?.();
|
|
446
|
+
next();
|
|
447
|
+
};
|
|
448
|
+
pi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ctx });
|
|
449
|
+
if (!started && done)
|
|
450
|
+
return;
|
|
451
|
+
if (!started) {
|
|
452
|
+
finish(() => reject(new Error("No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.")));
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function extractSlashMessageText(content) {
|
|
457
|
+
if (typeof content === "string")
|
|
458
|
+
return content;
|
|
459
|
+
if (!Array.isArray(content))
|
|
460
|
+
return "";
|
|
461
|
+
return content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
|
|
462
|
+
`);
|
|
463
|
+
}
|
|
464
|
+
function formatExportPathList(paths) {
|
|
465
|
+
return paths.map((file) => `- \`${file}\``).join(`
|
|
466
|
+
`);
|
|
467
|
+
}
|
|
468
|
+
function collectResultPaths(results, getPath) {
|
|
469
|
+
return results.map(getPath).filter((file) => typeof file === "string" && file.length > 0);
|
|
470
|
+
}
|
|
471
|
+
function buildSlashExportText(response) {
|
|
472
|
+
const output = extractSlashMessageText(response.result.content) || response.errorText || "(no output)";
|
|
473
|
+
const results = response.result.details?.results ?? [];
|
|
474
|
+
const sessionFiles = collectResultPaths(results, (result) => result.sessionFile);
|
|
475
|
+
const savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);
|
|
476
|
+
const artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);
|
|
477
|
+
const sections = ["## Subagent result", output];
|
|
478
|
+
if (sessionFiles.length > 0)
|
|
479
|
+
sections.push("## Child session exports", formatExportPathList(sessionFiles));
|
|
480
|
+
if (savedOutputs.length > 0)
|
|
481
|
+
sections.push("## Saved outputs", formatExportPathList(savedOutputs));
|
|
482
|
+
if (artifactOutputs.length > 0)
|
|
483
|
+
sections.push("## Artifact outputs", formatExportPathList(artifactOutputs));
|
|
484
|
+
return sections.join(`
|
|
14
485
|
|
|
15
|
-
`)
|
|
16
|
-
|
|
486
|
+
`);
|
|
487
|
+
}
|
|
488
|
+
function persistSlashSessionSnapshot(ctx) {
|
|
489
|
+
try {
|
|
490
|
+
if (!ctx.sessionManager)
|
|
491
|
+
return;
|
|
492
|
+
const sessionManager = ctx.sessionManager;
|
|
493
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
494
|
+
if (!sessionFile || typeof sessionManager._rewriteFile !== "function")
|
|
495
|
+
return;
|
|
496
|
+
fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
|
|
497
|
+
sessionManager._rewriteFile();
|
|
498
|
+
sessionManager.flushed = true;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.error("Failed to persist slash session snapshot for export:", error);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async function runSlashSubagent(pi, ctx, params) {
|
|
504
|
+
if (ctx.hasUI)
|
|
505
|
+
ctx.ui.setToolsExpanded(false);
|
|
506
|
+
const requestId = randomUUID();
|
|
507
|
+
const initialDetails = buildSlashInitialResult(requestId, params);
|
|
508
|
+
const initialText = extractSlashMessageText(initialDetails.result.content) || "Running subagent...";
|
|
509
|
+
pi.sendMessage({
|
|
510
|
+
customType: SLASH_RESULT_TYPE,
|
|
511
|
+
content: initialText,
|
|
512
|
+
display: true,
|
|
513
|
+
details: initialDetails
|
|
514
|
+
});
|
|
515
|
+
persistSlashSessionSnapshot(ctx);
|
|
516
|
+
try {
|
|
517
|
+
const response = await requestSlashRun(pi, ctx, requestId, params);
|
|
518
|
+
const finalDetails = finalizeSlashResult(response);
|
|
519
|
+
pi.sendMessage({
|
|
520
|
+
customType: SLASH_RESULT_TYPE,
|
|
521
|
+
content: buildSlashExportText(response),
|
|
522
|
+
display: !ctx.hasUI,
|
|
523
|
+
details: finalDetails
|
|
524
|
+
});
|
|
525
|
+
persistSlashSessionSnapshot(ctx);
|
|
526
|
+
if (ctx.hasUI) {
|
|
527
|
+
ctx.ui.setStatus("subagent-slash", undefined);
|
|
528
|
+
}
|
|
529
|
+
if (response.isError && ctx.hasUI) {
|
|
530
|
+
ctx.ui.notify(response.errorText || "Subagent failed", "error");
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
534
|
+
const failedDetails = failSlashResult(requestId, params, message);
|
|
535
|
+
pi.sendMessage({
|
|
536
|
+
customType: SLASH_RESULT_TYPE,
|
|
537
|
+
content: `## Subagent result
|
|
17
538
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
539
|
+
${message}`,
|
|
540
|
+
display: !ctx.hasUI,
|
|
541
|
+
details: failedDetails
|
|
542
|
+
});
|
|
543
|
+
persistSlashSessionSnapshot(ctx);
|
|
544
|
+
if (ctx.hasUI) {
|
|
545
|
+
ctx.ui.setStatus("subagent-slash", undefined);
|
|
546
|
+
}
|
|
547
|
+
if (message === "Cancelled") {
|
|
548
|
+
if (ctx.hasUI)
|
|
549
|
+
ctx.ui.notify("Cancelled", "warning");
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (ctx.hasUI)
|
|
553
|
+
ctx.ui.notify(message, "error");
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
export const PARALLEL_GROUP_USAGE = 'Usage: /chain agent "task" -> (agent2 "task" | agent3 "task") -> agent4';
|
|
22
557
|
|
|
23
|
-
|
|
558
|
+
export class SlashParseError extends Error {
|
|
559
|
+
}
|
|
560
|
+
function findUnmatchedCloseParen(input) {
|
|
561
|
+
let depth = 0, inSingle = false, inDouble = false;
|
|
562
|
+
for (let i = 0;i < input.length; i++) {
|
|
563
|
+
const ch = input[i];
|
|
564
|
+
if (inSingle) {
|
|
565
|
+
if (ch === "'")
|
|
566
|
+
inSingle = false;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (inDouble) {
|
|
570
|
+
if (ch === '"')
|
|
571
|
+
inDouble = false;
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
if (ch === "'") {
|
|
575
|
+
inSingle = true;
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (ch === '"') {
|
|
579
|
+
inDouble = true;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (ch === "(")
|
|
583
|
+
depth++;
|
|
584
|
+
else if (ch === ")") {
|
|
585
|
+
depth--;
|
|
586
|
+
if (depth < 0)
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return depth !== 0;
|
|
591
|
+
}
|
|
592
|
+
function splitOnArrow(input) {
|
|
593
|
+
const segments = [];
|
|
594
|
+
let depth = 0, inSingle = false, inDouble = false, start = 0;
|
|
595
|
+
for (let i = 0;i < input.length; i++) {
|
|
596
|
+
const ch = input[i];
|
|
597
|
+
if (inSingle) {
|
|
598
|
+
if (ch === "'")
|
|
599
|
+
inSingle = false;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (inDouble) {
|
|
603
|
+
if (ch === '"')
|
|
604
|
+
inDouble = false;
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (ch === "'") {
|
|
608
|
+
inSingle = true;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
if (ch === '"') {
|
|
612
|
+
inDouble = true;
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (ch === "(")
|
|
616
|
+
depth++;
|
|
617
|
+
else if (ch === ")")
|
|
618
|
+
depth--;
|
|
619
|
+
else if (depth === 0 && ch === "-" && input[i + 1] === ">" && input[i + 2] === " ") {
|
|
620
|
+
segments.push(input.slice(start, i));
|
|
621
|
+
i += 2;
|
|
622
|
+
start = i + 1;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
segments.push(input.slice(start));
|
|
626
|
+
return segments;
|
|
627
|
+
}
|
|
628
|
+
function splitGroupTasks(inner) {
|
|
629
|
+
const parts = [];
|
|
630
|
+
let depth = 0, inSingle = false, inDouble = false, start = 0;
|
|
631
|
+
for (let i = 0;i < inner.length; i++) {
|
|
632
|
+
const ch = inner[i];
|
|
633
|
+
if (inSingle) {
|
|
634
|
+
if (ch === "'")
|
|
635
|
+
inSingle = false;
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
if (inDouble) {
|
|
639
|
+
if (ch === '"')
|
|
640
|
+
inDouble = false;
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
if (ch === "'") {
|
|
644
|
+
inSingle = true;
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
if (ch === '"') {
|
|
648
|
+
inDouble = true;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (ch === "(")
|
|
652
|
+
depth++;
|
|
653
|
+
else if (ch === ")")
|
|
654
|
+
depth--;
|
|
655
|
+
else if (ch === "|" && depth === 0) {
|
|
656
|
+
parts.push(inner.slice(start, i));
|
|
657
|
+
start = i + 1;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
parts.push(inner.slice(start));
|
|
661
|
+
return parts;
|
|
662
|
+
}
|
|
663
|
+
export function parseSingleTaskToken(token) {
|
|
664
|
+
let agentPart;
|
|
665
|
+
let task;
|
|
666
|
+
const qMatch = token.match(/^(\S+(?:\[[^\]]*\])?)\s+(?:"([^"]*)"|'([^']*)')$/);
|
|
667
|
+
if (qMatch) {
|
|
668
|
+
agentPart = qMatch[1];
|
|
669
|
+
task = (qMatch[2] ?? qMatch[3]) || undefined;
|
|
670
|
+
} else {
|
|
671
|
+
const dashIdx = token.indexOf(" -- ");
|
|
672
|
+
if (dashIdx !== -1) {
|
|
673
|
+
agentPart = token.slice(0, dashIdx).trim();
|
|
674
|
+
task = token.slice(dashIdx + 4).trim() || undefined;
|
|
675
|
+
} else {
|
|
676
|
+
agentPart = token;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return { kind: "step", ...parseAgentToken(agentPart), task };
|
|
680
|
+
}
|
|
681
|
+
const parseGroupConfig = (raw) => {
|
|
682
|
+
const config = {};
|
|
683
|
+
for (const part of raw.split(",")) {
|
|
684
|
+
const trimmed = part.trim();
|
|
685
|
+
if (!trimmed)
|
|
686
|
+
continue;
|
|
687
|
+
const eq = trimmed.indexOf("=");
|
|
688
|
+
const key = eq === -1 ? trimmed : trimmed.slice(0, eq).trim();
|
|
689
|
+
const val = eq === -1 ? "" : trimmed.slice(eq + 1).trim();
|
|
690
|
+
switch (key) {
|
|
691
|
+
case "concurrency": {
|
|
692
|
+
const n = Number(val);
|
|
693
|
+
if (Number.isInteger(n) && n > 0)
|
|
694
|
+
config.concurrency = n;
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case "failFast":
|
|
698
|
+
config.failFast = eq === -1 ? true : val !== "false";
|
|
699
|
+
break;
|
|
700
|
+
case "worktree":
|
|
701
|
+
config.worktree = eq === -1 ? true : val !== "false";
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return config;
|
|
706
|
+
};
|
|
707
|
+
const splitGroupBody = (trimmed) => {
|
|
708
|
+
let depth = 0, inSingle = false, inDouble = false, closeIdx = -1;
|
|
709
|
+
for (let i = 0;i < trimmed.length; i++) {
|
|
710
|
+
const ch = trimmed[i];
|
|
711
|
+
if (inSingle) {
|
|
712
|
+
if (ch === "'")
|
|
713
|
+
inSingle = false;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (inDouble) {
|
|
717
|
+
if (ch === '"')
|
|
718
|
+
inDouble = false;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (ch === "'") {
|
|
722
|
+
inSingle = true;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (ch === '"') {
|
|
726
|
+
inDouble = true;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (ch === "(")
|
|
730
|
+
depth++;
|
|
731
|
+
else if (ch === ")") {
|
|
732
|
+
depth--;
|
|
733
|
+
if (depth === 0) {
|
|
734
|
+
closeIdx = i;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (closeIdx === -1)
|
|
740
|
+
throw new SlashParseError(`Unmatched parentheses in group: '${trimmed}'`);
|
|
741
|
+
const inner = trimmed.slice(1, closeIdx);
|
|
742
|
+
const suffix = trimmed.slice(closeIdx + 1).trim();
|
|
743
|
+
if (!suffix)
|
|
744
|
+
return { inner, config: {} };
|
|
745
|
+
if (!suffix.startsWith("[") || !suffix.endsWith("]")) {
|
|
746
|
+
throw new SlashParseError(`Group options must be wrapped in [...]: '${suffix}'`);
|
|
747
|
+
}
|
|
748
|
+
return { inner, config: parseGroupConfig(suffix.slice(1, -1)) };
|
|
749
|
+
};
|
|
750
|
+
export function parseGroupSegment(segment) {
|
|
751
|
+
const trimmed = segment.trim();
|
|
752
|
+
if (!trimmed.startsWith("(")) {
|
|
753
|
+
throw new SlashParseError(`Parallel group must be wrapped in parentheses: '${trimmed}'`);
|
|
754
|
+
}
|
|
755
|
+
const { inner, config } = splitGroupBody(trimmed);
|
|
756
|
+
const rawParts = splitGroupTasks(inner).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
757
|
+
if (rawParts.length < 2) {
|
|
758
|
+
throw new SlashParseError("Parallel group must contain at least two tasks separated by ' | '");
|
|
759
|
+
}
|
|
760
|
+
return { kind: "group", tasks: rawParts.map((part) => parseSingleTaskToken(part)), config };
|
|
761
|
+
}
|
|
762
|
+
export function hasGroupSyntax(input) {
|
|
763
|
+
return splitOnArrow(input).some((seg) => seg.trim().startsWith("("));
|
|
764
|
+
}
|
|
765
|
+
export function parseChainExpression(input) {
|
|
766
|
+
const trimmed = input.trim();
|
|
767
|
+
if (!trimmed.includes(" -> ")) {
|
|
768
|
+
throw new SlashParseError('Parallel groups in /chain require " -> " between steps');
|
|
769
|
+
}
|
|
770
|
+
if (findUnmatchedCloseParen(trimmed)) {
|
|
771
|
+
throw new SlashParseError("Unmatched parentheses in /chain expression");
|
|
772
|
+
}
|
|
773
|
+
const steps = [];
|
|
774
|
+
for (const seg of splitOnArrow(trimmed)) {
|
|
775
|
+
const t = seg.trim();
|
|
776
|
+
if (!t)
|
|
777
|
+
continue;
|
|
778
|
+
if (t.startsWith("(")) {
|
|
779
|
+
steps.push(parseGroupSegment(t));
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (findUnmatchedCloseParen(t)) {
|
|
783
|
+
throw new SlashParseError(`Unmatched parentheses in chain segment: '${t}'`);
|
|
784
|
+
}
|
|
785
|
+
steps.push(parseSingleTaskToken(t));
|
|
786
|
+
}
|
|
787
|
+
if (steps.length === 0) {
|
|
788
|
+
throw new SlashParseError("/chain expression must include at least one step");
|
|
789
|
+
}
|
|
790
|
+
return { steps };
|
|
791
|
+
}
|
|
792
|
+
const parseAgentArgs = (state, args, command, ctx) => {
|
|
793
|
+
const input = args.trim();
|
|
794
|
+
const usage = `Usage: /${command} agent1 "task1" -> agent2 "task2"`;
|
|
795
|
+
let steps;
|
|
796
|
+
let sharedTask;
|
|
797
|
+
let perStep = false;
|
|
798
|
+
if (input.includes(" -> ")) {
|
|
799
|
+
perStep = true;
|
|
800
|
+
const segments = input.split(" -> ");
|
|
801
|
+
steps = [];
|
|
802
|
+
for (const seg of segments) {
|
|
803
|
+
const trimmed = seg.trim();
|
|
804
|
+
if (!trimmed)
|
|
805
|
+
continue;
|
|
806
|
+
steps.push(parseSingleTaskToken(trimmed));
|
|
807
|
+
}
|
|
808
|
+
sharedTask = steps.find((s) => s.task)?.task ?? "";
|
|
809
|
+
} else {
|
|
810
|
+
const delimiterIndex = input.indexOf(" -- ");
|
|
811
|
+
if (delimiterIndex === -1) {
|
|
812
|
+
ctx.ui.notify(usage, "error");
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
815
|
+
const agentsPart = input.slice(0, delimiterIndex).trim();
|
|
816
|
+
sharedTask = input.slice(delimiterIndex + 4).trim();
|
|
817
|
+
if (!agentsPart || !sharedTask) {
|
|
818
|
+
ctx.ui.notify(usage, "error");
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
steps = agentsPart.split(/\s+/).filter(Boolean).map((t) => parseSingleTaskToken(t));
|
|
822
|
+
}
|
|
823
|
+
if (steps.length === 0) {
|
|
824
|
+
ctx.ui.notify(usage, "error");
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
if (!state.baseCwd) {
|
|
828
|
+
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
832
|
+
for (const step of steps) {
|
|
833
|
+
if (!agents.find((a) => a.name === step.name)) {
|
|
834
|
+
ctx.ui.notify(`Unknown agent: ${step.name}`, "error");
|
|
835
|
+
return null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (command === "chain" && !steps[0]?.task && (perStep || !sharedTask)) {
|
|
839
|
+
ctx.ui.notify(`First step must have a task: /chain agent "task" -> agent2`, "error");
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
if (command === "parallel" && !steps.some((s) => s.task) && !sharedTask) {
|
|
843
|
+
ctx.ui.notify("At least one step must have a task", "error");
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
return { steps, task: sharedTask };
|
|
847
|
+
};
|
|
848
|
+
const INLINE_ACCEPTANCE_LEVELS = new Set(["auto", "attested", "checked"]);
|
|
849
|
+
function validateInlineAcceptanceInput(value, agent) {
|
|
850
|
+
const errors = validateAcceptanceInput(value, `acceptance for step '${agent}'`);
|
|
851
|
+
if (errors.length > 0)
|
|
852
|
+
throw new SlashParseError(errors[0]);
|
|
853
|
+
if (!INLINE_ACCEPTANCE_LEVELS.has(value)) {
|
|
854
|
+
throw new SlashParseError(`Inline acceptance for step '${agent}' supports auto, attested, or checked. Use the subagent tool API or a saved .chain.json file for none, verified, or reviewed acceptance contracts.`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function loadInlineOutputSchema(baseCwd, agent, value) {
|
|
858
|
+
const schemaPath = path.isAbsolute(value) ? value : path.join(baseCwd, value);
|
|
859
|
+
const label = `outputSchema for step '${agent}' (${schemaPath})`;
|
|
860
|
+
let parsed;
|
|
861
|
+
try {
|
|
862
|
+
parsed = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
|
|
863
|
+
} catch (error) {
|
|
864
|
+
throw new SlashParseError(`Cannot read ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
865
|
+
}
|
|
866
|
+
assertJsonSchemaObject(parsed, label);
|
|
867
|
+
return parsed;
|
|
868
|
+
}
|
|
869
|
+
const mapParsedTaskToStepObject = (step, fallbackTask, isFirst, opts) => {
|
|
870
|
+
const { name, config, task: stepTask } = step;
|
|
871
|
+
if (config.acceptance !== undefined)
|
|
872
|
+
validateInlineAcceptanceInput(config.acceptance, name);
|
|
873
|
+
return {
|
|
874
|
+
agent: name,
|
|
875
|
+
...stepTask ? { task: stepTask } : isFirst && fallbackTask ? { task: fallbackTask } : {},
|
|
876
|
+
...config.output !== undefined ? { output: config.output } : {},
|
|
877
|
+
...config.outputMode !== undefined ? { outputMode: config.outputMode } : {},
|
|
878
|
+
...config.reads !== undefined ? { reads: config.reads } : {},
|
|
879
|
+
...config.model ? { model: config.model } : {},
|
|
880
|
+
...config.skill !== undefined ? { skill: config.skill } : {},
|
|
881
|
+
...config.progress !== undefined ? { progress: config.progress } : {},
|
|
882
|
+
...config.as ? { as: config.as } : {},
|
|
883
|
+
...config.label ? { label: config.label } : {},
|
|
884
|
+
...config.phase ? { phase: config.phase } : {},
|
|
885
|
+
...config.cwd ? { cwd: config.cwd } : {},
|
|
886
|
+
...opts.inGroup && config.count !== undefined ? { count: config.count } : {},
|
|
887
|
+
...config.outputSchema ? { outputSchema: loadInlineOutputSchema(opts.baseCwd, name, config.outputSchema) } : {},
|
|
888
|
+
...config.acceptance ? { acceptance: config.acceptance } : {}
|
|
889
|
+
};
|
|
890
|
+
};
|
|
891
|
+
export function buildChainExpressionSteps(state, input, ctx) {
|
|
892
|
+
const notify = (message) => ctx.ui.notify(message, "error");
|
|
893
|
+
if (!hasGroupSyntax(input)) {
|
|
894
|
+
const parsed = parseAgentArgs(state, input, "chain", ctx);
|
|
895
|
+
if (!parsed)
|
|
896
|
+
return null;
|
|
897
|
+
const baseCwd = state.baseCwd;
|
|
898
|
+
try {
|
|
899
|
+
const chain = parsed.steps.map((step, i) => mapParsedTaskToStepObject(step, parsed.task || undefined, i === 0, { baseCwd, inGroup: false }));
|
|
900
|
+
return { chain, task: parsed.task };
|
|
901
|
+
} catch (error) {
|
|
902
|
+
notify(error instanceof Error ? error.message : String(error));
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
let expression;
|
|
907
|
+
try {
|
|
908
|
+
expression = parseChainExpression(input);
|
|
909
|
+
} catch (error) {
|
|
910
|
+
notify(error instanceof Error ? error.message : String(error));
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
if (!state.baseCwd) {
|
|
914
|
+
notify("Subagent session cwd is not initialized yet");
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
918
|
+
const stepAgentNames = expression.steps.flatMap((step) => step.kind === "group" ? step.tasks.map((t) => t.name) : [step.name]);
|
|
919
|
+
for (const name of stepAgentNames) {
|
|
920
|
+
if (!agents.find((a) => a.name === name)) {
|
|
921
|
+
notify(`Unknown agent: ${name}`);
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
for (const step of expression.steps) {
|
|
926
|
+
if (step.kind === "group" && step.tasks.some((t) => !t.task)) {
|
|
927
|
+
notify('Each task in a parallel group needs a task: (agent "a" | agent "b")');
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const firstStep = expression.steps[0];
|
|
932
|
+
const firstHasTask = firstStep.kind === "group" ? firstStep.tasks.some((t) => Boolean(t.task)) : Boolean(firstStep.task);
|
|
933
|
+
if (!firstHasTask) {
|
|
934
|
+
notify('First step must have a task: /chain agent "task" -> agent2');
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
const sharedTask = firstStep.kind === "group" ? firstStep.tasks.find((t) => t.task)?.task ?? "" : firstStep.task ?? "";
|
|
938
|
+
const baseCwd = state.baseCwd;
|
|
939
|
+
let chain;
|
|
940
|
+
try {
|
|
941
|
+
chain = expression.steps.map((step) => {
|
|
942
|
+
if (step.kind === "group") {
|
|
943
|
+
const parallel = step.tasks.map((t) => mapParsedTaskToStepObject(t, undefined, false, { baseCwd, inGroup: true }));
|
|
944
|
+
return {
|
|
945
|
+
parallel,
|
|
946
|
+
...step.config.concurrency !== undefined ? { concurrency: step.config.concurrency } : {},
|
|
947
|
+
...step.config.failFast !== undefined ? { failFast: step.config.failFast } : {},
|
|
948
|
+
...step.config.worktree !== undefined ? { worktree: step.config.worktree } : {}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
return mapParsedTaskToStepObject(step, sharedTask || undefined, false, { baseCwd, inGroup: false });
|
|
952
|
+
});
|
|
953
|
+
} catch (error) {
|
|
954
|
+
notify(error instanceof Error ? error.message : String(error));
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
return { chain, task: sharedTask };
|
|
958
|
+
}
|
|
959
|
+
export function registerSlashCommands(pi, state) {
|
|
960
|
+
pi.registerCommand("run", {
|
|
961
|
+
description: "Run a subagent directly: /run agent[output=file] [task] [--bg] [--fork]",
|
|
962
|
+
getArgumentCompletions: makeAgentCompletions(state, false),
|
|
963
|
+
handler: async (args, ctx) => {
|
|
964
|
+
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
965
|
+
const input = cleanedArgs.trim();
|
|
966
|
+
const firstSpace = input.indexOf(" ");
|
|
967
|
+
if (!input) {
|
|
968
|
+
ctx.ui.notify("Usage: /run <agent> [task] [--bg] [--fork]", "error");
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const { name: agentName, config: inline } = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));
|
|
972
|
+
const task = firstSpace === -1 ? "" : input.slice(firstSpace + 1).trim();
|
|
973
|
+
if (!state.baseCwd) {
|
|
974
|
+
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const agents = discoverAgents(state.baseCwd, "both").agents;
|
|
978
|
+
if (!agents.find((a) => a.name === agentName)) {
|
|
979
|
+
ctx.ui.notify(`Unknown agent: ${agentName}`, "error");
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
let finalTask = task;
|
|
983
|
+
if (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {
|
|
984
|
+
finalTask = `[Read from: ${inline.reads.join(", ")}]
|
|
24
985
|
|
|
25
|
-
${
|
|
986
|
+
${finalTask}`;
|
|
987
|
+
}
|
|
988
|
+
const params = { agent: agentName, task: finalTask, clarify: false, agentScope: "both" };
|
|
989
|
+
if (inline.output !== undefined)
|
|
990
|
+
params.output = inline.output;
|
|
991
|
+
if (inline.outputMode !== undefined)
|
|
992
|
+
params.outputMode = inline.outputMode;
|
|
993
|
+
if (inline.skill !== undefined)
|
|
994
|
+
params.skill = inline.skill;
|
|
995
|
+
if (inline.model)
|
|
996
|
+
params.model = inline.model;
|
|
997
|
+
if (bg)
|
|
998
|
+
params.async = true;
|
|
999
|
+
if (fork)
|
|
1000
|
+
params.context = "fork";
|
|
1001
|
+
await runSlashSubagent(pi, ctx, params);
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
pi.registerCommand("chain", {
|
|
1005
|
+
description: 'Run agents in sequence: /chain scout "task" -> planner [--bg] [--fork]',
|
|
1006
|
+
getArgumentCompletions: makeAgentCompletions(state, true),
|
|
1007
|
+
handler: async (args, ctx) => {
|
|
1008
|
+
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
1009
|
+
const built = buildChainExpressionSteps(state, cleanedArgs, ctx);
|
|
1010
|
+
if (!built)
|
|
1011
|
+
return;
|
|
1012
|
+
const params = { chain: built.chain, task: built.task, clarify: false, agentScope: "both" };
|
|
1013
|
+
if (bg)
|
|
1014
|
+
params.async = true;
|
|
1015
|
+
if (fork)
|
|
1016
|
+
params.context = "fork";
|
|
1017
|
+
await runSlashSubagent(pi, ctx, params);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
pi.registerCommand("run-chain", {
|
|
1021
|
+
description: "Run a saved chain: /run-chain chainName -- task [--bg] [--fork]",
|
|
1022
|
+
getArgumentCompletions: makeChainCompletions(state),
|
|
1023
|
+
handler: async (args, ctx) => {
|
|
1024
|
+
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
1025
|
+
const delimiterIndex = cleanedArgs.indexOf(" -- ");
|
|
1026
|
+
const usage = "Usage: /run-chain <chainName> -- <task> [--bg] [--fork]";
|
|
1027
|
+
if (delimiterIndex === -1) {
|
|
1028
|
+
ctx.ui.notify(usage, "error");
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
|
|
1032
|
+
const task = cleanedArgs.slice(delimiterIndex + 4).trim();
|
|
1033
|
+
if (!chainName || !task) {
|
|
1034
|
+
ctx.ui.notify(usage, "error");
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (!state.baseCwd) {
|
|
1038
|
+
ctx.ui.notify("Subagent session cwd is not initialized yet", "error");
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const chain = discoverSavedChains(state.baseCwd).find((candidate) => candidate.name === chainName);
|
|
1042
|
+
if (!chain) {
|
|
1043
|
+
ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const params = { chain: mapSavedChainSteps(chain), task, clarify: false, agentScope: "both" };
|
|
1047
|
+
if (bg)
|
|
1048
|
+
params.async = true;
|
|
1049
|
+
if (fork)
|
|
1050
|
+
params.context = "fork";
|
|
1051
|
+
await runSlashSubagent(pi, ctx, params);
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
pi.registerCommand("parallel", {
|
|
1055
|
+
description: 'Run agents in parallel: /parallel scout "task1" -> reviewer "task2" [--bg] [--fork]',
|
|
1056
|
+
getArgumentCompletions: makeAgentCompletions(state, true),
|
|
1057
|
+
handler: async (args, ctx) => {
|
|
1058
|
+
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
1059
|
+
const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
|
|
1060
|
+
if (!parsed)
|
|
1061
|
+
return;
|
|
1062
|
+
const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
|
|
1063
|
+
agent: name,
|
|
1064
|
+
task: stepTask ?? parsed.task,
|
|
1065
|
+
...config.output !== undefined ? { output: config.output } : {},
|
|
1066
|
+
...config.outputMode !== undefined ? { outputMode: config.outputMode } : {},
|
|
1067
|
+
...config.reads !== undefined ? { reads: config.reads } : {},
|
|
1068
|
+
...config.model ? { model: config.model } : {},
|
|
1069
|
+
...config.skill !== undefined ? { skill: config.skill } : {},
|
|
1070
|
+
...config.progress !== undefined ? { progress: config.progress } : {}
|
|
1071
|
+
}));
|
|
1072
|
+
const params = { tasks, clarify: false, agentScope: "both" };
|
|
1073
|
+
if (bg)
|
|
1074
|
+
params.async = true;
|
|
1075
|
+
if (fork)
|
|
1076
|
+
params.context = "fork";
|
|
1077
|
+
await runSlashSubagent(pi, ctx, params);
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
pi.registerCommand("subagent-cost", {
|
|
1081
|
+
description: "Show parent and subagent child usage cost for this session",
|
|
1082
|
+
handler: async (_args, ctx) => {
|
|
1083
|
+
sendSlashText(pi, buildSubagentCostReport(ctx));
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
pi.registerCommand("subagents-doctor", {
|
|
1087
|
+
description: "Show subagent diagnostics",
|
|
1088
|
+
handler: async (_args, ctx) => {
|
|
1089
|
+
await runSlashSubagent(pi, ctx, { action: "doctor" });
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
pi.registerCommand("subagents-fleet", {
|
|
1093
|
+
description: "Show active subagent fleet status and transcript commands",
|
|
1094
|
+
handler: async (_args, ctx) => {
|
|
1095
|
+
await runSlashSubagent(pi, ctx, { action: "status", view: "fleet" });
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
registerPromptWorkflowCommands({
|
|
1099
|
+
pi,
|
|
1100
|
+
run: (params, ctx) => runSlashSubagent(pi, ctx, params)
|
|
1101
|
+
});
|
|
1102
|
+
pi.registerCommand("subagents-models", {
|
|
1103
|
+
description: "Show runtime-loaded builtin subagent models",
|
|
1104
|
+
getArgumentCompletions: makeBuiltinAgentNameCompletions(),
|
|
1105
|
+
handler: async (args, ctx) => {
|
|
1106
|
+
const trimmed = args.trim();
|
|
1107
|
+
if (!trimmed) {
|
|
1108
|
+
await runSlashSubagent(pi, ctx, { action: "models" });
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
1112
|
+
if (parts.length !== 1) {
|
|
1113
|
+
ctx.ui.notify("Usage: /subagents-models [builtin-agent-name]", "error");
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
const agent = parts[0];
|
|
1117
|
+
if (!BUILTIN_AGENT_NAMES.includes(agent)) {
|
|
1118
|
+
ctx.ui.notify(`Unknown builtin agent: ${agent}`, "error");
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
await runSlashSubagent(pi, ctx, { action: "models", agent });
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
pi.registerCommand("subagents-profiles", {
|
|
1125
|
+
description: "List saved subagent profiles",
|
|
1126
|
+
handler: async (_args, _ctx) => {
|
|
1127
|
+
const profiles = listSubagentProfiles();
|
|
1128
|
+
if (profiles.length === 0) {
|
|
1129
|
+
sendSlashText(pi, `Subagent profiles
|
|
26
1130
|
|
|
27
|
-
|
|
1131
|
+
No subagent profiles found in ~/.dm/agent/profiles/dm-subagents/`);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
sendSlashText(pi, `Subagent profiles
|
|
28
1135
|
|
|
29
|
-
|
|
1136
|
+
${profiles.join(`
|
|
1137
|
+
`)}`);
|
|
1138
|
+
}
|
|
1139
|
+
});
|
|
1140
|
+
pi.registerCommand("subagents-load-profile", {
|
|
1141
|
+
description: "Load a subagent profile into ~/.dm/agent/settings.json",
|
|
1142
|
+
getArgumentCompletions: (prefix) => {
|
|
1143
|
+
if (prefix.includes(" "))
|
|
1144
|
+
return null;
|
|
1145
|
+
return listSubagentProfiles().filter((name) => name.startsWith(prefix)).map((name) => ({ value: name, label: name }));
|
|
1146
|
+
},
|
|
1147
|
+
handler: async (args, ctx) => {
|
|
1148
|
+
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-load-profile <name>");
|
|
1149
|
+
if (!parsed.ok) {
|
|
1150
|
+
ctx.ui.notify(parsed.message, "error");
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
await withSlashStatus(ctx, `Loading profile ${parsed.value}…`, async () => {
|
|
1155
|
+
const { profile } = readSubagentProfile(parsed.value);
|
|
1156
|
+
const workerModel = getProfileWorkerModel(profile);
|
|
1157
|
+
const result = applySubagentProfile(parsed.value);
|
|
1158
|
+
const lines = [
|
|
1159
|
+
`Loaded subagent profile: ${parsed.value}`,
|
|
1160
|
+
`Profile: ${result.filePath}`,
|
|
1161
|
+
`Updated: ${result.settingsPath}`
|
|
1162
|
+
];
|
|
1163
|
+
if (workerModel && typeof pi.setModel === "function" && typeof ctx.modelRegistry?.find === "function" && typeof ctx.modelRegistry?.getAvailable === "function") {
|
|
1164
|
+
const shouldSwitch = await ctx.ui.confirm("", `Profile loaded. Also switch this session to the profile worker model?
|
|
30
1165
|
|
|
31
|
-
${
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1166
|
+
${workerModel}`);
|
|
1167
|
+
if (shouldSwitch) {
|
|
1168
|
+
const modelInfo = findModelInfo(workerModel, ctx.modelRegistry.getAvailable().map(toModelInfo));
|
|
1169
|
+
const model = modelInfo ? ctx.modelRegistry.find(modelInfo.provider, modelInfo.id) : undefined;
|
|
1170
|
+
if (!modelInfo || !model) {
|
|
1171
|
+
lines.push(`Could not switch current session model: '${workerModel}' is not available in the current model registry.`);
|
|
1172
|
+
} else {
|
|
1173
|
+
const success = await pi.setModel(model);
|
|
1174
|
+
if (success)
|
|
1175
|
+
lines.push(`Current session model switched to: ${modelInfo.fullId}`);
|
|
1176
|
+
else
|
|
1177
|
+
lines.push(`Could not switch current session model to '${workerModel}': no API key or provider access is available.`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
} else if (workerModel) {
|
|
1181
|
+
lines.push(`Profile worker model: ${workerModel}`);
|
|
1182
|
+
}
|
|
1183
|
+
sendSlashText(pi, lines.join(`
|
|
1184
|
+
`));
|
|
1185
|
+
});
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
pi.registerCommand("subagents-refresh-provider-models", {
|
|
1192
|
+
description: "Refresh the cached model catalog for one provider",
|
|
1193
|
+
getArgumentCompletions: makeProviderCompletions(state),
|
|
1194
|
+
handler: async (args, ctx) => {
|
|
1195
|
+
const trimmed = args.trim();
|
|
1196
|
+
const force = /(?:^|\s)--force$/.test(trimmed) || /(?:^|\s)force$/.test(trimmed);
|
|
1197
|
+
const withoutForce = trimmed.replace(/(?:^|\s)(?:--force|force)$/, "").trim();
|
|
1198
|
+
const parsed = parseSingleRequiredArg(withoutForce, "Usage: /subagents-refresh-provider-models <provider> [--force]");
|
|
1199
|
+
if (!parsed.ok) {
|
|
1200
|
+
ctx.ui.notify(parsed.message, "error");
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
try {
|
|
1204
|
+
await withSlashStatus(ctx, `Refreshing provider models for ${parsed.value}…`, async () => {
|
|
1205
|
+
const result = await refreshProviderModelCatalog(pi, ctx, parsed.value, { force, maxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS });
|
|
1206
|
+
const lines = [
|
|
1207
|
+
"Provider model catalog",
|
|
1208
|
+
`Provider: ${parsed.value}`,
|
|
1209
|
+
`Status: ${result.reused ? "fresh cache reused" : "refreshed"}`,
|
|
1210
|
+
`File: ${result.filePath}`,
|
|
1211
|
+
`Models: ${result.catalog.models.length}`,
|
|
1212
|
+
`Refreshed at: ${result.catalog.refreshedAt}`
|
|
1213
|
+
];
|
|
1214
|
+
if (result.heuristicFallbackCount > 0) {
|
|
1215
|
+
lines.push(`Warning: ${result.heuristicFallbackCount} model${result.heuristicFallbackCount === 1 ? " was" : "s were"} classified with name heuristics fallback.`);
|
|
1216
|
+
}
|
|
1217
|
+
sendSlashText(pi, lines.join(`
|
|
1218
|
+
`));
|
|
1219
|
+
});
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
pi.registerCommand("subagents-generate-profiles", {
|
|
1226
|
+
description: "Generate <provider>.quota and <provider>.quality subagent profiles",
|
|
1227
|
+
getArgumentCompletions: makeProviderCompletions(state),
|
|
1228
|
+
handler: async (args, ctx) => {
|
|
1229
|
+
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-generate-profiles <provider>");
|
|
1230
|
+
if (!parsed.ok) {
|
|
1231
|
+
ctx.ui.notify(parsed.message, "error");
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
try {
|
|
1235
|
+
await withSlashStatus(ctx, `Generating profiles for ${parsed.value}…`, async () => {
|
|
1236
|
+
const result = await generateProfilesForProvider(pi, ctx, parsed.value, { maxAgeDays: DEFAULT_PROVIDER_MODELS_MAX_AGE_DAYS });
|
|
1237
|
+
const lines = [
|
|
1238
|
+
"Generated subagent profiles",
|
|
1239
|
+
`Provider: ${parsed.value}`,
|
|
1240
|
+
`Catalog: ${result.catalogPath}`,
|
|
1241
|
+
`Quota: ${result.quotaPath}`,
|
|
1242
|
+
` cheap=${result.quotaModels.cheap}`,
|
|
1243
|
+
` medium=${result.quotaModels.medium}`,
|
|
1244
|
+
` strong=${result.quotaModels.strong}`,
|
|
1245
|
+
`Quality: ${result.qualityPath}`,
|
|
1246
|
+
` cheap=${result.qualityModels.cheap}`,
|
|
1247
|
+
` medium=${result.qualityModels.medium}`,
|
|
1248
|
+
` strong=${result.qualityModels.strong}`
|
|
1249
|
+
];
|
|
1250
|
+
if (result.selectedHeuristicFallbackCount > 0) {
|
|
1251
|
+
lines.push(`Warning: generated profiles depend on heuristic-only classification for ${result.selectedHeuristicFallbackCount} selected model${result.selectedHeuristicFallbackCount === 1 ? "" : "s"}.`);
|
|
1252
|
+
} else if (result.heuristicFallbackCount > 0) {
|
|
1253
|
+
lines.push(`Warning: provider catalog still contains ${result.heuristicFallbackCount} heuristic-classified model${result.heuristicFallbackCount === 1 ? "" : "s"}.`);
|
|
1254
|
+
}
|
|
1255
|
+
sendSlashText(pi, lines.join(`
|
|
1256
|
+
`));
|
|
1257
|
+
});
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
pi.registerCommand("subagents-check-profile", {
|
|
1264
|
+
description: "Check whether a saved profile still points to usable models",
|
|
1265
|
+
getArgumentCompletions: (prefix) => {
|
|
1266
|
+
if (prefix.includes(" "))
|
|
1267
|
+
return null;
|
|
1268
|
+
return listSubagentProfiles().filter((name) => name.startsWith(prefix)).map((name) => ({ value: name, label: name }));
|
|
1269
|
+
},
|
|
1270
|
+
handler: async (args, ctx) => {
|
|
1271
|
+
const parsed = parseSingleRequiredArg(args, "Usage: /subagents-check-profile <name>");
|
|
1272
|
+
if (!parsed.ok) {
|
|
1273
|
+
ctx.ui.notify(parsed.message, "error");
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
try {
|
|
1277
|
+
await withSlashStatus(ctx, `Checking profile ${parsed.value}…`, async () => {
|
|
1278
|
+
const result = await checkSubagentProfile(pi, ctx, parsed.value);
|
|
1279
|
+
const lines = [
|
|
1280
|
+
"Subagent profile check",
|
|
1281
|
+
`Profile: ${result.profileName}`,
|
|
1282
|
+
`File: ${result.filePath}`,
|
|
1283
|
+
"",
|
|
1284
|
+
...result.results.map((entry) => `${entry.agent} → ${entry.model} — registry ${entry.inRegistry ? "ok" : "missing"}; probe ${entry.probe.status}${entry.probe.message ? ` (${entry.probe.message.split(/\r?\n/, 1)[0]})` : ""}`)
|
|
1285
|
+
];
|
|
1286
|
+
sendSlashText(pi, lines.join(`
|
|
1287
|
+
`));
|
|
1288
|
+
});
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
}
|