@duckmind/dm-darwin-x64 0.43.4 → 0.48.5
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 +0 -0
- package/extensions/.dm-extensions.json +242 -50
- package/extensions/dm-9router-ext/package.json +2 -19
- package/extensions/dm-9router-ext/src/index.js +5 -0
- package/extensions/dm-ask-user/index.js +10 -0
- package/extensions/dm-ask-user/package.json +1 -21
- package/extensions/dm-cli-anything/package.json +0 -6
- package/extensions/dm-context/package.json +2 -12
- package/extensions/dm-context/src/context.js +1 -0
- package/extensions/dm-context/src/index.js +7 -0
- package/extensions/dm-context/src/utils.js +1 -0
- package/extensions/dm-cua/package.json +0 -7
- package/extensions/dm-fff/package.json +1 -28
- package/extensions/dm-fff/src/index.js +15 -0
- package/extensions/dm-fff/src/query.js +1 -0
- package/extensions/dm-goal/package.json +2 -31
- package/extensions/dm-goal/src/goal.js +43 -0
- package/extensions/dm-grill-me/index.js +174 -0
- package/extensions/dm-grill-me/package.json +1 -26
- package/extensions/dm-image2/package.json +0 -7
- package/extensions/dm-subagents/package.json +1 -32
- package/extensions/dm-subagents/src/agents/agent-management.js +37 -0
- package/extensions/dm-subagents/src/agents/agent-memory.js +6 -0
- package/extensions/dm-subagents/src/agents/agent-scope.js +1 -0
- package/extensions/dm-subagents/src/agents/agent-selection.js +1 -0
- package/extensions/dm-subagents/src/agents/agent-serializer.js +7 -0
- package/extensions/dm-subagents/src/agents/agents.js +11 -0
- package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -0
- package/extensions/dm-subagents/src/agents/frontmatter.js +6 -0
- package/extensions/dm-subagents/src/agents/identity.js +1 -0
- package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -0
- package/extensions/dm-subagents/src/agents/skills.js +6 -0
- package/extensions/dm-subagents/src/extension/config.js +2 -0
- package/extensions/dm-subagents/src/extension/control-notices.js +4 -0
- package/extensions/dm-subagents/src/extension/doctor.js +16 -0
- package/extensions/dm-subagents/src/extension/fanout-child.js +231 -0
- package/extensions/dm-subagents/src/extension/index.js +367 -0
- package/extensions/dm-subagents/src/extension/rpc.js +8 -0
- package/extensions/dm-subagents/src/extension/schemas.js +1 -0
- package/extensions/dm-subagents/src/extension/{tool-description.ts → tool-description.js} +6 -126
- package/extensions/dm-subagents/src/intercom/intercom-bridge.js +13 -0
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +5 -0
- package/extensions/dm-subagents/src/intercom/result-intercom.js +3 -0
- package/extensions/dm-subagents/src/profiles/profiles.js +3 -0
- package/extensions/dm-subagents/src/runs/background/async-execution.js +46 -0
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +14 -0
- package/extensions/dm-subagents/src/runs/background/async-resume.js +8 -0
- package/extensions/dm-subagents/src/runs/background/async-status.js +12 -0
- package/extensions/dm-subagents/src/runs/background/chain-append.js +2 -0
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +1 -0
- package/extensions/dm-subagents/src/runs/background/completion-batcher.js +1 -0
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +1 -0
- package/extensions/dm-subagents/src/runs/background/control-channel.js +1 -0
- package/extensions/dm-subagents/src/runs/background/fleet-view.js +17 -0
- package/extensions/dm-subagents/src/runs/background/notify.js +3 -0
- package/extensions/dm-subagents/src/runs/background/parallel-groups.js +1 -0
- package/extensions/dm-subagents/src/runs/background/result-watcher.js +8 -0
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +4 -0
- package/extensions/dm-subagents/src/runs/background/run-status.js +23 -0
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +4 -0
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +11 -0
- package/extensions/dm-subagents/src/runs/background/subagent-runner.js +78 -0
- package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -0
- package/extensions/dm-subagents/src/runs/background/wait.js +11 -0
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +12 -0
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +102 -0
- package/extensions/dm-subagents/src/runs/foreground/execution.js +51 -0
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +228 -0
- package/extensions/dm-subagents/src/runs/shared/acceptance.js +3 -0
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/completion-guard.js +3 -0
- package/extensions/dm-subagents/src/runs/shared/dm-args.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/model-fallback.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/model-scope.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/nested-events.js +8 -0
- package/extensions/dm-subagents/src/runs/shared/nested-path.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/nested-render.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/run-history.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/single-output.js +14 -0
- package/extensions/dm-subagents/src/runs/shared/structured-output.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/subagent-control.js +5 -0
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +22 -0
- package/extensions/dm-subagents/src/runs/shared/tool-budget.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/turn-budget.js +7 -0
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +1 -0
- package/extensions/dm-subagents/src/runs/shared/worktree.js +3 -0
- package/extensions/dm-subagents/src/shared/artifacts.js +2 -0
- package/extensions/dm-subagents/src/shared/atomic-json.js +1 -0
- package/extensions/dm-subagents/src/shared/child-transcript.js +5 -0
- package/extensions/dm-subagents/src/shared/file-coalescer.js +1 -0
- package/extensions/dm-subagents/src/shared/fork-context.js +6 -0
- package/extensions/dm-subagents/src/shared/formatters.js +9 -0
- package/extensions/dm-subagents/src/shared/jsonl-writer.js +2 -0
- package/extensions/dm-subagents/src/shared/model-info.js +1 -0
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +1 -0
- package/extensions/dm-subagents/src/shared/session-identity.js +1 -0
- package/extensions/dm-subagents/src/shared/session-tokens.js +2 -0
- package/extensions/dm-subagents/src/shared/settings.js +23 -0
- package/extensions/dm-subagents/src/shared/status-format.js +1 -0
- package/extensions/dm-subagents/src/shared/types.js +8 -0
- package/extensions/dm-subagents/src/shared/utils.js +2 -0
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +1 -0
- package/extensions/dm-subagents/src/slash/prompt-workflows.js +7 -0
- package/extensions/dm-subagents/src/slash/slash-bridge.js +1 -0
- package/extensions/dm-subagents/src/slash/slash-commands.js +38 -0
- package/extensions/dm-subagents/src/slash/slash-live-state.js +6 -0
- package/extensions/dm-subagents/src/tui/render-helpers.js +1 -0
- package/extensions/dm-subagents/src/tui/render.js +4 -0
- package/extensions/dm-tasks/package.json +1 -30
- package/extensions/dm-tasks/src/auto-clear.js +1 -0
- package/extensions/dm-tasks/src/index.js +212 -0
- package/extensions/dm-tasks/src/process-tracker.js +1 -0
- package/extensions/dm-tasks/src/task-store.js +1 -0
- package/extensions/dm-tasks/src/tasks-config.js +1 -0
- package/extensions/dm-tasks/src/types.js +0 -0
- package/extensions/dm-tasks/src/ui/settings-menu.js +1 -0
- package/extensions/dm-tasks/src/ui/task-widget.js +1 -0
- package/extensions/dm-usage/index.js +9 -0
- package/extensions/dm-usage/package.json +1 -18
- package/extensions/greedysearch-dm/bin/search.mjs +51 -51
- package/extensions/greedysearch-dm/index.js +20 -0
- package/extensions/greedysearch-dm/package.json +1 -22
- package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
- package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
- package/extensions/greedysearch-dm/src/search/research.mjs +59 -59
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +10 -10
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
- package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
- package/extensions/greedysearch-dm/src/types.js +0 -0
- package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
- package/package.json +1 -1
- package/extensions/dm-9router-ext/src/index.ts +0 -541
- package/extensions/dm-ask-user/index.ts +0 -857
- package/extensions/dm-context/src/context.ts +0 -158
- package/extensions/dm-context/src/index.ts +0 -439
- package/extensions/dm-context/src/utils.ts +0 -6
- package/extensions/dm-fff/src/index.ts +0 -1023
- package/extensions/dm-fff/src/query.ts +0 -87
- package/extensions/dm-goal/src/goal.ts +0 -1073
- package/extensions/dm-grill-me/index.ts +0 -1085
- package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
- package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
- package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
- package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
- package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
- package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
- package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
- package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
- package/extensions/dm-subagents/src/agents/identity.ts +0 -30
- package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
- package/extensions/dm-subagents/src/agents/skills.ts +0 -729
- package/extensions/dm-subagents/src/extension/config.ts +0 -39
- package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
- package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
- package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
- package/extensions/dm-subagents/src/extension/index.ts +0 -656
- package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
- package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
- package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
- package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
- package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
- package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
- package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
- package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
- package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
- package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
- package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
- package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
- package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
- package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
- package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
- package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
- package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
- package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
- package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
- package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
- package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
- package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
- package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
- package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
- package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
- package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
- package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
- package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
- package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
- package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
- package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
- package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
- package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
- package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
- package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
- package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
- package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
- package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
- package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
- package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
- package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
- package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
- package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
- package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
- package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
- package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
- package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
- package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
- package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
- package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
- package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
- package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
- package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
- package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
- package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
- package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
- package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
- package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
- package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
- package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
- package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
- package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
- package/extensions/dm-subagents/src/shared/settings.ts +0 -450
- package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
- package/extensions/dm-subagents/src/shared/types.ts +0 -1257
- package/extensions/dm-subagents/src/shared/utils.ts +0 -554
- package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
- package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
- package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
- package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
- package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
- package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
- package/extensions/dm-subagents/src/tui/render.ts +0 -1748
- package/extensions/dm-tasks/src/auto-clear.ts +0 -91
- package/extensions/dm-tasks/src/index.ts +0 -1145
- package/extensions/dm-tasks/src/process-tracker.ts +0 -140
- package/extensions/dm-tasks/src/task-store.ts +0 -305
- package/extensions/dm-tasks/src/tasks-config.ts +0 -23
- package/extensions/dm-tasks/src/types.ts +0 -40
- package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
- package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
- package/extensions/dm-usage/index.ts +0 -1718
- package/extensions/greedysearch-dm/index.ts +0 -177
- package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
- package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
- package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
- package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
- package/extensions/greedysearch-dm/src/types.ts +0 -110
- package/extensions/greedysearch-dm/src/utils/helpers.ts +0 -40
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import type { ChainConfig, ChainStepConfig } from "./agents.ts";
|
|
2
|
-
import { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.ts";
|
|
3
|
-
import { parseFrontmatter } from "./frontmatter.ts";
|
|
4
|
-
import { ChainOutputValidationError, validateChainOutputBindings } from "../runs/shared/chain-outputs.ts";
|
|
5
|
-
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
6
|
-
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
7
|
-
import type { ChainStep } from "../shared/settings.ts";
|
|
8
|
-
import type { AgentSource } from "./agents.ts";
|
|
9
|
-
|
|
10
|
-
function parseStepBody(agent: string, sectionBody: string): ChainStepConfig {
|
|
11
|
-
const lines = sectionBody.split("\n");
|
|
12
|
-
const blankIndex = lines.findIndex((line) => line.trim() === "");
|
|
13
|
-
const configLines = blankIndex === -1 ? lines : lines.slice(0, blankIndex);
|
|
14
|
-
const task = (blankIndex === -1 ? "" : lines.slice(blankIndex + 1).join("\n")).trim();
|
|
15
|
-
|
|
16
|
-
const step: ChainStepConfig = { agent, task };
|
|
17
|
-
for (const line of configLines) {
|
|
18
|
-
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
19
|
-
if (!match) continue;
|
|
20
|
-
const key = match[1].trim().toLowerCase();
|
|
21
|
-
const rawValue = match[2].trim();
|
|
22
|
-
|
|
23
|
-
if (key === "output") {
|
|
24
|
-
if (rawValue === "false") step.output = false;
|
|
25
|
-
else if (rawValue) step.output = rawValue;
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
if (key === "phase") {
|
|
29
|
-
if (rawValue) step.phase = rawValue;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (key === "label") {
|
|
33
|
-
if (rawValue) step.label = rawValue;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (key === "as") {
|
|
37
|
-
if (rawValue) step.as = rawValue;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (key === "outputschema") {
|
|
41
|
-
if (rawValue.startsWith("{") || rawValue.startsWith("[")) {
|
|
42
|
-
throw new Error("Inline outputSchema values are not supported in .chain.md files; use a schema file path.");
|
|
43
|
-
}
|
|
44
|
-
if (rawValue) step.outputSchema = rawValue;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (key === "outputmode") {
|
|
48
|
-
if (rawValue === "inline" || rawValue === "file-only") step.outputMode = rawValue;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (key === "reads") {
|
|
52
|
-
if (rawValue === "false") {
|
|
53
|
-
step.reads = false;
|
|
54
|
-
} else {
|
|
55
|
-
const reads = rawValue
|
|
56
|
-
.split(",")
|
|
57
|
-
.map((v) => v.trim())
|
|
58
|
-
.filter(Boolean);
|
|
59
|
-
step.reads = reads.length > 0 ? reads : false;
|
|
60
|
-
}
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (key === "model") {
|
|
64
|
-
if (rawValue) step.model = rawValue;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (key === "skills") {
|
|
68
|
-
if (rawValue === "false") {
|
|
69
|
-
step.skills = false;
|
|
70
|
-
} else {
|
|
71
|
-
const skills = rawValue
|
|
72
|
-
.split(",")
|
|
73
|
-
.map((v) => v.trim())
|
|
74
|
-
.filter(Boolean);
|
|
75
|
-
step.skills = skills.length > 0 ? skills : false;
|
|
76
|
-
}
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (key === "progress") {
|
|
80
|
-
if (rawValue === "true") step.progress = true;
|
|
81
|
-
else if (rawValue === "false") step.progress = false;
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (key === "toolbudget") {
|
|
85
|
-
let parsed: unknown;
|
|
86
|
-
try {
|
|
87
|
-
parsed = JSON.parse(rawValue);
|
|
88
|
-
} catch (error) {
|
|
89
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
-
throw new Error(`Invalid toolBudget in .chain.md step '${agent}': ${message}`);
|
|
91
|
-
}
|
|
92
|
-
const validation = validateToolBudgetConfig(parsed, `toolBudget for step '${agent}'`);
|
|
93
|
-
if (validation.error) throw new Error(validation.error);
|
|
94
|
-
step.toolBudget = parsed as ChainStepConfig["toolBudget"];
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return step;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function parseChain(content: string, source: AgentSource, filePath: string): ChainConfig {
|
|
102
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
103
|
-
if (!frontmatter.name || !frontmatter.description) {
|
|
104
|
-
throw new Error("Chain frontmatter must include name and description");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const matches = [...body.matchAll(/^##\s+(.+)[^\S\n]*$/gm)];
|
|
108
|
-
const steps: ChainStepConfig[] = [];
|
|
109
|
-
|
|
110
|
-
for (let i = 0; i < matches.length; i++) {
|
|
111
|
-
const match = matches[i]!;
|
|
112
|
-
const agent = match[1]!.trim();
|
|
113
|
-
const lineEndOffset = body[match.index! + match[0].length] === "\n" ? 1 : 0;
|
|
114
|
-
const sectionStart = match.index! + match[0].length + lineEndOffset;
|
|
115
|
-
const sectionEnd = i + 1 < matches.length ? matches[i + 1]!.index! : body.length;
|
|
116
|
-
const sectionBody = body.slice(sectionStart, sectionEnd).trimEnd();
|
|
117
|
-
steps.push(parseStepBody(agent, sectionBody));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const localName = frontmatter.name;
|
|
121
|
-
const parsedPackage = parsePackageName(frontmatter.package, `Chain '${localName}' package`);
|
|
122
|
-
if (parsedPackage.error) throw new Error(parsedPackage.error);
|
|
123
|
-
const packageName = parsedPackage.packageName;
|
|
124
|
-
const extraFields: Record<string, string> = {};
|
|
125
|
-
for (const [key, value] of Object.entries(frontmatter)) {
|
|
126
|
-
if (key === "name" || key === "package" || key === "description") continue;
|
|
127
|
-
extraFields[key] = value;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
name: buildRuntimeName(localName, packageName),
|
|
132
|
-
localName,
|
|
133
|
-
packageName,
|
|
134
|
-
description: frontmatter.description,
|
|
135
|
-
source,
|
|
136
|
-
filePath,
|
|
137
|
-
steps,
|
|
138
|
-
extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function validateJsonChainToolBudget(value: unknown, label: string): void {
|
|
143
|
-
const validation = validateToolBudgetConfig(value, label);
|
|
144
|
-
if (validation.error) throw new Error(validation.error);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function parseJsonChain(content: string, source: AgentSource, filePath: string): ChainConfig {
|
|
148
|
-
let parsed: unknown;
|
|
149
|
-
try {
|
|
150
|
-
parsed = JSON.parse(content);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
153
|
-
throw new Error(`Invalid JSON chain '${filePath}': ${message}`);
|
|
154
|
-
}
|
|
155
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
156
|
-
throw new Error(`JSON chain '${filePath}' must contain an object root.`);
|
|
157
|
-
}
|
|
158
|
-
const input = parsed as Record<string, unknown>;
|
|
159
|
-
if (typeof input.name !== "string" || !input.name.trim()) {
|
|
160
|
-
throw new Error(`JSON chain '${filePath}' must include string name.`);
|
|
161
|
-
}
|
|
162
|
-
if (typeof input.description !== "string" || !input.description.trim()) {
|
|
163
|
-
throw new Error(`JSON chain '${filePath}' must include string description.`);
|
|
164
|
-
}
|
|
165
|
-
if (!Array.isArray(input.chain)) {
|
|
166
|
-
throw new Error(`JSON chain '${filePath}' must include array chain.`);
|
|
167
|
-
}
|
|
168
|
-
for (let i = 0; i < input.chain.length; i++) {
|
|
169
|
-
const step = input.chain[i];
|
|
170
|
-
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
171
|
-
throw new Error(`JSON chain '${filePath}' step ${i + 1} must be an object.`);
|
|
172
|
-
}
|
|
173
|
-
const stepRecord = step as Record<string, unknown>;
|
|
174
|
-
if (stepRecord.toolBudget !== undefined) validateJsonChainToolBudget(stepRecord.toolBudget, `step ${i + 1} toolBudget`);
|
|
175
|
-
const acceptanceErrors = validateAcceptanceInput(stepRecord.acceptance, `step ${i + 1} acceptance`);
|
|
176
|
-
if (acceptanceErrors.length > 0) {
|
|
177
|
-
throw new Error(`Invalid JSON chain '${filePath}': ${acceptanceErrors.join(" ")}`);
|
|
178
|
-
}
|
|
179
|
-
const parallel = stepRecord.parallel;
|
|
180
|
-
if (Array.isArray(parallel)) {
|
|
181
|
-
for (let taskIndex = 0; taskIndex < parallel.length; taskIndex++) {
|
|
182
|
-
const task = parallel[taskIndex];
|
|
183
|
-
if (!task || typeof task !== "object" || Array.isArray(task)) continue;
|
|
184
|
-
const taskRecord = task as Record<string, unknown>;
|
|
185
|
-
if (taskRecord.toolBudget !== undefined) validateJsonChainToolBudget(taskRecord.toolBudget, `step ${i + 1} parallel task ${taskIndex + 1} toolBudget`);
|
|
186
|
-
const taskErrors = validateAcceptanceInput(taskRecord.acceptance, `step ${i + 1} parallel task ${taskIndex + 1} acceptance`);
|
|
187
|
-
if (taskErrors.length > 0) {
|
|
188
|
-
throw new Error(`Invalid JSON chain '${filePath}': ${taskErrors.join(" ")}`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} else if (parallel && typeof parallel === "object") {
|
|
192
|
-
const parallelRecord = parallel as Record<string, unknown>;
|
|
193
|
-
if (parallelRecord.toolBudget !== undefined) validateJsonChainToolBudget(parallelRecord.toolBudget, `step ${i + 1} dynamic template toolBudget`);
|
|
194
|
-
const templateErrors = validateAcceptanceInput(parallelRecord.acceptance, `step ${i + 1} dynamic template acceptance`);
|
|
195
|
-
if (templateErrors.length > 0) {
|
|
196
|
-
throw new Error(`Invalid JSON chain '${filePath}': ${templateErrors.join(" ")}`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
try {
|
|
201
|
-
validateChainOutputBindings(input.chain as ChainStep[], { maxItems: Number.MAX_SAFE_INTEGER });
|
|
202
|
-
} catch (error) {
|
|
203
|
-
if (error instanceof ChainOutputValidationError) throw new Error(`Invalid JSON chain '${filePath}': ${error.message}`);
|
|
204
|
-
throw error;
|
|
205
|
-
}
|
|
206
|
-
const parsedPackage = parsePackageName(typeof input.package === "string" ? input.package : undefined, `Chain '${input.name}' package`);
|
|
207
|
-
if (parsedPackage.error) throw new Error(parsedPackage.error);
|
|
208
|
-
const extraFields: Record<string, string> = {};
|
|
209
|
-
for (const [key, value] of Object.entries(input)) {
|
|
210
|
-
if (key === "name" || key === "package" || key === "description" || key === "chain") continue;
|
|
211
|
-
if (typeof value === "string") extraFields[key] = value;
|
|
212
|
-
}
|
|
213
|
-
return {
|
|
214
|
-
name: buildRuntimeName(input.name.trim(), parsedPackage.packageName),
|
|
215
|
-
localName: input.name.trim(),
|
|
216
|
-
packageName: parsedPackage.packageName,
|
|
217
|
-
description: input.description.trim(),
|
|
218
|
-
source,
|
|
219
|
-
filePath,
|
|
220
|
-
steps: input.chain as ChainStepConfig[],
|
|
221
|
-
extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function serializeJsonChain(config: ChainConfig): string {
|
|
226
|
-
const root: Record<string, unknown> = {
|
|
227
|
-
name: frontmatterNameForConfig(config),
|
|
228
|
-
description: config.description,
|
|
229
|
-
chain: config.steps,
|
|
230
|
-
};
|
|
231
|
-
if (config.packageName) root.package = config.packageName;
|
|
232
|
-
if (config.extraFields) {
|
|
233
|
-
for (const [key, value] of Object.entries(config.extraFields)) {
|
|
234
|
-
if (key !== "name" && key !== "description" && key !== "package" && key !== "chain") root[key] = value;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return `${JSON.stringify(root, null, 2)}\n`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export function serializeChain(config: ChainConfig): string {
|
|
241
|
-
const lines: string[] = [];
|
|
242
|
-
lines.push("---");
|
|
243
|
-
lines.push(`name: ${frontmatterNameForConfig(config)}`);
|
|
244
|
-
if (config.packageName) lines.push(`package: ${config.packageName}`);
|
|
245
|
-
lines.push(`description: ${config.description}`);
|
|
246
|
-
if (config.extraFields) {
|
|
247
|
-
for (const [key, value] of Object.entries(config.extraFields)) {
|
|
248
|
-
lines.push(`${key}: ${value}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
lines.push("---");
|
|
252
|
-
lines.push("");
|
|
253
|
-
|
|
254
|
-
for (let i = 0; i < config.steps.length; i++) {
|
|
255
|
-
const step = config.steps[i]!;
|
|
256
|
-
lines.push(`## ${step.agent}`);
|
|
257
|
-
if (step.output === false) lines.push("output: false");
|
|
258
|
-
else if (step.output) lines.push(`output: ${step.output}`);
|
|
259
|
-
if (step.phase) lines.push(`phase: ${step.phase}`);
|
|
260
|
-
if (step.label) lines.push(`label: ${step.label}`);
|
|
261
|
-
if (step.as) lines.push(`as: ${step.as}`);
|
|
262
|
-
if (step.outputSchema) lines.push(`outputSchema: ${step.outputSchema}`);
|
|
263
|
-
if (step.outputMode) lines.push(`outputMode: ${step.outputMode}`);
|
|
264
|
-
if (step.reads === false) lines.push("reads: false");
|
|
265
|
-
else if (Array.isArray(step.reads) && step.reads.length > 0) lines.push(`reads: ${step.reads.join(", ")}`);
|
|
266
|
-
if (step.model) lines.push(`model: ${step.model}`);
|
|
267
|
-
if (step.skills === false) lines.push("skills: false");
|
|
268
|
-
else if (Array.isArray(step.skills) && step.skills.length > 0) lines.push(`skills: ${step.skills.join(", ")}`);
|
|
269
|
-
if (step.progress !== undefined) lines.push(`progress: ${step.progress ? "true" : "false"}`);
|
|
270
|
-
if (step.toolBudget !== undefined) lines.push(`toolBudget: ${JSON.stringify(step.toolBudget)}`);
|
|
271
|
-
lines.push("");
|
|
272
|
-
lines.push(step.task ?? "");
|
|
273
|
-
if (i < config.steps.length - 1) lines.push("");
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return `${lines.join("\n")}\n`;
|
|
277
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Escape regex special characters for use in a RegExp constructor.
|
|
3
|
-
*/
|
|
4
|
-
function escapeRegex(s: string): string {
|
|
5
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Parse YAML frontmatter from agent/chain files.
|
|
10
|
-
* Handles both flat (key: value) and nested block (key: \n sub: val) values.
|
|
11
|
-
* Block values are stored as single strings with embedded newlines.
|
|
12
|
-
* The indentation of the block content is preserved relative to the key.
|
|
13
|
-
*/
|
|
14
|
-
export function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {
|
|
15
|
-
const frontmatter: Record<string, string> = {};
|
|
16
|
-
const normalized = content.replace(/\r\n/g, "\n");
|
|
17
|
-
|
|
18
|
-
if (!normalized.startsWith("---")) {
|
|
19
|
-
return { frontmatter, body: normalized };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const endIndex = normalized.indexOf("\n---", 3);
|
|
23
|
-
if (endIndex === -1) {
|
|
24
|
-
return { frontmatter, body: normalized };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const frontmatterBlock = normalized.slice(4, endIndex);
|
|
28
|
-
const body = normalized.slice(endIndex + 4).trim();
|
|
29
|
-
|
|
30
|
-
const lines = frontmatterBlock.split("\n");
|
|
31
|
-
let currentKey: string | null = null;
|
|
32
|
-
let currentBlockLines: string[] | null = null;
|
|
33
|
-
let currentIndent: number | null = null;
|
|
34
|
-
|
|
35
|
-
for (const line of lines) {
|
|
36
|
-
const indent = line.search(/\S|$/); // position of first non-whitespace char
|
|
37
|
-
const trimmed = line.trim();
|
|
38
|
-
|
|
39
|
-
if (currentKey !== null && currentBlockLines !== null && indent > (currentIndent ?? 0)) {
|
|
40
|
-
// This line is part of the current block value
|
|
41
|
-
currentBlockLines.push(line);
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Flush any pending block value
|
|
46
|
-
if (currentKey !== null && currentBlockLines !== null) {
|
|
47
|
-
// Strip the common leading whitespace from the block so the
|
|
48
|
-
// serializer can add its own indentation level.
|
|
49
|
-
const rawBlock = currentBlockLines.join("\n");
|
|
50
|
-
const leadingSpaces = rawBlock.match(/^([ \t]+)/m);
|
|
51
|
-
const prefix = leadingSpaces?.[1] ?? "";
|
|
52
|
-
const stripped = prefix
|
|
53
|
-
? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, "gm"), "").replace(/^\n/, "")
|
|
54
|
-
: rawBlock;
|
|
55
|
-
frontmatter[currentKey] = stripped;
|
|
56
|
-
currentKey = null;
|
|
57
|
-
currentBlockLines = null;
|
|
58
|
-
currentIndent = null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
62
|
-
if (match) {
|
|
63
|
-
let value = match[2].trim();
|
|
64
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
65
|
-
value = value.slice(1, -1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (value === "") {
|
|
69
|
-
// Key with empty value — might start a block; defer storing until we see indent
|
|
70
|
-
currentKey = match[1];
|
|
71
|
-
currentBlockLines = [];
|
|
72
|
-
currentIndent = indent;
|
|
73
|
-
} else {
|
|
74
|
-
// Simple key: value
|
|
75
|
-
frontmatter[match[1]] = value;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// Lines that don't match a key pattern (e.g., comments, empty lines) are ignored
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Flush final block value
|
|
82
|
-
if (currentKey !== null && currentBlockLines !== null) {
|
|
83
|
-
const rawBlock = currentBlockLines.join("\n");
|
|
84
|
-
const leadingSpaces = rawBlock.match(/^([ \t]+)/m);
|
|
85
|
-
const prefix = leadingSpaces?.[1] ?? "";
|
|
86
|
-
const stripped = prefix
|
|
87
|
-
? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, "gm"), "").replace(/^\n/, "")
|
|
88
|
-
: rawBlock;
|
|
89
|
-
frontmatter[currentKey] = stripped;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return { frontmatter, body };
|
|
93
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { AgentConfig, ChainConfig } from "./agents.ts";
|
|
2
|
-
|
|
3
|
-
const IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/;
|
|
4
|
-
|
|
5
|
-
function normalizePackageName(value: string | undefined): string | undefined {
|
|
6
|
-
const trimmed = value?.trim();
|
|
7
|
-
if (!trimmed) return undefined;
|
|
8
|
-
return trimmed.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9.-]/g, "").replace(/-+/g, "-").replace(/\.+/g, ".").replace(/(?:^[-.]+|[-.]+$)/g, "");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function parsePackageName(value: unknown, label = "package"): { packageName?: string; error?: string } {
|
|
12
|
-
if (value === undefined || value === false || value === "") return { packageName: undefined };
|
|
13
|
-
if (typeof value !== "string") return { error: `${label} must be a string or false when provided.` };
|
|
14
|
-
const packageName = normalizePackageName(value);
|
|
15
|
-
if (!packageName || !IDENTIFIER_PATTERN.test(packageName)) return { error: `${label} is invalid after sanitization.` };
|
|
16
|
-
return { packageName };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function buildRuntimeName(localName: string, packageName?: string): string {
|
|
20
|
-
const trimmedPackage = packageName?.trim();
|
|
21
|
-
return trimmedPackage ? `${trimmedPackage}.${localName}` : localName;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function frontmatterNameForConfig(config: Pick<AgentConfig | ChainConfig, "name" | "localName" | "packageName">): string {
|
|
25
|
-
if (config.localName) return config.localName;
|
|
26
|
-
if (config.packageName && config.name.startsWith(`${config.packageName}.`)) {
|
|
27
|
-
return config.name.slice(config.packageName.length + 1);
|
|
28
|
-
}
|
|
29
|
-
return config.name;
|
|
30
|
-
}
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import type { AgentConfig, ChainConfig, ChainStepConfig } from "./agents.ts";
|
|
2
|
-
import type { ProactiveSkillSubagentsConfig } from "../shared/types.ts";
|
|
3
|
-
|
|
4
|
-
const SUBAGENT_ORCHESTRATION_SKILL = "dm-subagents";
|
|
5
|
-
const DEFAULT_MIN_REFERENCES = 2;
|
|
6
|
-
const DEFAULT_MAX_RECOMMENDATIONS = 3;
|
|
7
|
-
const DEFAULT_PREFERRED_AGENT = "reviewer";
|
|
8
|
-
const FALLBACK_AGENT_ORDER = ["reviewer", "context-builder", "delegate"];
|
|
9
|
-
const MAX_RECOMMENDATION_CAP = 5;
|
|
10
|
-
|
|
11
|
-
export interface ResolvedProactiveSkillSubagentsConfig {
|
|
12
|
-
enabled: boolean;
|
|
13
|
-
minReferences: number;
|
|
14
|
-
maxRecommendations: number;
|
|
15
|
-
preferredAgent: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ProactiveSkillSubagentRecommendation {
|
|
19
|
-
skill: string;
|
|
20
|
-
agent: string;
|
|
21
|
-
references: number;
|
|
22
|
-
sources: string[];
|
|
23
|
-
description?: string;
|
|
24
|
-
reason: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface AvailableSkill {
|
|
28
|
-
name: string;
|
|
29
|
-
description?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function positiveInteger(value: unknown): number | undefined {
|
|
33
|
-
if (typeof value !== "number") return undefined;
|
|
34
|
-
if (!Number.isInteger(value) || !Number.isFinite(value) || value < 1) return undefined;
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function resolveProactiveSkillSubagentsConfig(
|
|
39
|
-
config?: ProactiveSkillSubagentsConfig | false,
|
|
40
|
-
): ResolvedProactiveSkillSubagentsConfig {
|
|
41
|
-
if (config === false) {
|
|
42
|
-
return {
|
|
43
|
-
enabled: false,
|
|
44
|
-
minReferences: DEFAULT_MIN_REFERENCES,
|
|
45
|
-
maxRecommendations: DEFAULT_MAX_RECOMMENDATIONS,
|
|
46
|
-
preferredAgent: DEFAULT_PREFERRED_AGENT,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const maxRecommendations = positiveInteger(config?.maxRecommendations) ?? DEFAULT_MAX_RECOMMENDATIONS;
|
|
51
|
-
return {
|
|
52
|
-
enabled: config?.enabled ?? true,
|
|
53
|
-
minReferences: positiveInteger(config?.minReferences) ?? DEFAULT_MIN_REFERENCES,
|
|
54
|
-
maxRecommendations: Math.min(maxRecommendations, MAX_RECOMMENDATION_CAP),
|
|
55
|
-
preferredAgent: typeof config?.preferredAgent === "string" && config.preferredAgent.trim()
|
|
56
|
-
? config.preferredAgent.trim()
|
|
57
|
-
: DEFAULT_PREFERRED_AGENT,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function normalizeSkillNames(value: unknown): string[] {
|
|
62
|
-
if (value === false || value === true || value === undefined || value === null) return [];
|
|
63
|
-
if (Array.isArray(value)) {
|
|
64
|
-
return [...new Set(value.filter((entry): entry is string => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean))];
|
|
65
|
-
}
|
|
66
|
-
if (typeof value === "string") {
|
|
67
|
-
return [...new Set(value.split(",").map((entry) => entry.trim()).filter(Boolean))];
|
|
68
|
-
}
|
|
69
|
-
return [];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function collectStepSkills(step: ChainStepConfig, out: Set<string>): void {
|
|
73
|
-
for (const skill of normalizeSkillNames(step.skills ?? (step as { skill?: unknown }).skill)) {
|
|
74
|
-
out.add(skill);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const parallel = step.parallel;
|
|
78
|
-
if (!parallel) return;
|
|
79
|
-
if (Array.isArray(parallel)) {
|
|
80
|
-
for (const child of parallel) {
|
|
81
|
-
if (child && typeof child === "object" && !Array.isArray(child)) {
|
|
82
|
-
collectStepSkills(child as ChainStepConfig, out);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (typeof parallel === "object") {
|
|
88
|
-
collectStepSkills(parallel as ChainStepConfig, out);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function chooseRecommendationAgent(agents: AgentConfig[], preferredAgent: string): string | undefined {
|
|
93
|
-
const enabled = agents.filter((agent) => !agent.disabled);
|
|
94
|
-
if (enabled.some((agent) => agent.name === preferredAgent)) return preferredAgent;
|
|
95
|
-
for (const name of FALLBACK_AGENT_ORDER) {
|
|
96
|
-
if (enabled.some((agent) => agent.name === name)) return name;
|
|
97
|
-
}
|
|
98
|
-
return enabled[0]?.name;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function addSource(counts: Map<string, Set<string>>, skill: string, source: string): void {
|
|
102
|
-
if (skill === SUBAGENT_ORCHESTRATION_SKILL) return;
|
|
103
|
-
const sources = counts.get(skill) ?? new Set<string>();
|
|
104
|
-
sources.add(source);
|
|
105
|
-
counts.set(skill, sources);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function recommendProactiveSkillSubagents(input: {
|
|
109
|
-
agents: AgentConfig[];
|
|
110
|
-
chains?: ChainConfig[];
|
|
111
|
-
availableSkills?: AvailableSkill[];
|
|
112
|
-
config?: ProactiveSkillSubagentsConfig | false;
|
|
113
|
-
}): ProactiveSkillSubagentRecommendation[] {
|
|
114
|
-
const config = resolveProactiveSkillSubagentsConfig(input.config);
|
|
115
|
-
if (!config.enabled) return [];
|
|
116
|
-
|
|
117
|
-
const agent = chooseRecommendationAgent(input.agents, config.preferredAgent);
|
|
118
|
-
if (!agent) return [];
|
|
119
|
-
|
|
120
|
-
const availableByName = input.availableSkills
|
|
121
|
-
? new Map(input.availableSkills.map((skill) => [skill.name, skill]))
|
|
122
|
-
: undefined;
|
|
123
|
-
const counts = new Map<string, Set<string>>();
|
|
124
|
-
|
|
125
|
-
for (const candidate of input.agents) {
|
|
126
|
-
if (candidate.disabled) continue;
|
|
127
|
-
for (const skill of candidate.skills ?? []) {
|
|
128
|
-
addSource(counts, skill, `agent:${candidate.name}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
for (const chain of input.chains ?? []) {
|
|
133
|
-
const chainSkills = new Set<string>();
|
|
134
|
-
for (const step of chain.steps) {
|
|
135
|
-
collectStepSkills(step, chainSkills);
|
|
136
|
-
}
|
|
137
|
-
for (const skill of chainSkills) {
|
|
138
|
-
addSource(counts, skill, `chain:${chain.name}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return [...counts.entries()]
|
|
143
|
-
.filter(([skill, sources]) => sources.size >= config.minReferences && (!availableByName || availableByName.has(skill)))
|
|
144
|
-
.map(([skill, sources]) => ({
|
|
145
|
-
skill,
|
|
146
|
-
agent,
|
|
147
|
-
references: sources.size,
|
|
148
|
-
sources: [...sources].sort((a, b) => a.localeCompare(b)),
|
|
149
|
-
description: availableByName?.get(skill)?.description,
|
|
150
|
-
reason: `referenced by ${sources.size} configured agents/chains`,
|
|
151
|
-
}))
|
|
152
|
-
.sort((a, b) => b.references - a.references || a.skill.localeCompare(b.skill))
|
|
153
|
-
.slice(0, config.maxRecommendations);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function formatProactiveSkillSubagentRecommendations(
|
|
157
|
-
recommendations: ProactiveSkillSubagentRecommendation[],
|
|
158
|
-
): string[] {
|
|
159
|
-
if (recommendations.length === 0) return [];
|
|
160
|
-
return [
|
|
161
|
-
"Proactive skill subagent suggestions:",
|
|
162
|
-
...recommendations.map((recommendation) => {
|
|
163
|
-
const sampleSources = recommendation.sources.slice(0, 3).join(", ");
|
|
164
|
-
const extra = recommendation.sources.length > 3 ? `, +${recommendation.sources.length - 3} more` : "";
|
|
165
|
-
const description = recommendation.description ? ` - ${recommendation.description}` : "";
|
|
166
|
-
return `- ${recommendation.skill} via ${recommendation.agent} (${recommendation.reason}; ${sampleSources}${extra})${description}`;
|
|
167
|
-
}),
|
|
168
|
-
"Guardrails: use these for broad tasks where a skill-specialist pass is useful; keep fanout small, use fresh context unless private/session context is explicitly needed, and skip when the user asks for a direct answer.",
|
|
169
|
-
];
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export function buildProactiveSkillSubagentRecommendationLines(input: {
|
|
173
|
-
agents: AgentConfig[];
|
|
174
|
-
chains?: ChainConfig[];
|
|
175
|
-
config?: ProactiveSkillSubagentsConfig | false;
|
|
176
|
-
discoverAvailableSkills: () => AvailableSkill[];
|
|
177
|
-
}): string[] {
|
|
178
|
-
if (!resolveProactiveSkillSubagentsConfig(input.config).enabled) return [];
|
|
179
|
-
let availableSkills: AvailableSkill[];
|
|
180
|
-
try {
|
|
181
|
-
availableSkills = input.discoverAvailableSkills();
|
|
182
|
-
} catch {
|
|
183
|
-
availableSkills = [];
|
|
184
|
-
}
|
|
185
|
-
return formatProactiveSkillSubagentRecommendations(recommendProactiveSkillSubagents({
|
|
186
|
-
agents: input.agents,
|
|
187
|
-
chains: input.chains,
|
|
188
|
-
availableSkills,
|
|
189
|
-
config: input.config,
|
|
190
|
-
}));
|
|
191
|
-
}
|