@melihmucuk/pi-crew 1.0.17 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/extension/catalog.ts +543 -0
  2. package/extension/crew.ts +383 -0
  3. package/extension/index.ts +5 -6
  4. package/extension/subagent-session.ts +270 -0
  5. package/extension/tools.ts +323 -0
  6. package/extension/ui.ts +309 -0
  7. package/package.json +1 -1
  8. package/extension/agent-catalog.ts +0 -369
  9. package/extension/agent-config-fields.ts +0 -359
  10. package/extension/agent-discovery.ts +0 -123
  11. package/extension/bootstrap-session.ts +0 -131
  12. package/extension/integration/crew-tool-actions.ts +0 -306
  13. package/extension/integration/crew-tool-executor.ts +0 -109
  14. package/extension/integration/register-renderers.ts +0 -77
  15. package/extension/integration/register-tools.ts +0 -47
  16. package/extension/integration/tool-presentation.ts +0 -30
  17. package/extension/integration/tools/crew-abort.ts +0 -56
  18. package/extension/integration/tools/crew-done.ts +0 -27
  19. package/extension/integration/tools/crew-list.ts +0 -36
  20. package/extension/integration/tools/crew-respond.ts +0 -38
  21. package/extension/integration/tools/crew-spawn.ts +0 -46
  22. package/extension/message-delivery-policy.ts +0 -22
  23. package/extension/runtime/crew-runtime.ts +0 -263
  24. package/extension/runtime/owner-session-coordinator.ts +0 -138
  25. package/extension/runtime/subagent-lifecycle.ts +0 -203
  26. package/extension/runtime/subagent-registry.ts +0 -122
  27. package/extension/runtime/subagent-transitions.ts +0 -100
  28. package/extension/status-widget.ts +0 -107
  29. package/extension/subagent-messages.ts +0 -116
  30. package/extension/tool-registry.ts +0 -19
  31. /package/extension/{runtime/overflow-recovery.ts → overflow-recovery.ts} +0 -0
