@melihmucuk/pi-crew 1.0.16 → 1.0.17
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/README.md +8 -8
- package/agents/code-reviewer.md +2 -2
- package/agents/oracle.md +1 -1
- package/agents/planner.md +5 -1
- package/agents/quality-reviewer.md +2 -2
- package/agents/scout.md +2 -2
- package/agents/worker.md +3 -3
- package/extension/agent-catalog.ts +369 -0
- package/extension/agent-config-fields.ts +359 -0
- package/extension/agent-discovery.ts +49 -717
- package/extension/index.ts +4 -2
- package/extension/integration/crew-tool-actions.ts +306 -0
- package/extension/integration/crew-tool-executor.ts +109 -0
- package/extension/integration/register-tools.ts +10 -2
- package/extension/integration/tool-presentation.ts +0 -20
- package/extension/integration/tools/crew-abort.ts +14 -84
- package/extension/integration/tools/crew-done.ts +7 -26
- package/extension/integration/tools/crew-list.ts +4 -60
- package/extension/integration/tools/crew-respond.ts +8 -29
- package/extension/integration/tools/crew-spawn.ts +15 -56
- package/extension/message-delivery-policy.ts +22 -0
- package/extension/runtime/crew-runtime.ts +60 -223
- package/extension/runtime/{delivery-coordinator.ts → owner-session-coordinator.ts} +44 -37
- package/extension/runtime/subagent-lifecycle.ts +203 -0
- package/extension/runtime/subagent-registry.ts +50 -6
- package/extension/runtime/subagent-transitions.ts +100 -0
- package/extension/subagent-messages.ts +9 -17
- package/package.json +8 -6
- package/prompts/pi-crew-plan.md +14 -13
- package/prompts/pi-crew-review.md +20 -16
- package/skills/pi-crew/REFERENCE.md +32 -20
- package/skills/pi-crew/SKILL.md +13 -10
- package/extension/integration/tools/tool-deps.ts +0 -16
- package/extension/integration.ts +0 -13
- package/extension/runtime/subagent-state.ts +0 -59
|
@@ -0,0 +1,359 @@
|
|
|
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
|
+
}
|