ocmm 0.5.4 → 0.6.0
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/.codex/agents/dw-builder.toml +1 -1
- package/.codex/agents/dw-clarifier.toml +1 -1
- package/.codex/agents/dw-code-search.toml +1 -1
- package/.codex/agents/dw-coding.toml +1 -1
- package/.codex/agents/dw-complex.toml +1 -1
- package/.codex/agents/dw-creative.toml +1 -1
- package/.codex/agents/dw-deep.toml +1 -1
- package/.codex/agents/dw-doc-search.toml +1 -1
- package/.codex/agents/dw-documenting.toml +1 -1
- package/.codex/agents/dw-explore.toml +1 -1
- package/.codex/agents/dw-frontend.toml +1 -1
- package/.codex/agents/dw-hard-reasoning.toml +1 -1
- package/.codex/agents/dw-media-reader.toml +1 -1
- package/.codex/agents/dw-normal-task.toml +1 -1
- package/.codex/agents/dw-oracle-2nd.toml +8 -0
- package/.codex/agents/dw-oracle.toml +1 -1
- package/.codex/agents/dw-orchestrator.toml +1 -1
- package/.codex/agents/dw-plan-critic.toml +1 -1
- package/.codex/agents/dw-planner.toml +1 -1
- package/.codex/agents/dw-quick.toml +1 -1
- package/.codex/agents/dw-research.toml +1 -1
- package/.codex/agents/dw-reviewer.toml +1 -1
- package/README.md +158 -13
- package/dist/cli/shim.d.ts +4 -2
- package/dist/cli/shim.js +26 -11
- package/dist/cli/shim.js.map +1 -1
- package/dist/codex/plugin-generator.js +82 -56
- package/dist/codex/plugin-generator.js.map +1 -1
- package/dist/config/load.d.ts +32 -23
- package/dist/config/load.js +384 -90
- package/dist/config/load.js.map +1 -1
- package/dist/config/merge.d.ts +18 -0
- package/dist/config/merge.js +46 -0
- package/dist/config/merge.js.map +1 -0
- package/dist/config/normalize.d.ts +1 -0
- package/dist/config/normalize.js +22 -16
- package/dist/config/normalize.js.map +1 -1
- package/dist/config/profile-aliases.d.ts +12 -0
- package/dist/config/profile-aliases.js +127 -0
- package/dist/config/profile-aliases.js.map +1 -0
- package/dist/config/profile-types.d.ts +13 -0
- package/dist/config/profile-types.js +2 -0
- package/dist/config/profile-types.js.map +1 -0
- package/dist/config/review-agent-migration.d.ts +89 -0
- package/dist/config/review-agent-migration.js +237 -0
- package/dist/config/review-agent-migration.js.map +1 -0
- package/dist/config/schema.d.ts +1364 -222
- package/dist/config/schema.js +169 -37
- package/dist/config/schema.js.map +1 -1
- package/dist/config/tolerant-parse.d.ts +41 -0
- package/dist/config/tolerant-parse.js +164 -0
- package/dist/config/tolerant-parse.js.map +1 -0
- package/dist/data/agents.d.ts +1 -1
- package/dist/data/agents.js +7 -7
- package/dist/data/agents.js.map +1 -1
- package/dist/hooks/chat-params.d.ts +2 -0
- package/dist/hooks/chat-params.js +113 -4
- package/dist/hooks/chat-params.js.map +1 -1
- package/dist/hooks/config.d.ts +14 -2
- package/dist/hooks/config.js +538 -60
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/event.d.ts +3 -11
- package/dist/hooks/event.js +5 -9
- package/dist/hooks/event.js.map +1 -1
- package/dist/index.js +21 -14
- package/dist/index.js.map +1 -1
- package/dist/permissions/index.d.ts +3 -0
- package/dist/permissions/index.js +43 -28
- package/dist/permissions/index.js.map +1 -1
- package/dist/review-agents/expand.d.ts +36 -0
- package/dist/review-agents/expand.js +157 -0
- package/dist/review-agents/expand.js.map +1 -0
- package/dist/review-agents/names.d.ts +16 -0
- package/dist/review-agents/names.js +55 -0
- package/dist/review-agents/names.js.map +1 -0
- package/dist/routing/effective-route.d.ts +19 -0
- package/dist/routing/effective-route.js +133 -0
- package/dist/routing/effective-route.js.map +1 -0
- package/dist/routing/model-upgrades.d.ts +5 -0
- package/dist/routing/model-upgrades.js +59 -12
- package/dist/routing/model-upgrades.js.map +1 -1
- package/dist/routing/resolver.d.ts +8 -1
- package/dist/routing/resolver.js +29 -9
- package/dist/routing/resolver.js.map +1 -1
- package/dist/routing/route-registry.d.ts +13 -0
- package/dist/routing/route-registry.js +84 -0
- package/dist/routing/route-registry.js.map +1 -0
- package/dist/runtime-fallback/dispatcher.d.ts +1 -0
- package/dist/runtime-fallback/dispatcher.js +7 -5
- package/dist/runtime-fallback/dispatcher.js.map +1 -1
- package/dist/runtime-fallback/error-classifier.d.ts +3 -1
- package/dist/runtime-fallback/error-classifier.js +135 -1
- package/dist/runtime-fallback/error-classifier.js.map +1 -1
- package/dist/runtime-fallback/event-handler-generic-fallback.d.ts +26 -0
- package/dist/runtime-fallback/event-handler-generic-fallback.js +54 -0
- package/dist/runtime-fallback/event-handler-generic-fallback.js.map +1 -0
- package/dist/runtime-fallback/event-handler-idle-continuation.d.ts +9 -0
- package/dist/runtime-fallback/event-handler-idle-continuation.js +48 -0
- package/dist/runtime-fallback/event-handler-idle-continuation.js.map +1 -0
- package/dist/runtime-fallback/event-handler-support.d.ts +39 -0
- package/dist/runtime-fallback/event-handler-support.js +194 -0
- package/dist/runtime-fallback/event-handler-support.js.map +1 -0
- package/dist/runtime-fallback/event-handler-test-fixtures.d.ts +874 -0
- package/dist/runtime-fallback/event-handler-test-fixtures.js +150 -0
- package/dist/runtime-fallback/event-handler-test-fixtures.js.map +1 -0
- package/dist/runtime-fallback/event-handler.d.ts +13 -1
- package/dist/runtime-fallback/event-handler.js +334 -192
- package/dist/runtime-fallback/event-handler.js.map +1 -1
- package/dist/runtime-fallback/fallback-state.d.ts +8 -5
- package/dist/runtime-fallback/fallback-state.js +9 -6
- package/dist/runtime-fallback/fallback-state.js.map +1 -1
- package/dist/runtime-fallback/index.d.ts +21 -1
- package/dist/runtime-fallback/index.js +7 -1
- package/dist/runtime-fallback/index.js.map +1 -1
- package/dist/runtime-fallback/interruption-output-adapter.d.ts +7 -0
- package/dist/runtime-fallback/interruption-output-adapter.js +91 -0
- package/dist/runtime-fallback/interruption-output-adapter.js.map +1 -0
- package/dist/runtime-fallback/subagent-429-controller-fixture.d.ts +105 -0
- package/dist/runtime-fallback/subagent-429-controller-fixture.js +116 -0
- package/dist/runtime-fallback/subagent-429-controller-fixture.js.map +1 -0
- package/dist/runtime-fallback/subagent-429-controller.d.ts +167 -0
- package/dist/runtime-fallback/subagent-429-controller.js +334 -0
- package/dist/runtime-fallback/subagent-429-controller.js.map +1 -0
- package/dist/runtime-fallback/subagent-429-policy.d.ts +13 -0
- package/dist/runtime-fallback/subagent-429-policy.js +37 -0
- package/dist/runtime-fallback/subagent-429-policy.js.map +1 -0
- package/dist/runtime-fallback/subagent-429-session.d.ts +48 -0
- package/dist/runtime-fallback/subagent-429-session.js +316 -0
- package/dist/runtime-fallback/subagent-429-session.js.map +1 -0
- package/dist/shared/opencode-events.d.ts +22 -0
- package/dist/shared/opencode-events.js +75 -0
- package/dist/shared/opencode-events.js.map +1 -0
- package/dist/shared/types.d.ts +12 -1
- package/package.json +1 -1
- package/plugins/deepwork/.codex-plugin/plugin.json +1 -1
- package/plugins/deepwork/README.md +2 -1
- package/plugins/deepwork/agents/dw-builder.toml +1 -1
- package/plugins/deepwork/agents/dw-clarifier.toml +1 -1
- package/plugins/deepwork/agents/dw-code-search.toml +1 -1
- package/plugins/deepwork/agents/dw-coding.toml +1 -1
- package/plugins/deepwork/agents/dw-complex.toml +1 -1
- package/plugins/deepwork/agents/dw-creative.toml +1 -1
- package/plugins/deepwork/agents/dw-deep.toml +1 -1
- package/plugins/deepwork/agents/dw-doc-search.toml +1 -1
- package/plugins/deepwork/agents/dw-documenting.toml +1 -1
- package/plugins/deepwork/agents/dw-explore.toml +1 -1
- package/plugins/deepwork/agents/dw-frontend.toml +1 -1
- package/plugins/deepwork/agents/dw-hard-reasoning.toml +1 -1
- package/plugins/deepwork/agents/dw-media-reader.toml +1 -1
- package/plugins/deepwork/agents/dw-normal-task.toml +1 -1
- package/plugins/deepwork/agents/dw-oracle-2nd.toml +8 -0
- package/plugins/deepwork/agents/dw-oracle.toml +1 -1
- package/plugins/deepwork/agents/dw-orchestrator.toml +1 -1
- package/plugins/deepwork/agents/dw-plan-critic.toml +1 -1
- package/plugins/deepwork/agents/dw-planner.toml +1 -1
- package/plugins/deepwork/agents/dw-quick.toml +1 -1
- package/plugins/deepwork/agents/dw-research.toml +1 -1
- package/plugins/deepwork/agents/dw-reviewer.toml +1 -1
- package/plugins/deepwork/dist/cli/shim.d.ts +4 -2
- package/plugins/deepwork/dist/cli/shim.js +26 -11
- package/plugins/deepwork/dist/cli/shim.js.map +1 -1
- package/plugins/deepwork/dist/shared/opencode-events.d.ts +22 -0
- package/plugins/deepwork/dist/shared/opencode-events.js +75 -0
- package/plugins/deepwork/dist/shared/opencode-events.js.map +1 -0
- package/plugins/deepwork/dist/shared/types.d.ts +12 -1
- package/plugins/deepwork/package.json +1 -1
- package/plugins/deepwork/skills/deepwork/SKILL.md +30 -14
- package/plugins/deepwork/skills/deepwork-requesting-code-review/SKILL.md +53 -31
- package/plugins/deepwork/skills/deepwork-requesting-code-review/code-reviewer.md +11 -7
- package/plugins/deepwork/skills/deepwork-subagent-driven-development/SKILL.md +28 -19
- package/plugins/deepwork/skills/deepwork-subagent-driven-development/implementer-prompt.md +10 -3
- package/prompts/codex/agents/clarifier.md +4 -0
- package/prompts/codex/agents/orchestrator.md +12 -2
- package/prompts/codex/agents/plan-critic.md +4 -0
- package/prompts/codex/agents/planner.md +15 -10
- package/prompts/codex/agents/reviewer.md +8 -2
- package/prompts/codex/deepwork/gpt-5.6.md +20 -37
- package/prompts/omo/agents/clarifier.md +4 -0
- package/prompts/omo/agents/orchestrator.md +11 -0
- package/prompts/omo/agents/plan-critic.md +4 -0
- package/prompts/omo/agents/planner.md +15 -3
- package/prompts/omo/agents/reviewer.md +8 -2
- package/prompts/omo/deepwork/gpt-5.6.md +20 -37
- package/prompts/v1/agents/clarifier.md +4 -0
- package/prompts/v1/agents/orchestrator.md +12 -2
- package/prompts/v1/agents/plan-critic.md +4 -0
- package/prompts/v1/agents/planner.md +15 -10
- package/prompts/v1/agents/reviewer.md +8 -2
- package/prompts/v1/deepwork/gpt-5.6.md +20 -37
- package/skills/v1/requesting-code-review/SKILL.md +53 -31
- package/skills/v1/requesting-code-review/code-reviewer.md +11 -7
- package/skills/v1/subagent-driven-development/SKILL.md +28 -19
- package/skills/v1/subagent-driven-development/implementer-prompt.md +10 -3
- package/.codex/agents/dw-oracle-high.toml +0 -8
- package/plugins/deepwork/agents/dw-oracle-high.toml +0 -8
package/dist/hooks/config.js
CHANGED
|
@@ -7,42 +7,80 @@ import { getAgentPrompt, getCategoryPrompt, getDeepworkPrompt, isGpt56Model, pic
|
|
|
7
7
|
import { buildSkillCommand, DEFAULT_SKILLS_ROOT, loadSharedSkills, loadV1SkillCommands } from "../intent/skill-loader.js";
|
|
8
8
|
import { resolveMcpServers } from "../mcp/index.js";
|
|
9
9
|
import { normalizeAgentShorthand, normalizeShorthand } from "../config/normalize.js";
|
|
10
|
+
import { buildEffectiveModelRoute } from "../routing/effective-route.js";
|
|
10
11
|
import { selectCatalogModel } from "../routing/model-upgrades.js";
|
|
12
|
+
import { resolveEffectiveRequirement } from "../routing/resolver.js";
|
|
11
13
|
import { isRecord, log } from "../shared/logger.js";
|
|
14
|
+
import { expandReviewAgents, isExpandedReviewAgentDisabled } from "../review-agents/expand.js";
|
|
15
|
+
import { isReviewAgentName, parseReviewAgentName } from "../review-agents/names.js";
|
|
12
16
|
const COMPAT_AGENT_ALIASES = [
|
|
13
17
|
{ alias: "explore", target: "code-search" },
|
|
14
18
|
];
|
|
19
|
+
const PRIMARY_COORDINATORS = ["orchestrator", "builder"];
|
|
20
|
+
const UTILITY_LEAF_AGENTS = [
|
|
21
|
+
"quick",
|
|
22
|
+
"code-search",
|
|
23
|
+
"explore",
|
|
24
|
+
"doc-search",
|
|
25
|
+
"research",
|
|
26
|
+
"media-reader",
|
|
27
|
+
];
|
|
28
|
+
const READ_ONLY_UTILITY_AGENTS = [
|
|
29
|
+
"code-search",
|
|
30
|
+
"explore",
|
|
31
|
+
"doc-search",
|
|
32
|
+
"research",
|
|
33
|
+
"media-reader",
|
|
34
|
+
];
|
|
35
|
+
const STANDARD_WORKFLOW_SUBAGENTS = [
|
|
36
|
+
"coding",
|
|
37
|
+
"normal-task",
|
|
38
|
+
"frontend",
|
|
39
|
+
"creative",
|
|
40
|
+
"hard-reasoning",
|
|
41
|
+
"documenting",
|
|
42
|
+
];
|
|
43
|
+
const READ_ONLY_WORKFLOW_AGENTS = [
|
|
44
|
+
"clarifier",
|
|
45
|
+
];
|
|
46
|
+
const LOCAL_COORDINATORS = ["deep", "complex"];
|
|
47
|
+
const SPECIALIST_EXECUTION_AGENTS = [
|
|
48
|
+
"coding",
|
|
49
|
+
"frontend",
|
|
50
|
+
"hard-reasoning",
|
|
51
|
+
"creative",
|
|
52
|
+
"documenting",
|
|
53
|
+
];
|
|
54
|
+
const QUESTION_ENABLED_WORKFLOW_AGENTS = ["planner", "deep", "complex", "coding", "normal-task"];
|
|
55
|
+
function taskAllowlist(allowed) {
|
|
56
|
+
const rules = { "*": "deny" };
|
|
57
|
+
for (const name of allowed)
|
|
58
|
+
rules[name] = "allow";
|
|
59
|
+
return rules;
|
|
60
|
+
}
|
|
15
61
|
const LOCALE_GUIDANCE_TAG = "ocmm-locale-guidance";
|
|
16
62
|
const LOCALE_GUIDANCE_BLOCK = new RegExp(`<${LOCALE_GUIDANCE_TAG}>[\\s\\S]*?<\\/${LOCALE_GUIDANCE_TAG}>\\s*(?:---\\s*)?`, "g");
|
|
17
63
|
function fmtModel(entry) {
|
|
18
64
|
const provider = entry.providers[0] ?? "";
|
|
19
65
|
return provider ? `${provider}/${entry.model}` : entry.model;
|
|
20
66
|
}
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!isRecord(entry))
|
|
67
|
+
function applyAgentEntry(agentMap, agent, override, extras, registration) {
|
|
68
|
+
const current = agentMap[agent.name];
|
|
69
|
+
if (isRecord(current) && current.disable === true)
|
|
25
70
|
return false;
|
|
26
|
-
return ["model", "fallbackModels", "requirement", "alias"].some((key) => entry[key] !== undefined);
|
|
27
|
-
}
|
|
28
|
-
function rawAgentModel(agentMap, name) {
|
|
29
|
-
const entry = agentMap[name];
|
|
30
|
-
return isRecord(entry) && typeof entry.model === "string" ? entry.model : undefined;
|
|
31
|
-
}
|
|
32
|
-
function applyAgentEntry(agentMap, agent, override, extras) {
|
|
33
71
|
if (override?.disabled)
|
|
34
|
-
return;
|
|
72
|
+
return false;
|
|
35
73
|
let chain = agent.requirement.fallbackChain;
|
|
36
74
|
const description = override?.description ?? agent.description;
|
|
37
75
|
if (override?.requirement?.fallbackChain?.length) {
|
|
38
76
|
chain = override.requirement.fallbackChain;
|
|
39
77
|
}
|
|
40
78
|
if (!chain.length)
|
|
41
|
-
return;
|
|
79
|
+
return false;
|
|
42
80
|
const head = chain[0];
|
|
43
81
|
const modelStr = extras?.model ?? fmtModel(head);
|
|
44
|
-
const existing = isRecord(
|
|
45
|
-
?
|
|
82
|
+
const existing = isRecord(current)
|
|
83
|
+
? current
|
|
46
84
|
: {};
|
|
47
85
|
if (typeof existing.model !== "string")
|
|
48
86
|
existing.model = modelStr;
|
|
@@ -56,9 +94,31 @@ function applyAgentEntry(agentMap, agent, override, extras) {
|
|
|
56
94
|
if (extras?.promptPrefix && typeof existing.prompt === "string") {
|
|
57
95
|
existing.prompt = prependPromptPrefix(existing.prompt, extras.promptPrefix);
|
|
58
96
|
}
|
|
97
|
+
if (extras?.promptSuffix) {
|
|
98
|
+
const basePrompt = typeof existing.prompt === "string" ? existing.prompt : "";
|
|
99
|
+
existing.prompt = appendPromptSuffix(basePrompt, extras.promptSuffix);
|
|
100
|
+
}
|
|
101
|
+
if (registration?.tools) {
|
|
102
|
+
mergePermission(existing, Object.fromEntries(Object.entries(registration.tools).map(([name, enabled]) => [name, enabled ? "allow" : "deny"])), true);
|
|
103
|
+
}
|
|
59
104
|
if (override?.permission)
|
|
60
105
|
mergePermission(existing, override.permission, true);
|
|
106
|
+
if (registration) {
|
|
107
|
+
for (const key of ["skills", "temperature", "topP", "maxTokens", "thinking", "reasoningEffort"]) {
|
|
108
|
+
if (existing[key] !== undefined)
|
|
109
|
+
continue;
|
|
110
|
+
const value = registration[key];
|
|
111
|
+
if (value === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
existing[key] = Array.isArray(value)
|
|
114
|
+
? [...value]
|
|
115
|
+
: typeof value === "object" && value !== null
|
|
116
|
+
? structuredClone(value)
|
|
117
|
+
: value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
61
120
|
agentMap[agent.name] = existing;
|
|
121
|
+
return true;
|
|
62
122
|
}
|
|
63
123
|
function buildLocaleGuidance(locale) {
|
|
64
124
|
const configuredLocale = locale?.trim();
|
|
@@ -86,6 +146,74 @@ function prependPromptPrefix(prompt, prefix) {
|
|
|
86
146
|
const cleanPrefix = prefix.trim();
|
|
87
147
|
return body ? `${cleanPrefix}\n\n---\n\n${body}` : cleanPrefix;
|
|
88
148
|
}
|
|
149
|
+
const DELEGATION_CONTRACT_TAG = "ocmm-delegation-contract";
|
|
150
|
+
const DELEGATION_CONTRACT_BLOCK = new RegExp(`(?:\\n\\n---\\n\\n)?<${DELEGATION_CONTRACT_TAG}>[\\s\\S]*?<\\/${DELEGATION_CONTRACT_TAG}>\\s*`, "g");
|
|
151
|
+
function appendPromptSuffix(prompt, suffix) {
|
|
152
|
+
const body = prompt.replace(DELEGATION_CONTRACT_BLOCK, "").trim();
|
|
153
|
+
const cleanSuffix = suffix.trim();
|
|
154
|
+
return body ? `${body}\n\n---\n\n${cleanSuffix}` : cleanSuffix;
|
|
155
|
+
}
|
|
156
|
+
const UTILITY_LEAF_AGENT_SET = new Set(UTILITY_LEAF_AGENTS);
|
|
157
|
+
const STANDARD_WORKFLOW_SUBAGENT_SET = new Set(STANDARD_WORKFLOW_SUBAGENTS);
|
|
158
|
+
const READ_ONLY_WORKFLOW_AGENT_SET = new Set(READ_ONLY_WORKFLOW_AGENTS);
|
|
159
|
+
const LOCAL_COORDINATOR_SET = new Set(LOCAL_COORDINATORS);
|
|
160
|
+
function formatTargets(names) {
|
|
161
|
+
return names.map((name) => `\`${name}\``).join(", ");
|
|
162
|
+
}
|
|
163
|
+
function wrapDelegationContract(lines) {
|
|
164
|
+
return [
|
|
165
|
+
`<${DELEGATION_CONTRACT_TAG}>`,
|
|
166
|
+
"## Delegation Contract (Authoritative)",
|
|
167
|
+
...lines,
|
|
168
|
+
"This contract overrides any skill, model calibration, generated-adapter compatibility text, or other prompt layer that suggests broader delegation.",
|
|
169
|
+
`</${DELEGATION_CONTRACT_TAG}>`,
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
function delegationContractFor(name) {
|
|
173
|
+
const readOnlyReviewRole = isReviewAgentName(name) || name === "plan-critic";
|
|
174
|
+
if (UTILITY_LEAF_AGENT_SET.has(name)) {
|
|
175
|
+
return wrapDelegationContract([
|
|
176
|
+
"This role is a utility leaf agent. Do not dispatch any subagent.",
|
|
177
|
+
"Complete the bounded assignment with direct tools and return the result to the caller.",
|
|
178
|
+
]);
|
|
179
|
+
}
|
|
180
|
+
if (name === "planner") {
|
|
181
|
+
return wrapDelegationContract([
|
|
182
|
+
"Use direct tools first. Delegate only when direct tools are insufficient and a separate bounded research result materially improves completion.",
|
|
183
|
+
`Allowed utility targets: ${formatTargets(READ_ONLY_UTILITY_AGENTS)}.`,
|
|
184
|
+
"You may consult exactly the unsuffixed `reviewer` at most once, only for one concrete blocking architecture, security, or performance decision that repository evidence cannot settle. This is not formal plan review or final acceptance.",
|
|
185
|
+
"`quick` is forbidden. Do not dispatch `plan-critic`, any Reviewer tier (`reviewer-low`, `reviewer-high`, `reviewer-max`), any Oracle profile, implementation agents, or routine reviewer self-checks.",
|
|
186
|
+
"Return the completed plan to the caller. Formal planner dispatch, the `plan-critic` loop, review dispatch, and final acceptance review are orchestrator-owned.",
|
|
187
|
+
]);
|
|
188
|
+
}
|
|
189
|
+
if (READ_ONLY_WORKFLOW_AGENT_SET.has(name) || readOnlyReviewRole) {
|
|
190
|
+
return wrapDelegationContract([
|
|
191
|
+
"Use direct tools first. Delegate only when direct tools are insufficient and a separate bounded research result materially improves completion.",
|
|
192
|
+
`Allowed utility targets: ${formatTargets(READ_ONLY_UTILITY_AGENTS)}.`,
|
|
193
|
+
"`quick` is forbidden because this read-only role must not modify work by proxy. Planning, review, coordination, and implementation workflow agents are also forbidden.",
|
|
194
|
+
"Return the completed plan or findings to the caller. Formal planner dispatch, the `plan-critic` loop, review dispatch, and final acceptance review are orchestrator-owned.",
|
|
195
|
+
]);
|
|
196
|
+
}
|
|
197
|
+
if (STANDARD_WORKFLOW_SUBAGENT_SET.has(name)) {
|
|
198
|
+
return wrapDelegationContract([
|
|
199
|
+
"Use direct tools first. Delegate only when direct tools are insufficient or a separate bounded utility result materially improves completion.",
|
|
200
|
+
`Allowed utility targets: ${formatTargets(UTILITY_LEAF_AGENTS)}.`,
|
|
201
|
+
"Do not dispatch planning, review, coordination, or implementation workflow agents.",
|
|
202
|
+
"After local verification, return status and evidence to the caller. Formal planner dispatch, the `plan-critic` loop, review dispatch, and final acceptance review are orchestrator-owned.",
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
if (LOCAL_COORDINATOR_SET.has(name)) {
|
|
206
|
+
return wrapDelegationContract([
|
|
207
|
+
"Use direct tools first. Delegate only when the child owns a distinct bounded deliverable that materially improves completion.",
|
|
208
|
+
"Multiple steps, routine confirmation, or wanting another opinion are not sufficient.",
|
|
209
|
+
`Allowed utility targets: ${formatTargets(UTILITY_LEAF_AGENTS)}.`,
|
|
210
|
+
`Allowed specialist targets: ${formatTargets(SPECIALIST_EXECUTION_AGENTS)}.`,
|
|
211
|
+
"Do not call `orchestrator`, `builder`, `planner`, `clarifier`, `plan-critic`, any Reviewer profile (`reviewer`, `reviewer-low`, `reviewer-high`, `reviewer-max`), any Oracle profile (`oracle`, `oracle-2nd`, configured `oracle-3rd`…`oracle-9th`, and their `low`/`high`/`max` tier variants), `normal-task`, `deep`, or `complex`.",
|
|
212
|
+
"Integrate and verify child results, then return to the parent. Formal planner dispatch, the `plan-critic` loop, review dispatch, and final acceptance review are orchestrator-owned.",
|
|
213
|
+
]);
|
|
214
|
+
}
|
|
215
|
+
return "";
|
|
216
|
+
}
|
|
89
217
|
function mergePermission(entry, permission, overwrite) {
|
|
90
218
|
const existing = isRecord(entry.permission) ? entry.permission : {};
|
|
91
219
|
const merged = { ...existing };
|
|
@@ -146,23 +274,128 @@ function categoryAsAgent(c, override) {
|
|
|
146
274
|
requirement: override ?? c.requirement,
|
|
147
275
|
};
|
|
148
276
|
}
|
|
277
|
+
function hasRouteRegistry(args) {
|
|
278
|
+
return args.routeRegistry !== undefined;
|
|
279
|
+
}
|
|
280
|
+
function requirementSourceForRoute(source) {
|
|
281
|
+
if (source === "user-config" || source === "agent-default" || source === "category-default") {
|
|
282
|
+
return source;
|
|
283
|
+
}
|
|
284
|
+
throw new Error(`unsupported route requirement source: ${source}`);
|
|
285
|
+
}
|
|
286
|
+
function resolveRouteRequirement(cfg, agentName) {
|
|
287
|
+
const resolved = resolveEffectiveRequirement({
|
|
288
|
+
agentName,
|
|
289
|
+
agentsConfig: cfg.agents,
|
|
290
|
+
categoriesConfig: cfg.categories,
|
|
291
|
+
disabledAgents: cfg.disabledAgents,
|
|
292
|
+
});
|
|
293
|
+
if (!resolved)
|
|
294
|
+
return null;
|
|
295
|
+
return {
|
|
296
|
+
requirement: resolved.requirement,
|
|
297
|
+
source: requirementSourceForRoute(resolved.source),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function selectRoutePrimary(args) {
|
|
301
|
+
if (args.existingModel)
|
|
302
|
+
return { model: args.existingModel, source: "existing-model" };
|
|
303
|
+
if (args.requirementSource === "user-config") {
|
|
304
|
+
return { model: fmtModel(args.requirement.fallbackChain[0]), source: "user-requirement" };
|
|
305
|
+
}
|
|
306
|
+
const catalogModel = args.allowCatalogUpgrade
|
|
307
|
+
? selectCatalogModel(args.target, args.agentName, args.requirement)
|
|
308
|
+
: undefined;
|
|
309
|
+
if (catalogModel)
|
|
310
|
+
return { model: catalogModel, source: "catalog-upgrade" };
|
|
311
|
+
return { model: fmtModel(args.requirement.fallbackChain[0]), source: "builtin-requirement" };
|
|
312
|
+
}
|
|
313
|
+
function hasExplicitRouteSelection(cfg, name) {
|
|
314
|
+
return [cfg.agents?.[name], cfg.categories?.[name]].some((entry) => entry !== undefined && ["model", "fallbackModels", "requirement", "alias"].some((field) => Object.prototype.hasOwnProperty.call(entry, field)));
|
|
315
|
+
}
|
|
316
|
+
function hasOwnAgentEntry(cfg, name) {
|
|
317
|
+
return cfg.agents !== undefined && Object.prototype.hasOwnProperty.call(cfg.agents, name);
|
|
318
|
+
}
|
|
319
|
+
function isCompatAliasDisabled(alias, target, agents) {
|
|
320
|
+
return [alias, target].some((name) => agents?.[name]?.disabled === true || normalizeAgentShorthand(name, agents)?.disabled === true);
|
|
321
|
+
}
|
|
322
|
+
function cloneAgentMap(agentMap) {
|
|
323
|
+
return structuredClone(agentMap);
|
|
324
|
+
}
|
|
325
|
+
function allowCatalogUpgrade(args) {
|
|
326
|
+
if (!args.requested)
|
|
327
|
+
return false;
|
|
328
|
+
return args.registryManaged || !hasExplicitRouteSelection(args.cfg, args.agentName);
|
|
329
|
+
}
|
|
330
|
+
function selectedProviderCatalogModels(target, selectedModel) {
|
|
331
|
+
const separator = selectedModel.indexOf("/");
|
|
332
|
+
if (separator <= 0 || separator === selectedModel.length - 1)
|
|
333
|
+
return undefined;
|
|
334
|
+
const providers = isRecord(target.provider) ? target.provider : undefined;
|
|
335
|
+
const provider = providers?.[selectedModel.slice(0, separator)];
|
|
336
|
+
if (!isRecord(provider) || !isRecord(provider.models))
|
|
337
|
+
return undefined;
|
|
338
|
+
return new Set(Object.keys(provider.models));
|
|
339
|
+
}
|
|
340
|
+
function registerEffectiveRoute(args) {
|
|
341
|
+
if (!args.build)
|
|
342
|
+
return;
|
|
343
|
+
const route = buildEffectiveModelRoute({
|
|
344
|
+
selectedModel: args.primary.model,
|
|
345
|
+
requirement: args.requirement,
|
|
346
|
+
requirementSource: args.requirementSource,
|
|
347
|
+
primarySource: args.primary.source,
|
|
348
|
+
fastMode: args.build.fastMode,
|
|
349
|
+
fastModels: args.cfg.fastModels,
|
|
350
|
+
catalogModels: selectedProviderCatalogModels(args.target, args.primary.model),
|
|
351
|
+
});
|
|
352
|
+
args.build.nextRoutes.set(args.name, route);
|
|
353
|
+
const entry = args.agentMap[args.name];
|
|
354
|
+
if (isRecord(entry))
|
|
355
|
+
entry.model = route.model;
|
|
356
|
+
}
|
|
149
357
|
export function createConfigHandler(args) {
|
|
150
358
|
return async (rawInput, _output) => {
|
|
151
|
-
args
|
|
152
|
-
|
|
359
|
+
const registryManaged = hasRouteRegistry(args);
|
|
360
|
+
if (!registryManaged)
|
|
361
|
+
args.registeredAgentModels?.clear();
|
|
362
|
+
const compatibilityConfig = registryManaged ? undefined : args.getConfig();
|
|
363
|
+
const generation = registryManaged ? args.routeRegistry.beginBuild() : undefined;
|
|
153
364
|
if (!isRecord(rawInput))
|
|
154
365
|
return;
|
|
155
366
|
const target = isRecord(rawInput.config) ? rawInput.config : rawInput;
|
|
367
|
+
const routeBuild = registryManaged
|
|
368
|
+
? { fastMode: args.getFastMode(), nextRoutes: new Map() }
|
|
369
|
+
: undefined;
|
|
370
|
+
const cfg = compatibilityConfig ?? args.getConfig();
|
|
156
371
|
const registered = registerSkillsAndCommands(target, cfg, args.skillsRoot);
|
|
157
372
|
const registeredMcps = registerMcps(target, cfg, args.cwd);
|
|
158
373
|
if (!cfg.registerBuiltinAgents) {
|
|
159
374
|
log.info(`config: registered ${registered.skills} skills, ${registered.commands} commands, ${registeredMcps} MCPs`);
|
|
375
|
+
if (registryManaged && routeBuild && generation !== undefined) {
|
|
376
|
+
args.routeRegistry.publish(generation, routeBuild.nextRoutes);
|
|
377
|
+
}
|
|
160
378
|
return;
|
|
161
379
|
}
|
|
162
|
-
if (!isRecord(target.agent))
|
|
380
|
+
if (!registryManaged && !isRecord(target.agent))
|
|
163
381
|
target.agent = {};
|
|
164
|
-
const agentMap =
|
|
382
|
+
const agentMap = registryManaged
|
|
383
|
+
? cloneAgentMap(isRecord(target.agent) ? target.agent : {})
|
|
384
|
+
: target.agent;
|
|
385
|
+
const existingModels = new Map();
|
|
386
|
+
for (const [name, raw] of Object.entries(agentMap)) {
|
|
387
|
+
if (isRecord(raw) && typeof raw.model === "string")
|
|
388
|
+
existingModels.set(name, raw.model);
|
|
389
|
+
}
|
|
165
390
|
const disabled = new Set(cfg.disabledAgents ?? []);
|
|
391
|
+
for (const [name, raw] of Object.entries(agentMap)) {
|
|
392
|
+
const disabledReview = parseReviewAgentName(name) !== null && isExpandedReviewAgentDisabled(name, {
|
|
393
|
+
agents: cfg.agents,
|
|
394
|
+
disabledAgents: cfg.disabledAgents,
|
|
395
|
+
});
|
|
396
|
+
if ((disabled.has(name) || disabledReview) && isRecord(raw))
|
|
397
|
+
raw.disable = true;
|
|
398
|
+
}
|
|
166
399
|
if (cfg.disableOpenCodeBuiltinAgents) {
|
|
167
400
|
for (const name of ["build", "plan"]) {
|
|
168
401
|
if (!isRecord(agentMap[name]))
|
|
@@ -179,6 +412,8 @@ export function createConfigHandler(args) {
|
|
|
179
412
|
}
|
|
180
413
|
}
|
|
181
414
|
for (const a of BUILTIN_AGENTS) {
|
|
415
|
+
if (parseReviewAgentName(a.name))
|
|
416
|
+
continue;
|
|
182
417
|
if (disabled.has(a.name))
|
|
183
418
|
continue;
|
|
184
419
|
let norm = normalizeAgentShorthand(a.name, cfg.agents);
|
|
@@ -186,27 +421,29 @@ export function createConfigHandler(args) {
|
|
|
186
421
|
// but didn't specify a model (no requirement) and didn't set an explicit
|
|
187
422
|
// alias. If there is no user entry at all, the builtin requirement stands.
|
|
188
423
|
const userEntryForAgent = cfg.agents?.[a.name];
|
|
189
|
-
let inheritedDefaultAlias = false;
|
|
190
424
|
if (userEntryForAgent !== undefined && !norm?.requirement?.fallbackChain?.length && a.defaultAlias && !userEntryForAgent.alias) {
|
|
191
425
|
const aliasNorm = normalizeAgentShorthand(a.defaultAlias, cfg.agents);
|
|
192
426
|
if (aliasNorm?.requirement?.fallbackChain?.length) {
|
|
193
427
|
norm = { ...norm, requirement: aliasNorm.requirement };
|
|
194
|
-
inheritedDefaultAlias = true;
|
|
195
428
|
}
|
|
196
429
|
}
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
430
|
+
const effective = resolveRouteRequirement(cfg, a.name);
|
|
431
|
+
if (!effective)
|
|
432
|
+
continue;
|
|
433
|
+
const primary = selectRoutePrimary({
|
|
434
|
+
target,
|
|
435
|
+
agentName: a.name,
|
|
436
|
+
requirement: effective.requirement,
|
|
437
|
+
requirementSource: effective.source,
|
|
438
|
+
existingModel: existingModels.get(a.name),
|
|
439
|
+
allowCatalogUpgrade: allowCatalogUpgrade({
|
|
440
|
+
registryManaged,
|
|
441
|
+
cfg,
|
|
442
|
+
agentName: a.name,
|
|
443
|
+
requested: true,
|
|
444
|
+
}),
|
|
445
|
+
});
|
|
446
|
+
const prompt = promptForBuiltinAgent(a, norm, cfg.workflow, primary.model);
|
|
210
447
|
const mode = a.name === "orchestrator" || a.name === "builder"
|
|
211
448
|
? "primary"
|
|
212
449
|
: a.name === "planner"
|
|
@@ -216,35 +453,122 @@ export function createConfigHandler(args) {
|
|
|
216
453
|
if (prompt)
|
|
217
454
|
extras.prompt = prompt;
|
|
218
455
|
extras.mode = mode;
|
|
219
|
-
extras.model =
|
|
456
|
+
extras.model = primary.model;
|
|
220
457
|
if (mode === "primary" || mode === "all") {
|
|
221
458
|
extras.promptPrefix = buildLocaleGuidance(cfg.locale);
|
|
222
459
|
}
|
|
223
|
-
|
|
460
|
+
const contract = delegationContractFor(a.name);
|
|
461
|
+
if (contract)
|
|
462
|
+
extras.promptSuffix = contract;
|
|
463
|
+
if (applyAgentEntry(agentMap, a, norm, extras)) {
|
|
464
|
+
registerEffectiveRoute({
|
|
465
|
+
build: routeBuild,
|
|
466
|
+
agentMap,
|
|
467
|
+
target,
|
|
468
|
+
cfg,
|
|
469
|
+
name: a.name,
|
|
470
|
+
requirement: effective.requirement,
|
|
471
|
+
requirementSource: effective.source,
|
|
472
|
+
primary,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (const profile of expandReviewAgents({ agents: cfg.agents, disabledAgents: cfg.disabledAgents })) {
|
|
477
|
+
const synthetic = {
|
|
478
|
+
name: profile.name,
|
|
479
|
+
...(profile.registration.description ? { description: profile.registration.description } : {}),
|
|
480
|
+
requirement: profile.requirement,
|
|
481
|
+
promptSource: profile.promptSource,
|
|
482
|
+
};
|
|
483
|
+
const effective = resolveRouteRequirement(cfg, profile.name) ?? {
|
|
484
|
+
requirement: profile.requirement,
|
|
485
|
+
source: "agent-default",
|
|
486
|
+
};
|
|
487
|
+
const primary = selectRoutePrimary({
|
|
488
|
+
target,
|
|
489
|
+
agentName: profile.name,
|
|
490
|
+
requirement: effective.requirement,
|
|
491
|
+
requirementSource: effective.source,
|
|
492
|
+
existingModel: existingModels.get(profile.name),
|
|
493
|
+
allowCatalogUpgrade: allowCatalogUpgrade({
|
|
494
|
+
registryManaged,
|
|
495
|
+
cfg,
|
|
496
|
+
agentName: profile.name,
|
|
497
|
+
requested: !profile.suppressCatalogUpgrade,
|
|
498
|
+
}),
|
|
499
|
+
});
|
|
500
|
+
let prompt = promptForBuiltinAgent(synthetic, { requirement: profile.requirement }, cfg.workflow, primary.model);
|
|
501
|
+
if (profile.registration.promptAppend)
|
|
502
|
+
prompt = `${prompt}\n\n${profile.registration.promptAppend.trim()}`;
|
|
503
|
+
const extras = { mode: "subagent", model: primary.model };
|
|
504
|
+
if (prompt)
|
|
505
|
+
extras.prompt = prompt;
|
|
506
|
+
const contract = delegationContractFor(profile.name);
|
|
507
|
+
if (contract)
|
|
508
|
+
extras.promptSuffix = contract;
|
|
509
|
+
if (applyAgentEntry(agentMap, synthetic, {
|
|
510
|
+
...(profile.registration.description ? { description: profile.registration.description } : {}),
|
|
511
|
+
requirement: profile.requirement,
|
|
512
|
+
...(profile.registration.permission ? { permission: profile.registration.permission } : {}),
|
|
513
|
+
}, extras, profile.registration)) {
|
|
514
|
+
registerEffectiveRoute({
|
|
515
|
+
build: routeBuild,
|
|
516
|
+
agentMap,
|
|
517
|
+
target,
|
|
518
|
+
cfg,
|
|
519
|
+
name: profile.name,
|
|
520
|
+
requirement: effective.requirement,
|
|
521
|
+
requirementSource: effective.source,
|
|
522
|
+
primary,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
224
525
|
}
|
|
225
526
|
for (const c of BUILTIN_CATEGORIES) {
|
|
226
527
|
if (disabled.has(c.name))
|
|
227
528
|
continue;
|
|
228
529
|
const agentOverride = normalizeAgentShorthand(c.name, cfg.agents);
|
|
530
|
+
if (registryManaged && hasOwnAgentEntry(cfg, c.name) && !agentOverride?.requirement?.fallbackChain?.length) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
229
533
|
const categoryOverride = normalizeShorthand(cfg.categories?.[c.name]);
|
|
230
|
-
const
|
|
534
|
+
const effective = resolveRouteRequirement(cfg, c.name);
|
|
535
|
+
if (!effective)
|
|
536
|
+
continue;
|
|
537
|
+
const baseAgent = categoryAsAgent(c, effective.requirement);
|
|
231
538
|
const merged = agentOverride ?? categoryOverride;
|
|
539
|
+
const primary = selectRoutePrimary({
|
|
540
|
+
target,
|
|
541
|
+
agentName: c.name,
|
|
542
|
+
requirement: effective.requirement,
|
|
543
|
+
requirementSource: effective.source,
|
|
544
|
+
existingModel: existingModels.get(c.name),
|
|
545
|
+
allowCatalogUpgrade: allowCatalogUpgrade({
|
|
546
|
+
registryManaged,
|
|
547
|
+
cfg,
|
|
548
|
+
agentName: c.name,
|
|
549
|
+
requested: true,
|
|
550
|
+
}),
|
|
551
|
+
});
|
|
232
552
|
const extras = { mode: "subagent" };
|
|
233
|
-
const
|
|
234
|
-
const existingModel = rawAgentModel(agentMap, c.name);
|
|
235
|
-
const configuredRequirement = agentOverride?.requirement ?? categoryOverride?.requirement;
|
|
236
|
-
const catalogModel = !hasUserSelection && !existingModel
|
|
237
|
-
? selectCatalogModel(target, c.name, baseAgent.requirement)
|
|
238
|
-
: undefined;
|
|
239
|
-
const finalModel = existingModel
|
|
240
|
-
?? (configuredRequirement ? fmtModel(configuredRequirement.fallbackChain[0]) : undefined)
|
|
241
|
-
?? catalogModel
|
|
242
|
-
?? fmtModel(baseAgent.requirement.fallbackChain[0]);
|
|
243
|
-
const prompt = promptForBuiltinCategory(c.name, cfg.workflow, finalModel);
|
|
553
|
+
const prompt = promptForBuiltinCategory(c.name, cfg.workflow, primary.model);
|
|
244
554
|
if (prompt)
|
|
245
555
|
extras.prompt = prompt;
|
|
246
|
-
extras.model =
|
|
247
|
-
|
|
556
|
+
extras.model = primary.model;
|
|
557
|
+
const contract = delegationContractFor(c.name);
|
|
558
|
+
if (contract)
|
|
559
|
+
extras.promptSuffix = contract;
|
|
560
|
+
if (applyAgentEntry(agentMap, baseAgent, merged, extras)) {
|
|
561
|
+
registerEffectiveRoute({
|
|
562
|
+
build: routeBuild,
|
|
563
|
+
agentMap,
|
|
564
|
+
target,
|
|
565
|
+
cfg,
|
|
566
|
+
name: c.name,
|
|
567
|
+
requirement: effective.requirement,
|
|
568
|
+
requirementSource: effective.source,
|
|
569
|
+
primary,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
248
572
|
}
|
|
249
573
|
if (cfg.agents) {
|
|
250
574
|
for (const name of Object.keys(cfg.agents)) {
|
|
@@ -254,57 +578,211 @@ export function createConfigHandler(args) {
|
|
|
254
578
|
continue;
|
|
255
579
|
if (BUILTIN_CATEGORIES.some((c) => c.name === name))
|
|
256
580
|
continue;
|
|
581
|
+
if (parseReviewAgentName(name))
|
|
582
|
+
continue;
|
|
257
583
|
const norm = normalizeAgentShorthand(name, cfg.agents);
|
|
258
584
|
if (!norm?.requirement?.fallbackChain?.length)
|
|
259
585
|
continue;
|
|
586
|
+
const effective = resolveRouteRequirement(cfg, name);
|
|
587
|
+
if (!effective)
|
|
588
|
+
continue;
|
|
589
|
+
const primary = selectRoutePrimary({
|
|
590
|
+
target,
|
|
591
|
+
agentName: name,
|
|
592
|
+
requirement: effective.requirement,
|
|
593
|
+
requirementSource: effective.source,
|
|
594
|
+
existingModel: existingModels.get(name),
|
|
595
|
+
allowCatalogUpgrade: false,
|
|
596
|
+
});
|
|
260
597
|
const synthetic = {
|
|
261
598
|
name,
|
|
262
599
|
...(norm.description ? { description: norm.description } : {}),
|
|
263
600
|
requirement: norm.requirement,
|
|
264
601
|
};
|
|
265
|
-
applyAgentEntry(agentMap, synthetic, norm);
|
|
602
|
+
const registeredAgent = applyAgentEntry(agentMap, synthetic, norm, { model: primary.model });
|
|
603
|
+
if (registeredAgent && !COMPAT_AGENT_ALIASES.some((entry) => entry.alias === name)) {
|
|
604
|
+
registerEffectiveRoute({
|
|
605
|
+
build: routeBuild,
|
|
606
|
+
agentMap,
|
|
607
|
+
target,
|
|
608
|
+
cfg,
|
|
609
|
+
name,
|
|
610
|
+
requirement: effective.requirement,
|
|
611
|
+
requirementSource: effective.source,
|
|
612
|
+
primary,
|
|
613
|
+
});
|
|
614
|
+
}
|
|
266
615
|
}
|
|
267
616
|
}
|
|
268
|
-
|
|
617
|
+
if (registryManaged && cfg.categories) {
|
|
618
|
+
for (const name of Object.keys(cfg.categories)) {
|
|
619
|
+
if (disabled.has(name))
|
|
620
|
+
continue;
|
|
621
|
+
if (BUILTIN_AGENTS.some((a) => a.name === name))
|
|
622
|
+
continue;
|
|
623
|
+
if (BUILTIN_CATEGORIES.some((c) => c.name === name))
|
|
624
|
+
continue;
|
|
625
|
+
if (hasOwnAgentEntry(cfg, name))
|
|
626
|
+
continue;
|
|
627
|
+
const agentOverride = normalizeAgentShorthand(name, cfg.agents);
|
|
628
|
+
const categoryOverride = normalizeShorthand(cfg.categories[name]);
|
|
629
|
+
if (!agentOverride?.requirement?.fallbackChain?.length && !categoryOverride?.requirement?.fallbackChain?.length) {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
const effective = resolveRouteRequirement(cfg, name);
|
|
633
|
+
if (!effective)
|
|
634
|
+
continue;
|
|
635
|
+
const primary = selectRoutePrimary({
|
|
636
|
+
target,
|
|
637
|
+
agentName: name,
|
|
638
|
+
requirement: effective.requirement,
|
|
639
|
+
requirementSource: effective.source,
|
|
640
|
+
existingModel: existingModels.get(name),
|
|
641
|
+
allowCatalogUpgrade: false,
|
|
642
|
+
});
|
|
643
|
+
const synthetic = {
|
|
644
|
+
name,
|
|
645
|
+
...(agentOverride?.description ?? categoryOverride?.description
|
|
646
|
+
? { description: agentOverride?.description ?? categoryOverride?.description }
|
|
647
|
+
: {}),
|
|
648
|
+
requirement: effective.requirement,
|
|
649
|
+
};
|
|
650
|
+
const merged = agentOverride?.requirement ? agentOverride : categoryOverride;
|
|
651
|
+
if (applyAgentEntry(agentMap, synthetic, merged, { mode: "subagent", model: primary.model })) {
|
|
652
|
+
registerEffectiveRoute({
|
|
653
|
+
build: routeBuild,
|
|
654
|
+
agentMap,
|
|
655
|
+
target,
|
|
656
|
+
cfg,
|
|
657
|
+
name,
|
|
658
|
+
requirement: effective.requirement,
|
|
659
|
+
requirementSource: effective.source,
|
|
660
|
+
primary,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
registerCompatAgentAliases(agentMap, disabled, cfg.agents, registryManaged);
|
|
269
666
|
registerDefaultPermissions(target, agentMap);
|
|
270
|
-
for (const
|
|
271
|
-
if (
|
|
272
|
-
|
|
667
|
+
for (const { alias, target: compatTarget } of COMPAT_AGENT_ALIASES) {
|
|
668
|
+
if (!routeBuild || disabled.has(alias) || disabled.has(compatTarget) || isCompatAliasDisabled(alias, compatTarget, cfg.agents))
|
|
669
|
+
continue;
|
|
670
|
+
const currentTarget = agentMap[compatTarget];
|
|
671
|
+
const aliasEntry = agentMap[alias];
|
|
672
|
+
const targetRoute = routeBuild.nextRoutes.get(compatTarget);
|
|
673
|
+
const effective = resolveRouteRequirement(cfg, alias);
|
|
674
|
+
if (!isRecord(currentTarget)
|
|
675
|
+
|| currentTarget.disable === true
|
|
676
|
+
|| !isRecord(aliasEntry)
|
|
677
|
+
|| aliasEntry.disable === true
|
|
678
|
+
|| typeof aliasEntry.model !== "string"
|
|
679
|
+
|| !effective)
|
|
680
|
+
continue;
|
|
681
|
+
const explicitAliasRequirement = normalizeAgentShorthand(alias, cfg.agents)?.requirement;
|
|
682
|
+
const primary = existingModels.has(alias)
|
|
683
|
+
? { model: aliasEntry.model, source: "existing-model" }
|
|
684
|
+
: explicitAliasRequirement
|
|
685
|
+
? { model: aliasEntry.model, source: "user-requirement" }
|
|
686
|
+
: { model: aliasEntry.model, source: targetRoute?.primarySource ?? "builtin-requirement" };
|
|
687
|
+
registerEffectiveRoute({
|
|
688
|
+
build: routeBuild,
|
|
689
|
+
agentMap,
|
|
690
|
+
target,
|
|
691
|
+
cfg,
|
|
692
|
+
name: alias,
|
|
693
|
+
requirement: effective.requirement,
|
|
694
|
+
requirementSource: effective.source,
|
|
695
|
+
primary,
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
if (!registryManaged) {
|
|
699
|
+
for (const [name, raw] of Object.entries(agentMap)) {
|
|
700
|
+
if (isRecord(raw) && typeof raw.model === "string") {
|
|
701
|
+
args.registeredAgentModels?.set(name, raw.model);
|
|
702
|
+
}
|
|
273
703
|
}
|
|
274
704
|
}
|
|
275
705
|
log.info(`config: registered ${Object.keys(agentMap).length} agents (built-in + categories + user), ${registered.skills} skills, ${registered.commands} commands, ${registeredMcps} MCPs`);
|
|
706
|
+
if (registryManaged && routeBuild && generation !== undefined && args.routeRegistry.publish(generation, routeBuild.nextRoutes)) {
|
|
707
|
+
target.agent = agentMap;
|
|
708
|
+
}
|
|
276
709
|
};
|
|
277
710
|
}
|
|
278
711
|
function registerDefaultPermissions(target, agentMap) {
|
|
279
712
|
const topLevel = isRecord(target.permission) ? target.permission : {};
|
|
280
713
|
target.permission = topLevel;
|
|
281
714
|
mergePermission(target, { webfetch: "allow", external_directory: "allow", task: "deny" }, false);
|
|
282
|
-
for (const name of
|
|
715
|
+
for (const name of PRIMARY_COORDINATORS) {
|
|
283
716
|
const entry = agentMap[name];
|
|
284
717
|
if (isRecord(entry))
|
|
285
718
|
mergePermission(entry, { task: "allow", question: "allow", "task_*": "allow" }, false);
|
|
286
719
|
}
|
|
287
|
-
for (const name of
|
|
720
|
+
for (const name of STANDARD_WORKFLOW_SUBAGENTS) {
|
|
721
|
+
const entry = agentMap[name];
|
|
722
|
+
if (isRecord(entry))
|
|
723
|
+
mergePermission(entry, { task: taskAllowlist(UTILITY_LEAF_AGENTS) }, false);
|
|
724
|
+
}
|
|
725
|
+
const planner = agentMap.planner;
|
|
726
|
+
if (isRecord(planner)) {
|
|
727
|
+
mergePermission(planner, { task: taskAllowlist([...READ_ONLY_UTILITY_AGENTS, "reviewer"]) }, false);
|
|
728
|
+
}
|
|
729
|
+
for (const name of READ_ONLY_WORKFLOW_AGENTS) {
|
|
730
|
+
const entry = agentMap[name];
|
|
731
|
+
if (isRecord(entry))
|
|
732
|
+
mergePermission(entry, { task: taskAllowlist(READ_ONLY_UTILITY_AGENTS) }, false);
|
|
733
|
+
}
|
|
734
|
+
for (const [name, entry] of Object.entries(agentMap)) {
|
|
735
|
+
if (isRecord(entry) && (isReviewAgentName(name) || name === "plan-critic")) {
|
|
736
|
+
mergePermission(entry, { task: taskAllowlist(READ_ONLY_UTILITY_AGENTS) }, false);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
for (const name of LOCAL_COORDINATORS) {
|
|
740
|
+
const entry = agentMap[name];
|
|
741
|
+
if (isRecord(entry)) {
|
|
742
|
+
mergePermission(entry, {
|
|
743
|
+
task: taskAllowlist([...UTILITY_LEAF_AGENTS, ...SPECIALIST_EXECUTION_AGENTS]),
|
|
744
|
+
}, false);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
for (const name of UTILITY_LEAF_AGENTS) {
|
|
288
748
|
const entry = agentMap[name];
|
|
289
749
|
if (isRecord(entry))
|
|
290
750
|
mergePermission(entry, { task: "deny" }, false);
|
|
291
751
|
}
|
|
752
|
+
for (const name of QUESTION_ENABLED_WORKFLOW_AGENTS) {
|
|
753
|
+
const entry = agentMap[name];
|
|
754
|
+
if (isRecord(entry))
|
|
755
|
+
mergePermission(entry, { question: "allow" }, false);
|
|
756
|
+
}
|
|
292
757
|
const docSearch = agentMap["doc-search"];
|
|
293
758
|
if (isRecord(docSearch))
|
|
294
759
|
mergePermission(docSearch, { "grep_app_*": "allow" }, false);
|
|
295
760
|
}
|
|
296
|
-
function registerCompatAgentAliases(agentMap, disabled) {
|
|
761
|
+
function registerCompatAgentAliases(agentMap, disabled, agents, registryManaged) {
|
|
297
762
|
for (const { alias, target } of COMPAT_AGENT_ALIASES) {
|
|
298
|
-
if (disabled.has(alias) || disabled.has(target))
|
|
763
|
+
if (disabled.has(alias) || disabled.has(target) || isCompatAliasDisabled(alias, target, agents)) {
|
|
764
|
+
if (registryManaged)
|
|
765
|
+
delete agentMap[alias];
|
|
299
766
|
continue;
|
|
767
|
+
}
|
|
300
768
|
const source = agentMap[target];
|
|
301
|
-
if (!isRecord(source))
|
|
769
|
+
if (!isRecord(source) || source.disable === true) {
|
|
770
|
+
if (registryManaged)
|
|
771
|
+
delete agentMap[alias];
|
|
302
772
|
continue;
|
|
773
|
+
}
|
|
303
774
|
const existing = isRecord(agentMap[alias]) ? agentMap[alias] : {};
|
|
775
|
+
if (existing.disable === true)
|
|
776
|
+
continue;
|
|
304
777
|
const aliasEntry = { ...source, ...existing };
|
|
305
778
|
if (typeof aliasEntry.description !== "string") {
|
|
306
779
|
aliasEntry.description = `Compatibility alias for @${target}.`;
|
|
307
780
|
}
|
|
781
|
+
const contract = delegationContractFor(alias);
|
|
782
|
+
if (contract) {
|
|
783
|
+
const basePrompt = typeof aliasEntry.prompt === "string" ? aliasEntry.prompt : "";
|
|
784
|
+
aliasEntry.prompt = appendPromptSuffix(basePrompt, contract);
|
|
785
|
+
}
|
|
308
786
|
agentMap[alias] = aliasEntry;
|
|
309
787
|
}
|
|
310
788
|
}
|