@@ -1,369 +0,0 @@
1
- import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
2
- import {
3
- type AgentConfigFields,
4
- parseDefinitionFields,
5
- parseOverrideFields as parseConfigOverrideFields,
6
- } from "./agent-config-fields.js";
7
-
8
- export interface AgentConfig extends AgentConfigFields {
9
- name: string;
10
- description: string;
11
- systemPrompt: string;
12
- filePath: string;
13
- }
14
-
15
- type AgentConfigOverride = AgentConfigFields;
16
-
17
- export interface AgentDiscoveryWarning {
18
- filePath: string;
19
- message: string;
20
- }
21
-
22
- export interface AgentDiscoveryResult {
23
- agents: AgentConfig[];
24
- warnings: AgentDiscoveryWarning[];
25
- }
26
-
27
- interface ParseResult {
28
- agent: AgentConfig | null;
29
- warnings: AgentDiscoveryWarning[];
30
- }
31
-
32
- export interface AgentDefinitionFile {
33
- filePath: string;
34
- content: string | null;
35
- warnings?: AgentDiscoveryWarning[];
36
- }
37
-
38
- export interface AgentDefinitionSourceGroup {
39
- agentsDir: string;
40
- files: AgentDefinitionFile[];
41
- warnings?: AgentDiscoveryWarning[];
42
- }
43
-
44
- export interface AgentConfigFile {
45
- filePath: string;
46
- content: string | null;
47
- warnings?: AgentDiscoveryWarning[];
48
- }
49
-
50
- export interface AgentCatalogSource {
51
- loadAgentDefinitionGroups(cwd: string): AgentDefinitionSourceGroup[];
52
- loadConfigFiles(cwd: string): AgentConfigFile[];
53
- }
54
-
55
- interface ConfigParseResult {
56
- overrides: Record<string, AgentConfigOverride>;
57
- overrideSources: Record<string, string>;
58
- warnings: AgentDiscoveryWarning[];
59
- }
60
-
61
- function createDiscoveryWarning(filePath: string, message: string): AgentDiscoveryWarning {
62
- return { filePath, message };
63
- }
64
-
65
- function parseAgentDefinition(content: string, filePath: string): ParseResult {
66
- const warnings: AgentDiscoveryWarning[] = [];
67
-
68
- let frontmatter: Record<string, unknown>;
69
- let body: string;
70
- try {
71
- const parsed = parseFrontmatter<Record<string, unknown>>(content);
72
- frontmatter = parsed.frontmatter;
73
- body = parsed.body;
74
- } catch (error) {
75
- const reason = error instanceof Error ? error.message : String(error);
76
- return {
77
- agent: null,
78
- warnings: [
79
- createDiscoveryWarning(
80
- filePath,
81
- `Ignored invalid subagent definition. Frontmatter could not be parsed: ${reason}`,
82
- ),
83
- ],
84
- };
85
- }
86
-
87
- const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : undefined;
88
- const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : undefined;
89
-
90
- if (!name || !description) {
91
- return {
92
- agent: null,
93
- warnings: [
94
- createDiscoveryWarning(
95
- filePath,
96
- 'Ignored invalid subagent definition. Required frontmatter fields "name" and "description" must be non-empty strings.',
97
- ),
98
- ],
99
- };
100
- }
101
-
102
- if (/\s/.test(name)) {
103
- return {
104
- agent: null,
105
- warnings: [
106
- createDiscoveryWarning(
107
- filePath,
108
- `Ignored subagent definition "${name}". Subagent names cannot contain whitespace. Use "-" instead.`,
109
- ),
110
- ],
111
- };
112
- }
113
-
114
- const parsedFields = parseDefinitionFields(frontmatter, filePath, name);
115
- warnings.push(...parsedFields.warnings);
116
-
117
- return {
118
- agent: {
119
- name,
120
- description,
121
- ...parsedFields.fields,
122
- systemPrompt: body,
123
- filePath,
124
- },
125
- warnings,
126
- };
127
- }
128
-
129
- function parseAgentOverride(
130
- agentName: string,
131
- value: unknown,
132
- filePath: string,
133
- ): { override: AgentConfigOverride | null; warnings: AgentDiscoveryWarning[] } {
134
- if (!value || typeof value !== "object" || Array.isArray(value)) {
135
- return {
136
- override: null,
137
- warnings: [
138
- createDiscoveryWarning(
139
- filePath,
140
- `Subagent override "${agentName}" must be a JSON object, ignoring`,
141
- ),
142
- ],
143
- };
144
- }
145
-
146
- const parsedFields = parseConfigOverrideFields(value as Record<string, unknown>, filePath, agentName);
147
- return { override: parsedFields.fields, warnings: parsedFields.warnings };
148
- }
149
-
150
- function parseConfigFile(content: string, filePath: string): ConfigParseResult {
151
- let parsed: unknown;
152
- try {
153
- parsed = JSON.parse(content);
154
- } catch (error) {
155
- const reason = error instanceof Error ? error.message : String(error);
156
- return {
157
- overrides: {},
158
- overrideSources: {},
159
- warnings: [
160
- createDiscoveryWarning(
161
- filePath,
162
- `Ignored pi-crew config. JSON could not be parsed: ${reason}`,
163
- ),
164
- ],
165
- };
166
- }
167
-
168
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
169
- return {
170
- overrides: {},
171
- overrideSources: {},
172
- warnings: [
173
- createDiscoveryWarning(
174
- filePath,
175
- "Ignored pi-crew config. Root value must be a JSON object.",
176
- ),
177
- ],
178
- };
179
- }
180
-
181
- const root = parsed as Record<string, unknown>;
182
- if (root.agents === undefined) {
183
- return { overrides: {}, overrideSources: {}, warnings: [] };
184
- }
185
-
186
- if (!root.agents || typeof root.agents !== "object" || Array.isArray(root.agents)) {
187
- return {
188
- overrides: {},
189
- overrideSources: {},
190
- warnings: [
191
- createDiscoveryWarning(
192
- filePath,
193
- 'Ignored pi-crew config. Field "agents" must be a JSON object.',
194
- ),
195
- ],
196
- };
197
- }
198
-
199
- const overrides: Record<string, AgentConfigOverride> = {};
200
- const overrideSources: Record<string, string> = {};
201
- const warnings: AgentDiscoveryWarning[] = [];
202
-
203
- for (const [agentName, value] of Object.entries(root.agents)) {
204
- if (!agentName.trim()) {
205
- warnings.push(
206
- createDiscoveryWarning(
207
- filePath,
208
- "Ignored pi-crew config entry with empty subagent name.",
209
- ),
210
- );
211
- continue;
212
- }
213
-
214
- const parsedOverride = parseAgentOverride(agentName, value, filePath);
215
- warnings.push(...parsedOverride.warnings);
216
- if (parsedOverride.override) {
217
- overrides[agentName] = parsedOverride.override;
218
- overrideSources[agentName] = filePath;
219
- }
220
- }
221
-
222
- return { overrides, overrideSources, warnings };
223
- }
224
-
225
- function mergeConfigOverrides(
226
- base: Record<string, AgentConfigOverride>,
227
- override: Record<string, AgentConfigOverride>,
228
- ): Record<string, AgentConfigOverride> {
229
- const merged: Record<string, AgentConfigOverride> = { ...base };
230
-
231
- for (const [agentName, agentOverride] of Object.entries(override)) {
232
- merged[agentName] = {
233
- ...(merged[agentName] ?? {}),
234
- ...agentOverride,
235
- };
236
- }
237
-
238
- return merged;
239
- }
240
-
241
- function mergeOverrideSources(
242
- base: Record<string, string>,
243
- override: Record<string, string>,
244
- ): Record<string, string> {
245
- return {
246
- ...base,
247
- ...override,
248
- };
249
- }
250
-
251
- function applyAgentOverride(agent: AgentConfig, override: AgentConfigOverride): AgentConfig {
252
- return {
253
- ...agent,
254
- ...(override.model !== undefined ? { model: override.model, parsedModel: override.parsedModel } : {}),
255
- ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
256
- ...(override.tools !== undefined ? { tools: override.tools } : {}),
257
- ...(override.skills !== undefined ? { skills: override.skills } : {}),
258
- ...(override.compaction !== undefined ? { compaction: override.compaction } : {}),
259
- ...(override.interactive !== undefined ? { interactive: override.interactive } : {}),
260
- };
261
- }
262
-
263
- function loadAgentDefinitionFromFile(file: AgentDefinitionFile): ParseResult {
264
- if (!file.content) {
265
- return { agent: null, warnings: file.warnings ?? [] };
266
- }
267
-
268
- const parsed = parseAgentDefinition(file.content, file.filePath);
269
- return {
270
- agent: parsed.agent,
271
- warnings: [...(file.warnings ?? []), ...parsed.warnings],
272
- };
273
- }
274
-
275
- function mergeConfigFiles(configFiles: AgentConfigFile[]): ConfigParseResult {
276
- let overrides: Record<string, AgentConfigOverride> = {};
277
- let overrideSources: Record<string, string> = {};
278
- const warnings: AgentDiscoveryWarning[] = [];
279
-
280
- for (const configFile of configFiles) {
281
- warnings.push(...(configFile.warnings ?? []));
282
- if (!configFile.content) continue;
283
-
284
- const parsed = parseConfigFile(configFile.content, configFile.filePath);
285
- overrides = mergeConfigOverrides(overrides, parsed.overrides);
286
- overrideSources = mergeOverrideSources(overrideSources, parsed.overrideSources);
287
- warnings.push(...parsed.warnings);
288
- }
289
-
290
- return { overrides, overrideSources, warnings };
291
- }
292
-
293
- export class AgentCatalog {
294
- constructor(private readonly source: AgentCatalogSource) {}
295
-
296
- discover(cwd: string = process.cwd()): AgentDiscoveryResult {
297
- const agents: AgentConfig[] = [];
298
- const warnings: AgentDiscoveryWarning[] = [];
299
- const seenNames = new Map<string, string>();
300
-
301
- for (const group of this.source.loadAgentDefinitionGroups(cwd)) {
302
- this.loadAgentsFromGroup(group, seenNames, agents, warnings);
303
- }
304
-
305
- const configOverrides = mergeConfigFiles(this.source.loadConfigFiles(cwd));
306
- warnings.push(...configOverrides.warnings);
307
-
308
- const finalAgents = agents.map((agent) => {
309
- const override = configOverrides.overrides[agent.name];
310
- return override ? applyAgentOverride(agent, override) : agent;
311
- });
312
-
313
- for (const agentName of Object.keys(configOverrides.overrides)) {
314
- if (!seenNames.has(agentName)) {
315
- warnings.push(
316
- createDiscoveryWarning(
317
- configOverrides.overrideSources[agentName] ?? "pi-crew.json",
318
- `Subagent override "${agentName}" does not match any discovered subagent, ignoring`,
319
- ),
320
- );
321
- }
322
- }
323
-
324
- return { agents: finalAgents, warnings };
325
- }
326
-
327
- /**
328
- * Loads agents from a single source group into the agents list.
329
- * Skips agents whose name already exists in seenNames (higher-priority source wins).
330
- * Within the same source group, duplicate names produce a warning.
331
- */
332
- private loadAgentsFromGroup(
333
- group: AgentDefinitionSourceGroup,
334
- seenNames: Map<string, string>,
335
- agents: AgentConfig[],
336
- warnings: AgentDiscoveryWarning[],
337
- ): void {
338
- warnings.push(...(group.warnings ?? []));
339
-
340
- const groupNames = new Set<string>();
341
-
342
- for (const file of group.files) {
343
- const loaded = loadAgentDefinitionFromFile(file);
344
- warnings.push(...loaded.warnings);
345
- if (!loaded.agent) continue;
346
-
347
- const { name } = loaded.agent;
348
-
349
- // Duplicate within the same source group
350
- if (groupNames.has(name)) {
351
- warnings.push(
352
- createDiscoveryWarning(
353
- file.filePath,
354
- `Duplicate subagent name "${name}" in ${group.agentsDir}, skipping`,
355
- ),
356
- );
357
- continue;
358
- }
359
-
360
- groupNames.add(name);
361
-
362
- // Higher-priority source already registered this name
363
- if (seenNames.has(name)) continue;
364
-
365
- seenNames.set(name, file.filePath);
366
- agents.push(loaded.agent);
367
- }
368
- }
369
- }
@@ -1,359 +0,0 @@
1
- import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
- import { type SupportedToolName, isSupportedToolName } from "./tool-registry.js";
3
-
4
- export interface ParsedModel {
5
- provider: string;
6
- modelId: string;
7
- }
8
-
9
- export interface AgentConfigFields {
10
- model?: string;
11
- parsedModel?: ParsedModel;
12
- thinking?: ThinkingLevel;
13
- tools?: SupportedToolName[];
14
- skills?: string[];
15
- compaction?: boolean;
16
- interactive?: boolean;
17
- }
18
-
19
- export interface AgentConfigFieldParseWarning {
20
- filePath: string;
21
- message: string;
22
- }
23
-
24
- export interface AgentConfigFieldParseResult {
25
- fields: AgentConfigFields;
26
- warnings: AgentConfigFieldParseWarning[];
27
- }
28
-
29
- const VALID_THINKING_LEVELS: readonly string[] = [
30
- "off",
31
- "minimal",
32
- "low",
33
- "medium",
34
- "high",
35
- "xhigh",
36
- ];
37
-
38
- const ALLOWED_OVERRIDE_FIELDS = new Set([
39
- "model",
40
- "thinking",
41
- "tools",
42
- "skills",
43
- "compaction",
44
- "interactive",
45
- ]);
46
-
47
- type ParsedFieldName = "model" | "thinking" | "tools" | "skills" | "compaction" | "interactive";
48
- type ParsedListFieldName = "tools" | "skills";
49
- type ParsedBooleanFieldName = "compaction" | "interactive";
50
- type WarningSubject = "subagent" | "subagent override";
51
-
52
- type ParsedFieldWarning =
53
- | {
54
- code: "invalid-list-format";
55
- fieldName: ParsedListFieldName;
56
- }
57
- | {
58
- code: "invalid-type";
59
- fieldName: ParsedFieldName;
60
- expected: "string" | "boolean";
61
- }
62
- | {
63
- code: "invalid-model-format";
64
- model: string;
65
- }
66
- | {
67
- code: "invalid-thinking-level";
68
- thinking: string;
69
- }
70
- | {
71
- code: "unknown-tools";
72
- tools: string[];
73
- };
74
-
75
- interface ParseFieldOptions {
76
- warnOnInvalidType: boolean;
77
- setValueOnInvalidType: boolean;
78
- }
79
-
80
- interface ParsedFieldSet extends AgentConfigFields {
81
- warnings: ParsedFieldWarning[];
82
- }
83
-
84
- function createParseWarning(filePath: string, message: string): AgentConfigFieldParseWarning {
85
- return { filePath, message };
86
- }
87
-
88
- /**
89
- * Converts a comma-separated string or YAML array to string[].
90
- * Returns undefined for null/undefined input.
91
- */
92
- function parseCommaSeparated(value: unknown): string[] | undefined {
93
- if (value == null) return undefined;
94
-
95
- if (Array.isArray(value)) {
96
- return value.map((v) => String(v).trim()).filter(Boolean);
97
- }
98
-
99
- if (typeof value === "string") {
100
- return value
101
- .split(",")
102
- .map((s) => s.trim())
103
- .filter(Boolean);
104
- }
105
-
106
- return undefined;
107
- }
108
-
109
- function formatFieldWarning(subject: WarningSubject, name: string, warning: ParsedFieldWarning): string {
110
- const prefix = `${subject === "subagent" ? "Subagent" : "Subagent override"} "${name}"`;
111
-
112
- switch (warning.code) {
113
- case "invalid-list-format":
114
- return `${prefix}: invalid ${warning.fieldName} field, expected a comma-separated string or YAML array`;
115
- case "invalid-type":
116
- return `${prefix}: field "${warning.fieldName}" must be a ${warning.expected}, ignoring`;
117
- case "invalid-model-format":
118
- return `${prefix}: invalid model format "${warning.model}" (expected "provider/model-id"), ignoring model field`;
119
- case "invalid-thinking-level":
120
- return `${prefix}: invalid thinking level "${warning.thinking}", ignoring`;
121
- case "unknown-tools":
122
- return `${prefix}: unknown tools ${warning.tools.map((toolName) => `"${toolName}"`).join(", ")}, ignoring`;
123
- }
124
- }
125
-
126
- function toParseWarnings(
127
- filePath: string,
128
- subject: WarningSubject,
129
- name: string,
130
- warnings: ParsedFieldWarning[],
131
- ): AgentConfigFieldParseWarning[] {
132
- return warnings.map((warning) => createParseWarning(filePath, formatFieldWarning(subject, name, warning)));
133
- }
134
-
135
- function parseListField(value: unknown, fieldName: ParsedListFieldName): { values: string[]; warnings: ParsedFieldWarning[] } {
136
- if (value == null) return { values: [], warnings: [] };
137
-
138
- const parsed = parseCommaSeparated(value);
139
- if (parsed !== undefined) return { values: parsed, warnings: [] };
140
-
141
- return {
142
- values: [],
143
- warnings: [{ code: "invalid-list-format", fieldName }],
144
- };
145
- }
146
-
147
- /**
148
- * Parses "provider/model-id" format.
149
- * Returns null if "/" is missing.
150
- */
151
- function parseModel(value: unknown): ParsedModel | null {
152
- if (typeof value !== "string" || !value.includes("/")) {
153
- return null;
154
- }
155
-
156
- const slashIndex = value.indexOf("/");
157
- const provider = value.slice(0, slashIndex).trim();
158
- const modelId = value.slice(slashIndex + 1).trim();
159
-
160
- if (!provider || !modelId) return null;
161
-
162
- return { provider, modelId };
163
- }
164
-
165
- function validateThinkingLevel(value: string | undefined): ThinkingLevel | undefined {
166
- if (!value) return undefined;
167
- if (VALID_THINKING_LEVELS.includes(value)) return value as ThinkingLevel;
168
- return undefined;
169
- }
170
-
171
- function parseModelField(value: unknown, options: ParseFieldOptions): Pick<ParsedFieldSet, "model" | "parsedModel" | "warnings"> {
172
- if (typeof value === "string") {
173
- const parsedModel = parseModel(value);
174
- if (!parsedModel) {
175
- return {
176
- ...(options.setValueOnInvalidType ? { model: value } : {}),
177
- warnings: [{ code: "invalid-model-format", model: value }],
178
- };
179
- }
180
-
181
- return {
182
- model: value,
183
- parsedModel,
184
- warnings: [],
185
- };
186
- }
187
-
188
- if (value !== undefined && options.warnOnInvalidType) {
189
- return {
190
- warnings: [{ code: "invalid-type", fieldName: "model", expected: "string" }],
191
- };
192
- }
193
-
194
- return { warnings: [] };
195
- }
196
-
197
- function parseThinkingField(value: unknown, options: ParseFieldOptions): Pick<ParsedFieldSet, "thinking" | "warnings"> {
198
- if (typeof value === "string") {
199
- const thinking = validateThinkingLevel(value);
200
- if (!thinking) {
201
- return {
202
- warnings: [{ code: "invalid-thinking-level", thinking: value }],
203
- };
204
- }
205
-
206
- return { thinking, warnings: [] };
207
- }
208
-
209
- if (value !== undefined && options.warnOnInvalidType) {
210
- return {
211
- warnings: [{ code: "invalid-type", fieldName: "thinking", expected: "string" }],
212
- };
213
- }
214
-
215
- return { warnings: [] };
216
- }
217
-
218
- function parseToolsField(value: unknown, options: ParseFieldOptions): Pick<ParsedFieldSet, "tools" | "warnings"> {
219
- const parsedTools = parseListField(value, "tools");
220
- const validTools = parsedTools.values.filter(isSupportedToolName);
221
- const invalidTools = parsedTools.values.filter((toolName) => !isSupportedToolName(toolName));
222
- const warnings: ParsedFieldWarning[] = [...parsedTools.warnings];
223
-
224
- if (invalidTools.length > 0) {
225
- warnings.push({ code: "unknown-tools", tools: invalidTools });
226
- }
227
-
228
- if (invalidTools.length > 0 && validTools.length === 0 && !options.setValueOnInvalidType) {
229
- return { warnings };
230
- }
231
-
232
- if (parsedTools.warnings.length > 0 && !options.setValueOnInvalidType) {
233
- return { warnings };
234
- }
235
-
236
- return {
237
- tools: validTools,
238
- warnings,
239
- };
240
- }
241
-
242
- function parseSkillsField(value: unknown, options: ParseFieldOptions): Pick<ParsedFieldSet, "skills" | "warnings"> {
243
- const parsedSkills = parseListField(value, "skills");
244
- if (parsedSkills.warnings.length > 0 && !options.setValueOnInvalidType) {
245
- return { warnings: parsedSkills.warnings };
246
- }
247
-
248
- return {
249
- skills: parsedSkills.values,
250
- warnings: parsedSkills.warnings,
251
- };
252
- }
253
-
254
- function parseBooleanField(
255
- fieldName: ParsedBooleanFieldName,
256
- value: unknown,
257
- options: ParseFieldOptions,
258
- ): Pick<ParsedFieldSet, ParsedBooleanFieldName | "warnings"> {
259
- if (typeof value === "boolean") {
260
- return {
261
- [fieldName]: value,
262
- warnings: [],
263
- };
264
- }
265
-
266
- if (value !== undefined && options.warnOnInvalidType) {
267
- return {
268
- warnings: [{ code: "invalid-type", fieldName, expected: "boolean" }],
269
- };
270
- }
271
-
272
- return { warnings: [] };
273
- }
274
-
275
- function parseSharedFields(record: Record<string, unknown>, options: ParseFieldOptions): ParsedFieldSet {
276
- const model = parseModelField(record.model, options);
277
- const thinking = parseThinkingField(record.thinking, options);
278
- const tools = Object.prototype.hasOwnProperty.call(record, "tools")
279
- ? parseToolsField(record.tools, options)
280
- : { warnings: [] };
281
- const skills = Object.prototype.hasOwnProperty.call(record, "skills")
282
- ? parseSkillsField(record.skills, options)
283
- : { warnings: [] };
284
- const compaction = parseBooleanField("compaction", record.compaction, options);
285
- const interactive = parseBooleanField("interactive", record.interactive, options);
286
-
287
- return {
288
- ...("model" in model ? { model: model.model } : {}),
289
- ...("parsedModel" in model ? { parsedModel: model.parsedModel } : {}),
290
- ...(thinking.thinking !== undefined ? { thinking: thinking.thinking } : {}),
291
- ...(tools.tools !== undefined ? { tools: tools.tools } : {}),
292
- ...(skills.skills !== undefined ? { skills: skills.skills } : {}),
293
- ...(compaction.compaction !== undefined ? { compaction: compaction.compaction } : {}),
294
- ...(interactive.interactive !== undefined ? { interactive: interactive.interactive } : {}),
295
- warnings: [
296
- ...model.warnings,
297
- ...thinking.warnings,
298
- ...tools.warnings,
299
- ...skills.warnings,
300
- ...compaction.warnings,
301
- ...interactive.warnings,
302
- ],
303
- };
304
- }
305
-
306
- export function parseDefinitionFields(
307
- record: Record<string, unknown>,
308
- filePath: string,
309
- agentName: string,
310
- ): AgentConfigFieldParseResult {
311
- const parsed = parseSharedFields(record, {
312
- warnOnInvalidType: false,
313
- setValueOnInvalidType: true,
314
- });
315
- const { warnings, ...fields } = parsed;
316
-
317
- return {
318
- fields,
319
- warnings: toParseWarnings(filePath, "subagent", agentName, warnings),
320
- };
321
- }
322
-
323
- export function parseOverrideFields(
324
- record: Record<string, unknown>,
325
- filePath: string,
326
- agentName: string,
327
- ): AgentConfigFieldParseResult {
328
- const warnings: AgentConfigFieldParseWarning[] = [];
329
-
330
- for (const fieldName of Object.keys(record)) {
331
- if (fieldName === "name" || fieldName === "description") {
332
- warnings.push(
333
- createParseWarning(
334
- filePath,
335
- `Subagent override "${agentName}": field "${fieldName}" is not overridable, ignoring`,
336
- ),
337
- );
338
- continue;
339
- }
340
-
341
- if (!ALLOWED_OVERRIDE_FIELDS.has(fieldName)) {
342
- warnings.push(
343
- createParseWarning(
344
- filePath,
345
- `Subagent override "${agentName}": unknown field "${fieldName}", ignoring`,
346
- ),
347
- );
348
- }
349
- }
350
-
351
- const parsed = parseSharedFields(record, {
352
- warnOnInvalidType: true,
353
- setValueOnInvalidType: false,
354
- });
355
- const { warnings: fieldWarnings, ...fields } = parsed;
356
- warnings.push(...toParseWarnings(filePath, "subagent override", agentName, fieldWarnings));
357
-
358
- return { fields, warnings };
359
- }