@katyella/legio 0.1.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/CHANGELOG.md +422 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/agents/builder.md +141 -0
- package/agents/coordinator.md +351 -0
- package/agents/cto.md +196 -0
- package/agents/gateway.md +276 -0
- package/agents/lead.md +281 -0
- package/agents/merger.md +156 -0
- package/agents/monitor.md +212 -0
- package/agents/reviewer.md +142 -0
- package/agents/scout.md +131 -0
- package/agents/supervisor.md +416 -0
- package/bin/legio.mjs +38 -0
- package/package.json +77 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +102 -0
- package/src/agents/hooks-deployer.test.ts +1820 -0
- package/src/agents/hooks-deployer.ts +574 -0
- package/src/agents/identity.test.ts +614 -0
- package/src/agents/identity.ts +385 -0
- package/src/agents/lifecycle.test.ts +202 -0
- package/src/agents/lifecycle.ts +184 -0
- package/src/agents/manifest.test.ts +558 -0
- package/src/agents/manifest.ts +297 -0
- package/src/agents/overlay.test.ts +592 -0
- package/src/agents/overlay.ts +316 -0
- package/src/beads/client.test.ts +210 -0
- package/src/beads/client.ts +227 -0
- package/src/beads/molecules.test.ts +320 -0
- package/src/beads/molecules.ts +209 -0
- package/src/commands/agents.test.ts +325 -0
- package/src/commands/agents.ts +286 -0
- package/src/commands/clean.test.ts +730 -0
- package/src/commands/clean.ts +653 -0
- package/src/commands/completions.test.ts +346 -0
- package/src/commands/completions.ts +950 -0
- package/src/commands/coordinator.test.ts +1524 -0
- package/src/commands/coordinator.ts +880 -0
- package/src/commands/costs.test.ts +1015 -0
- package/src/commands/costs.ts +473 -0
- package/src/commands/dashboard.test.ts +94 -0
- package/src/commands/dashboard.ts +607 -0
- package/src/commands/doctor.test.ts +295 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/down.test.ts +308 -0
- package/src/commands/down.ts +124 -0
- package/src/commands/errors.test.ts +648 -0
- package/src/commands/errors.ts +255 -0
- package/src/commands/feed.test.ts +579 -0
- package/src/commands/feed.ts +368 -0
- package/src/commands/gateway.test.ts +698 -0
- package/src/commands/gateway.ts +419 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +539 -0
- package/src/commands/hooks.test.ts +292 -0
- package/src/commands/hooks.ts +210 -0
- package/src/commands/init.test.ts +211 -0
- package/src/commands/init.ts +622 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +455 -0
- package/src/commands/log.test.ts +1556 -0
- package/src/commands/log.ts +752 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +544 -0
- package/src/commands/mail.test.ts +1726 -0
- package/src/commands/mail.ts +926 -0
- package/src/commands/merge.test.ts +676 -0
- package/src/commands/merge.ts +374 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +150 -0
- package/src/commands/monitor.test.ts +151 -0
- package/src/commands/monitor.ts +394 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +373 -0
- package/src/commands/prime.test.ts +467 -0
- package/src/commands/prime.ts +386 -0
- package/src/commands/replay.test.ts +742 -0
- package/src/commands/replay.ts +367 -0
- package/src/commands/run.test.ts +443 -0
- package/src/commands/run.ts +365 -0
- package/src/commands/server.test.ts +626 -0
- package/src/commands/server.ts +298 -0
- package/src/commands/sling.test.ts +810 -0
- package/src/commands/sling.ts +700 -0
- package/src/commands/spec.test.ts +206 -0
- package/src/commands/spec.ts +171 -0
- package/src/commands/status.test.ts +276 -0
- package/src/commands/status.ts +339 -0
- package/src/commands/stop.test.ts +357 -0
- package/src/commands/stop.ts +119 -0
- package/src/commands/supervisor.test.ts +186 -0
- package/src/commands/supervisor.ts +544 -0
- package/src/commands/trace.test.ts +746 -0
- package/src/commands/trace.ts +332 -0
- package/src/commands/up.test.ts +597 -0
- package/src/commands/up.ts +275 -0
- package/src/commands/watch.test.ts +152 -0
- package/src/commands/watch.ts +238 -0
- package/src/commands/worktree.test.ts +648 -0
- package/src/commands/worktree.ts +266 -0
- package/src/config.test.ts +496 -0
- package/src/config.ts +616 -0
- package/src/doctor/agents.test.ts +448 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +184 -0
- package/src/doctor/config-check.ts +185 -0
- package/src/doctor/consistency.test.ts +645 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +284 -0
- package/src/doctor/databases.ts +211 -0
- package/src/doctor/dependencies.test.ts +150 -0
- package/src/doctor/dependencies.ts +179 -0
- package/src/doctor/logs.test.ts +244 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +210 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +285 -0
- package/src/doctor/structure.ts +195 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +130 -0
- package/src/doctor/version.ts +131 -0
- package/src/e2e/chat-flow.test.ts +346 -0
- package/src/e2e/init-sling-lifecycle.test.ts +288 -0
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +246 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +344 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/global-setup.ts +14 -0
- package/src/index.ts +339 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +118 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +812 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +258 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +873 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +815 -0
- package/src/mail/store.ts +402 -0
- package/src/merge/queue.test.ts +449 -0
- package/src/merge/queue.ts +262 -0
- package/src/merge/resolver.test.ts +1453 -0
- package/src/merge/resolver.ts +759 -0
- package/src/metrics/store.test.ts +1167 -0
- package/src/metrics/store.ts +511 -0
- package/src/metrics/summary.test.ts +397 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +643 -0
- package/src/metrics/transcript.ts +351 -0
- package/src/mulch/client.test.ts +547 -0
- package/src/mulch/client.ts +416 -0
- package/src/server/audit-store.test.ts +384 -0
- package/src/server/audit-store.ts +257 -0
- package/src/server/headless.test.ts +180 -0
- package/src/server/headless.ts +151 -0
- package/src/server/index.test.ts +241 -0
- package/src/server/index.ts +317 -0
- package/src/server/public/app.js +187 -0
- package/src/server/public/apple-touch-icon.png +0 -0
- package/src/server/public/components/agent-badge.js +37 -0
- package/src/server/public/components/data-table.js +114 -0
- package/src/server/public/components/gateway-chat.js +256 -0
- package/src/server/public/components/issue-card.js +96 -0
- package/src/server/public/components/layout.js +88 -0
- package/src/server/public/components/message-bubble.js +120 -0
- package/src/server/public/components/stat-card.js +26 -0
- package/src/server/public/components/terminal-panel.js +140 -0
- package/src/server/public/favicon-16.png +0 -0
- package/src/server/public/favicon-32.png +0 -0
- package/src/server/public/favicon.ico +0 -0
- package/src/server/public/favicon.png +0 -0
- package/src/server/public/index.html +64 -0
- package/src/server/public/lib/api.js +35 -0
- package/src/server/public/lib/markdown.js +8 -0
- package/src/server/public/lib/preact-setup.js +8 -0
- package/src/server/public/lib/state.js +99 -0
- package/src/server/public/lib/utils.js +309 -0
- package/src/server/public/lib/ws.js +79 -0
- package/src/server/public/views/chat.js +983 -0
- package/src/server/public/views/costs.js +692 -0
- package/src/server/public/views/dashboard.js +781 -0
- package/src/server/public/views/gateway-chat.js +622 -0
- package/src/server/public/views/inspect.js +399 -0
- package/src/server/public/views/issues.js +470 -0
- package/src/server/public/views/setup.js +94 -0
- package/src/server/public/views/task-detail.js +422 -0
- package/src/server/routes.test.ts +3816 -0
- package/src/server/routes.ts +1964 -0
- package/src/server/websocket.test.ts +288 -0
- package/src/server/websocket.ts +196 -0
- package/src/sessions/compat.test.ts +109 -0
- package/src/sessions/compat.ts +17 -0
- package/src/sessions/store.test.ts +969 -0
- package/src/sessions/store.ts +480 -0
- package/src/test-helpers.test.ts +97 -0
- package/src/test-helpers.ts +143 -0
- package/src/types.ts +708 -0
- package/src/watchdog/daemon.test.ts +1233 -0
- package/src/watchdog/daemon.ts +533 -0
- package/src/watchdog/health.test.ts +371 -0
- package/src/watchdog/health.ts +248 -0
- package/src/watchdog/triage.test.ts +162 -0
- package/src/watchdog/triage.ts +193 -0
- package/src/worktree/manager.test.ts +444 -0
- package/src/worktree/manager.ts +224 -0
- package/src/worktree/tmux.test.ts +1238 -0
- package/src/worktree/tmux.ts +644 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +132 -0
- package/templates/overlay.md.tmpl +79 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { AgentError } from "../errors.ts";
|
|
4
|
+
import type { AgentDefinition, AgentManifest, LegioConfig } from "../types.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Interface for loading, querying, and validating an agent manifest.
|
|
8
|
+
*/
|
|
9
|
+
export interface ManifestLoader {
|
|
10
|
+
/** Load the manifest from disk, parse, validate, and build indexes. */
|
|
11
|
+
load(): Promise<AgentManifest>;
|
|
12
|
+
/** Get an agent definition by name. Returns undefined if not found. */
|
|
13
|
+
getAgent(name: string): AgentDefinition | undefined;
|
|
14
|
+
/** Find all agent names whose capabilities include the given capability. */
|
|
15
|
+
findByCapability(capability: string): AgentDefinition[];
|
|
16
|
+
/** Validate the manifest. Returns a list of errors (empty = valid). */
|
|
17
|
+
validate(): string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Raw manifest shape as read from JSON before validation.
|
|
22
|
+
* Used internally to validate structure before casting to AgentManifest.
|
|
23
|
+
*/
|
|
24
|
+
interface RawManifest {
|
|
25
|
+
version?: unknown;
|
|
26
|
+
agents?: unknown;
|
|
27
|
+
capabilityIndex?: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const VALID_MODELS = new Set(["sonnet", "opus", "haiku"]);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validate that a raw parsed object conforms to the AgentDefinition shape.
|
|
34
|
+
* Returns a list of error messages for any violations.
|
|
35
|
+
*/
|
|
36
|
+
function validateAgentDefinition(name: string, raw: unknown): string[] {
|
|
37
|
+
const errors: string[] = [];
|
|
38
|
+
|
|
39
|
+
if (raw === null || typeof raw !== "object") {
|
|
40
|
+
errors.push(`Agent "${name}": definition must be an object`);
|
|
41
|
+
return errors;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const def = raw as Record<string, unknown>;
|
|
45
|
+
|
|
46
|
+
if (typeof def.file !== "string" || def.file.length === 0) {
|
|
47
|
+
errors.push(`Agent "${name}": "file" must be a non-empty string`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof def.model !== "string" || !VALID_MODELS.has(def.model)) {
|
|
51
|
+
errors.push(`Agent "${name}": "model" must be one of: sonnet, opus, haiku`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!Array.isArray(def.tools)) {
|
|
55
|
+
errors.push(`Agent "${name}": "tools" must be an array`);
|
|
56
|
+
} else {
|
|
57
|
+
for (let i = 0; i < def.tools.length; i++) {
|
|
58
|
+
if (typeof def.tools[i] !== "string") {
|
|
59
|
+
errors.push(`Agent "${name}": "tools[${i}]" must be a string`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!Array.isArray(def.capabilities)) {
|
|
65
|
+
errors.push(`Agent "${name}": "capabilities" must be an array`);
|
|
66
|
+
} else {
|
|
67
|
+
for (let i = 0; i < def.capabilities.length; i++) {
|
|
68
|
+
if (typeof def.capabilities[i] !== "string") {
|
|
69
|
+
errors.push(`Agent "${name}": "capabilities[${i}]" must be a string`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof def.canSpawn !== "boolean") {
|
|
75
|
+
errors.push(`Agent "${name}": "canSpawn" must be a boolean`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!Array.isArray(def.constraints)) {
|
|
79
|
+
errors.push(`Agent "${name}": "constraints" must be an array`);
|
|
80
|
+
} else {
|
|
81
|
+
for (let i = 0; i < def.constraints.length; i++) {
|
|
82
|
+
if (typeof def.constraints[i] !== "string") {
|
|
83
|
+
errors.push(`Agent "${name}": "constraints[${i}]" must be a string`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return errors;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build a capability index: maps each capability string to the list of
|
|
93
|
+
* agent names that declare that capability.
|
|
94
|
+
*/
|
|
95
|
+
function buildCapabilityIndex(agents: Record<string, AgentDefinition>): Record<string, string[]> {
|
|
96
|
+
const index: Record<string, string[]> = {};
|
|
97
|
+
|
|
98
|
+
for (const [name, def] of Object.entries(agents)) {
|
|
99
|
+
for (const cap of def.capabilities) {
|
|
100
|
+
const existing = index[cap];
|
|
101
|
+
if (existing) {
|
|
102
|
+
existing.push(name);
|
|
103
|
+
} else {
|
|
104
|
+
index[cap] = [name];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return index;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Create a ManifestLoader that reads from the given manifest JSON path
|
|
114
|
+
* and resolves agent .md files relative to the given base directory.
|
|
115
|
+
*
|
|
116
|
+
* @param manifestPath - Absolute path to the agent-manifest.json file
|
|
117
|
+
* @param agentBaseDir - Absolute path to the directory containing agent .md files
|
|
118
|
+
*/
|
|
119
|
+
export function createManifestLoader(manifestPath: string, agentBaseDir: string): ManifestLoader {
|
|
120
|
+
let manifest: AgentManifest | null = null;
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
async load(): Promise<AgentManifest> {
|
|
124
|
+
const exists = await access(manifestPath)
|
|
125
|
+
.then(() => true)
|
|
126
|
+
.catch(() => false);
|
|
127
|
+
|
|
128
|
+
if (!exists) {
|
|
129
|
+
throw new AgentError(`Agent manifest not found: ${manifestPath}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let text: string;
|
|
133
|
+
try {
|
|
134
|
+
text = await readFile(manifestPath, "utf-8");
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throw new AgentError(`Failed to read agent manifest: ${manifestPath}`, {
|
|
137
|
+
cause: err instanceof Error ? err : undefined,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let raw: RawManifest;
|
|
142
|
+
try {
|
|
143
|
+
raw = JSON.parse(text) as RawManifest;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
throw new AgentError(`Failed to parse agent manifest as JSON: ${manifestPath}`, {
|
|
146
|
+
cause: err instanceof Error ? err : undefined,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Validate top-level structure
|
|
151
|
+
if (typeof raw.version !== "string" || raw.version.length === 0) {
|
|
152
|
+
throw new AgentError(
|
|
153
|
+
'Agent manifest missing or invalid "version" field (must be a non-empty string)',
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (raw.agents === null || typeof raw.agents !== "object" || Array.isArray(raw.agents)) {
|
|
158
|
+
throw new AgentError(
|
|
159
|
+
'Agent manifest missing or invalid "agents" field (must be an object)',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const rawAgents = raw.agents as Record<string, unknown>;
|
|
164
|
+
|
|
165
|
+
// Validate each agent definition
|
|
166
|
+
const allErrors: string[] = [];
|
|
167
|
+
for (const [name, def] of Object.entries(rawAgents)) {
|
|
168
|
+
const defErrors = validateAgentDefinition(name, def);
|
|
169
|
+
allErrors.push(...defErrors);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (allErrors.length > 0) {
|
|
173
|
+
throw new AgentError(`Agent manifest validation failed:\n${allErrors.join("\n")}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// At this point, all agent definitions have been validated
|
|
177
|
+
const agents = rawAgents as Record<string, AgentDefinition>;
|
|
178
|
+
|
|
179
|
+
// Verify that all referenced .md files exist
|
|
180
|
+
for (const [name, def] of Object.entries(agents)) {
|
|
181
|
+
const filePath = join(agentBaseDir, def.file);
|
|
182
|
+
const mdExists = await access(filePath)
|
|
183
|
+
.then(() => true)
|
|
184
|
+
.catch(() => false);
|
|
185
|
+
|
|
186
|
+
if (!mdExists) {
|
|
187
|
+
throw new AgentError(
|
|
188
|
+
`Agent "${name}" references file "${def.file}" which does not exist at: ${filePath}`,
|
|
189
|
+
{ agentName: name },
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Build the capability index
|
|
195
|
+
const capabilityIndex = buildCapabilityIndex(agents);
|
|
196
|
+
|
|
197
|
+
manifest = {
|
|
198
|
+
version: raw.version,
|
|
199
|
+
agents,
|
|
200
|
+
capabilityIndex,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return manifest;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
getAgent(name: string): AgentDefinition | undefined {
|
|
207
|
+
if (!manifest) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return manifest.agents[name];
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
findByCapability(capability: string): AgentDefinition[] {
|
|
214
|
+
if (!manifest) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const agentNames = manifest.capabilityIndex[capability];
|
|
219
|
+
if (!agentNames) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const results: AgentDefinition[] = [];
|
|
224
|
+
for (const name of agentNames) {
|
|
225
|
+
const def = manifest.agents[name];
|
|
226
|
+
if (def) {
|
|
227
|
+
results.push(def);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return results;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
validate(): string[] {
|
|
234
|
+
if (!manifest) {
|
|
235
|
+
return ["Manifest not loaded. Call load() first."];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const errors: string[] = [];
|
|
239
|
+
|
|
240
|
+
// Re-validate each agent definition structurally
|
|
241
|
+
for (const [name, def] of Object.entries(manifest.agents)) {
|
|
242
|
+
const defErrors = validateAgentDefinition(name, def);
|
|
243
|
+
errors.push(...defErrors);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Verify capability index consistency
|
|
247
|
+
for (const [cap, names] of Object.entries(manifest.capabilityIndex)) {
|
|
248
|
+
for (const name of names) {
|
|
249
|
+
const def = manifest.agents[name];
|
|
250
|
+
if (!def) {
|
|
251
|
+
errors.push(
|
|
252
|
+
`Capability index references agent "${name}" for capability "${cap}", but agent does not exist`,
|
|
253
|
+
);
|
|
254
|
+
} else if (!def.capabilities.includes(cap)) {
|
|
255
|
+
errors.push(
|
|
256
|
+
`Capability index lists agent "${name}" under "${cap}", but agent does not declare that capability`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check that every agent capability is present in the index
|
|
263
|
+
for (const [name, def] of Object.entries(manifest.agents)) {
|
|
264
|
+
for (const cap of def.capabilities) {
|
|
265
|
+
const indexed = manifest.capabilityIndex[cap];
|
|
266
|
+
if (!indexed || !indexed.includes(name)) {
|
|
267
|
+
errors.push(
|
|
268
|
+
`Agent "${name}" declares capability "${cap}" but is not listed in the capability index`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return errors;
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
type ModelName = "sonnet" | "opus" | "haiku";
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Resolve the model for an agent role.
|
|
283
|
+
*
|
|
284
|
+
* Resolution order: config.models override > manifest default > fallback.
|
|
285
|
+
*/
|
|
286
|
+
export function resolveModel(
|
|
287
|
+
config: LegioConfig,
|
|
288
|
+
manifest: AgentManifest,
|
|
289
|
+
role: string,
|
|
290
|
+
fallback: ModelName,
|
|
291
|
+
): ModelName {
|
|
292
|
+
const configModel = config.models[role];
|
|
293
|
+
if (configModel) return configModel;
|
|
294
|
+
const manifestModel = manifest.agents[role]?.model;
|
|
295
|
+
if (manifestModel) return manifestModel;
|
|
296
|
+
return fallback;
|
|
297
|
+
}
|