pi-subagents 0.34.0 → 0.35.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 +78 -9
- package/README.md +213 -32
- package/index.ts +1 -0
- package/install.mjs +1 -1
- package/package.json +23 -8
- package/prompts/review-loop.md +3 -1
- package/skills/pi-subagents/SKILL.md +87 -25
- package/src/agents/agent-management.ts +82 -15
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +91 -49
- package/src/agents/frontmatter.ts +67 -13
- package/src/agents/skills.ts +25 -12
- package/src/api/background-work.ts +197 -0
- package/src/api/delegation.ts +158 -0
- package/src/extension/chain-validation.ts +165 -0
- package/src/extension/doctor.ts +15 -0
- package/src/extension/fanout-child.ts +3 -1
- package/src/extension/index.ts +65 -124
- package/src/extension/rpc.ts +10 -2
- package/src/extension/schemas.ts +18 -14
- package/src/extension/steering-notices.ts +35 -0
- package/src/extension/tool-description.ts +20 -9
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/native-supervisor-channel.ts +9 -1
- package/src/intercom/result-intercom.ts +4 -0
- package/src/runs/background/async-execution.ts +293 -44
- package/src/runs/background/async-job-tracker.ts +56 -9
- package/src/runs/background/async-resume.ts +159 -52
- package/src/runs/background/async-status.ts +25 -18
- package/src/runs/background/auto-drain.ts +67 -0
- package/src/runs/background/chain-root-attachment.ts +16 -8
- package/src/runs/background/control-channel.ts +260 -13
- package/src/runs/background/fleet-view.ts +23 -2
- package/src/runs/background/notify.ts +79 -10
- package/src/runs/background/result-watcher.ts +12 -9
- package/src/runs/background/run-id-resolver.ts +14 -2
- package/src/runs/background/run-status.ts +23 -15
- package/src/runs/background/scheduled-runs.ts +3 -0
- package/src/runs/background/stale-run-reconciler.ts +32 -10
- package/src/runs/background/steering.ts +237 -0
- package/src/runs/background/subagent-runner.ts +898 -236
- package/src/runs/background/subagent-wait.ts +484 -0
- package/src/runs/background/top-level-async.ts +2 -1
- package/src/runs/background/wait-config.ts +36 -0
- package/src/runs/background/wait-tool.ts +26 -0
- package/src/runs/foreground/async-steering-action.ts +230 -0
- package/src/runs/foreground/chain-clarify.ts +22 -6
- package/src/runs/foreground/chain-execution.ts +50 -32
- package/src/runs/foreground/execution.ts +308 -94
- package/src/runs/foreground/subagent-executor.ts +592 -268
- package/src/runs/shared/acceptance.ts +355 -97
- package/src/runs/shared/child-protocol.ts +121 -0
- package/src/runs/shared/completion-guard.ts +8 -127
- package/src/runs/shared/dynamic-fanout.ts +6 -4
- package/src/runs/shared/model-fallback.ts +36 -0
- package/src/runs/shared/nested-events.ts +9 -4
- package/src/runs/shared/nested-render.ts +4 -1
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +34 -7
- package/src/runs/shared/pi-spawn.ts +18 -12
- package/src/runs/shared/session-lease.ts +279 -0
- package/src/runs/shared/single-output.ts +61 -6
- package/src/runs/shared/spawn-budget.ts +128 -0
- package/src/runs/shared/subagent-control.ts +10 -6
- package/src/runs/shared/subagent-prompt-runtime.ts +127 -26
- package/src/runs/shared/task-intent.ts +176 -0
- package/src/runs/shared/tool-availability.ts +65 -0
- package/src/runs/shared/turn-budget.ts +49 -4
- package/src/shared/atomic-json.ts +4 -1
- package/src/shared/fork-context.ts +28 -3
- package/src/shared/model-info.ts +7 -4
- package/src/shared/status-format.ts +7 -1
- package/src/shared/types.ts +203 -25
- package/src/shared/utils.ts +35 -7
- package/src/slash/delegation-adapters.ts +457 -0
- package/src/slash/delegation-request.ts +103 -0
- package/src/slash/prompt-template-bridge.ts +167 -344
- package/src/slash/slash-commands.ts +239 -6
- package/src/slash/subagents-admin.ts +428 -0
- package/src/slash/subagents-editor.ts +86 -0
- package/src/tui/fleet.ts +405 -0
- package/src/tui/render.ts +90 -16
- package/src/watchdog/change-signature.ts +127 -0
- package/src/watchdog/child-status.ts +205 -0
- package/src/watchdog/emission-guard.ts +123 -0
- package/src/watchdog/lsp-diagnostics.ts +532 -0
- package/src/watchdog/model-selection.ts +167 -0
- package/src/watchdog/register-child.ts +117 -0
- package/src/watchdog/register-main.ts +433 -0
- package/src/watchdog/render.ts +54 -0
- package/src/watchdog/review.ts +293 -0
- package/src/watchdog/runtime.ts +712 -0
- package/src/watchdog/settings.ts +528 -0
- package/src/watchdog/tool-actions.ts +155 -0
- package/src/watchdog/turn-delta.ts +161 -0
- package/src/watchdog/types.ts +188 -0
- package/src/watchdog/warning-format.ts +73 -0
- package/src/runs/background/wait.ts +0 -394
|
@@ -21,8 +21,9 @@ import {
|
|
|
21
21
|
removeBuiltinAgentOverrideFields,
|
|
22
22
|
} from "./agents.ts";
|
|
23
23
|
import { serializeAgent } from "./agent-serializer.ts";
|
|
24
|
+
import { mergeAgentsForScope } from "./agent-selection.ts";
|
|
24
25
|
import { serializeChain, serializeJsonChain } from "./chain-serializer.ts";
|
|
25
|
-
import { discoverAvailableSkills } from "./skills.ts";
|
|
26
|
+
import { discoverAvailableSkills, resolveSkills } from "./skills.ts";
|
|
26
27
|
import {
|
|
27
28
|
buildProactiveSkillSubagentRecommendationLines,
|
|
28
29
|
} from "./proactive-skills.ts";
|
|
@@ -30,7 +31,9 @@ import { parseFrontmatter } from "./frontmatter.ts";
|
|
|
30
31
|
import { toModelInfo } from "../shared/model-info.ts";
|
|
31
32
|
import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
32
33
|
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
33
|
-
import
|
|
34
|
+
import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
|
|
35
|
+
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
36
|
+
import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
34
37
|
import { getProjectConfigDir } from "../shared/utils.ts";
|
|
35
38
|
|
|
36
39
|
type ManagementAction = "list" | "get" | "models" | "create" | "update" | "delete" | "eject" | "disable" | "enable" | "reset";
|
|
@@ -119,7 +122,7 @@ function findChains(name: string, cwd: string, scope: AgentScope = "both"): Chai
|
|
|
119
122
|
const raw = name.trim();
|
|
120
123
|
const sanitized = sanitizeName(raw);
|
|
121
124
|
return discoverAgentsAll(cwd).chains
|
|
122
|
-
.filter((c) => (scope === "both" || c.source === scope) && (c.name === raw || c.name === sanitized))
|
|
125
|
+
.filter((c) => (scope === "both" || c.source === "package" || c.source === scope) && (c.name === raw || c.name === sanitized))
|
|
123
126
|
.sort((a, b) => a.source.localeCompare(b.source));
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -187,14 +190,18 @@ function fallbackModelsWarning(ctx: ManagementContext, fallbackModels: string[]
|
|
|
187
190
|
return missing.length ? `Warning: fallback models not in the current model registry: ${missing.join(", ")}.` : undefined;
|
|
188
191
|
}
|
|
189
192
|
|
|
190
|
-
function skillsWarning(cwd: string,
|
|
191
|
-
if (!skills
|
|
192
|
-
const
|
|
193
|
-
|
|
193
|
+
function skillsWarning(cwd: string, agent: Pick<AgentConfig, "skills" | "skillPath" | "filePath">): string | undefined {
|
|
194
|
+
if (!agent.skills?.length) return undefined;
|
|
195
|
+
const { missing } = resolveSkills(
|
|
196
|
+
agent.skills,
|
|
197
|
+
cwd,
|
|
198
|
+
agent.skillPath,
|
|
199
|
+
agent.filePath ? path.dirname(agent.filePath) : cwd,
|
|
200
|
+
);
|
|
194
201
|
return missing.length ? `Warning: skills not found: ${missing.join(", ")}.` : undefined;
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
204
|
+
export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
198
205
|
const base = agent.override?.base;
|
|
199
206
|
if (!base) return { ...agent };
|
|
200
207
|
|
|
@@ -207,9 +214,11 @@ function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
207
214
|
inheritProjectContext: base.inheritProjectContext,
|
|
208
215
|
inheritSkills: base.inheritSkills,
|
|
209
216
|
defaultContext: base.defaultContext,
|
|
217
|
+
acceptanceRole: base.acceptanceRole,
|
|
210
218
|
disabled: base.disabled,
|
|
211
219
|
systemPrompt: base.systemPrompt,
|
|
212
220
|
skills: base.skills ? [...base.skills] : undefined,
|
|
221
|
+
skillPath: base.skillPath ? [...base.skillPath] : undefined,
|
|
213
222
|
tools: base.tools ? [...base.tools] : undefined,
|
|
214
223
|
mcpDirectTools: base.mcpDirectTools ? [...base.mcpDirectTools] : undefined,
|
|
215
224
|
subagentOnlyExtensions: base.subagentOnlyExtensions ? [...base.subagentOnlyExtensions] : undefined,
|
|
@@ -227,7 +236,7 @@ function readAgentFrontmatterFields(filePath: string): Set<string> {
|
|
|
227
236
|
}
|
|
228
237
|
}
|
|
229
238
|
|
|
230
|
-
function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<string, unknown>): Set<string> {
|
|
239
|
+
export function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<string, unknown>): Set<string> {
|
|
231
240
|
const fields = readAgentFrontmatterFields(agent.filePath);
|
|
232
241
|
const changed = (...names: string[]) => {
|
|
233
242
|
for (const name of names) fields.delete(name);
|
|
@@ -241,6 +250,7 @@ function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<string,
|
|
|
241
250
|
if (hasKey(cfg, "fallbackModels")) changed("fallbackModels");
|
|
242
251
|
if (hasKey(cfg, "tools")) changed("tools");
|
|
243
252
|
if (hasKey(cfg, "skills")) changed("skill", "skills");
|
|
253
|
+
if (hasKey(cfg, "skillPath")) changed("skillPath");
|
|
244
254
|
if (hasKey(cfg, "extensions")) changed("extensions");
|
|
245
255
|
if (hasKey(cfg, "subagentOnlyExtensions")) changed("subagentOnlyExtensions");
|
|
246
256
|
if (hasKey(cfg, "thinking")) {
|
|
@@ -260,6 +270,11 @@ function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<string,
|
|
|
260
270
|
fields.add("inheritSkills");
|
|
261
271
|
}
|
|
262
272
|
if (hasKey(cfg, "defaultContext")) changed("defaultContext");
|
|
273
|
+
if (hasKey(cfg, "async")) changed("async");
|
|
274
|
+
if (hasKey(cfg, "timeoutMs")) changed("timeoutMs");
|
|
275
|
+
if (hasKey(cfg, "turnBudget")) changed("turnBudget");
|
|
276
|
+
if (hasKey(cfg, "acceptance")) changed("acceptance");
|
|
277
|
+
if (hasKey(cfg, "acceptanceRole")) changed("acceptanceRole");
|
|
263
278
|
if (hasKey(cfg, "output")) changed("output");
|
|
264
279
|
if (hasKey(cfg, "reads")) changed("defaultReads");
|
|
265
280
|
if (hasKey(cfg, "progress")) changed("defaultProgress");
|
|
@@ -382,6 +397,14 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
382
397
|
else if (typeof cfg.skills === "string") { const skills = parseCsv(cfg.skills); target.skills = skills.length ? skills : undefined; }
|
|
383
398
|
else return "config.skills must be a comma-separated string or false when provided.";
|
|
384
399
|
}
|
|
400
|
+
if (hasKey(cfg, "skillPath")) {
|
|
401
|
+
if (cfg.skillPath === false || cfg.skillPath === "") target.skillPath = undefined;
|
|
402
|
+
else if (typeof cfg.skillPath === "string") { const skillPath = parseCsv(cfg.skillPath); target.skillPath = skillPath.length ? skillPath : undefined; }
|
|
403
|
+
else if (Array.isArray(cfg.skillPath) && cfg.skillPath.every((entry) => typeof entry === "string")) {
|
|
404
|
+
const skillPath = [...new Set(cfg.skillPath.map((entry) => entry.trim()).filter(Boolean))];
|
|
405
|
+
target.skillPath = skillPath.length ? skillPath : undefined;
|
|
406
|
+
} else return "config.skillPath must be a comma-separated string, string array, or false when provided.";
|
|
407
|
+
}
|
|
385
408
|
if (hasKey(cfg, "extensions")) {
|
|
386
409
|
if (cfg.extensions === false) target.extensions = undefined;
|
|
387
410
|
else if (cfg.extensions === "") target.extensions = [];
|
|
@@ -416,6 +439,37 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
416
439
|
else if (cfg.defaultContext === "fresh" || cfg.defaultContext === "fork") target.defaultContext = cfg.defaultContext;
|
|
417
440
|
else return "config.defaultContext must be 'fresh', 'fork', or false when provided.";
|
|
418
441
|
}
|
|
442
|
+
if (hasKey(cfg, "async")) {
|
|
443
|
+
if (cfg.async === "") target.defaultAsync = undefined;
|
|
444
|
+
else if (typeof cfg.async === "boolean") target.defaultAsync = cfg.async;
|
|
445
|
+
else return "config.async must be a boolean or empty string when provided.";
|
|
446
|
+
}
|
|
447
|
+
if (hasKey(cfg, "timeoutMs")) {
|
|
448
|
+
if (cfg.timeoutMs === false || cfg.timeoutMs === "") target.defaultTimeoutMs = undefined;
|
|
449
|
+
else if (typeof cfg.timeoutMs === "number" && Number.isInteger(cfg.timeoutMs) && cfg.timeoutMs > 0) target.defaultTimeoutMs = cfg.timeoutMs;
|
|
450
|
+
else return "config.timeoutMs must be a positive integer or false when provided.";
|
|
451
|
+
}
|
|
452
|
+
if (hasKey(cfg, "turnBudget")) {
|
|
453
|
+
if (cfg.turnBudget === false || cfg.turnBudget === "") target.defaultTurnBudget = undefined;
|
|
454
|
+
else {
|
|
455
|
+
const resolved = resolveTurnBudgetConfig(cfg.turnBudget, "config.turnBudget");
|
|
456
|
+
if (resolved.error) return resolved.error;
|
|
457
|
+
target.defaultTurnBudget = resolved.turnBudget;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (hasKey(cfg, "acceptance")) {
|
|
461
|
+
if (cfg.acceptance === "") target.defaultAcceptance = undefined;
|
|
462
|
+
else {
|
|
463
|
+
const errors = validateAcceptanceInput(cfg.acceptance, "config.acceptance");
|
|
464
|
+
if (errors.length > 0) return errors.join(" ");
|
|
465
|
+
target.defaultAcceptance = cfg.acceptance as AcceptanceInput;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (hasKey(cfg, "acceptanceRole")) {
|
|
469
|
+
if (cfg.acceptanceRole === false || cfg.acceptanceRole === "") target.acceptanceRole = undefined;
|
|
470
|
+
else if (cfg.acceptanceRole === "read-only" || cfg.acceptanceRole === "writer") target.acceptanceRole = cfg.acceptanceRole;
|
|
471
|
+
else return "config.acceptanceRole must be 'read-only', 'writer', or false when provided.";
|
|
472
|
+
}
|
|
419
473
|
if (hasKey(cfg, "output")) {
|
|
420
474
|
if (cfg.output === false || cfg.output === "") target.output = undefined;
|
|
421
475
|
else if (typeof cfg.output === "string") target.output = cfg.output;
|
|
@@ -508,10 +562,16 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
508
562
|
if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
|
|
509
563
|
if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
|
|
510
564
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
565
|
+
if (agent.skillPath?.length) lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
|
|
511
566
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
512
567
|
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
513
568
|
lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
|
|
514
569
|
if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
|
|
570
|
+
if (agent.defaultAsync !== undefined) lines.push(`Async: ${agent.defaultAsync ? "true" : "false"}`);
|
|
571
|
+
if (agent.defaultTimeoutMs !== undefined) lines.push(`Timeout: ${agent.defaultTimeoutMs}ms`);
|
|
572
|
+
if (agent.defaultTurnBudget) lines.push(`Turn budget: ${JSON.stringify(agent.defaultTurnBudget)}`);
|
|
573
|
+
if (agent.defaultAcceptance !== undefined) lines.push(`Acceptance: ${typeof agent.defaultAcceptance === "object" ? JSON.stringify(agent.defaultAcceptance) : String(agent.defaultAcceptance)}`);
|
|
574
|
+
if (agent.acceptanceRole) lines.push(`Acceptance role: ${agent.acceptanceRole}`);
|
|
515
575
|
if (agent.source === "builtin") lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
|
|
516
576
|
if (agent.extensions !== undefined) lines.push(`Extensions: ${agent.extensions.length ? agent.extensions.join(", ") : "(none)"}`);
|
|
517
577
|
if (agent.subagentOnlyExtensions !== undefined) lines.push(`Subagent-only extensions: ${agent.subagentOnlyExtensions.length ? agent.subagentOnlyExtensions.join(", ") : "(none)"}`);
|
|
@@ -580,7 +640,8 @@ function formatChainDetail(chain: ChainConfig): string {
|
|
|
580
640
|
export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
581
641
|
const scope = normalizeListScope(params.agentScope) ?? "both";
|
|
582
642
|
const d = discoverAgentsAll(ctx.cwd);
|
|
583
|
-
const scopedAgents =
|
|
643
|
+
const scopedAgents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package)
|
|
644
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
584
645
|
const agents = scopedAgents.filter((a) => !a.disabled);
|
|
585
646
|
const chains = d.chains.filter((c) => scope === "both" || c.source === "package" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
586
647
|
const diagnostics = d.chainDiagnostics.filter((entry) => scope === "both" || entry.source === scope);
|
|
@@ -687,11 +748,17 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
687
748
|
|
|
688
749
|
function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
689
750
|
if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for get.", true);
|
|
751
|
+
const scope = normalizeListScope(params.agentScope);
|
|
752
|
+
if (!scope) return result("agentScope must be 'user', 'project', or 'both' for get.", true);
|
|
690
753
|
const hasBoth = Boolean(params.agent && params.chainName);
|
|
691
754
|
const blocks: string[] = [];
|
|
692
755
|
let anyFound = false;
|
|
693
756
|
if (params.agent) {
|
|
694
|
-
const
|
|
757
|
+
const raw = params.agent.trim();
|
|
758
|
+
const sanitized = sanitizeName(raw);
|
|
759
|
+
const d = discoverAgentsAll(ctx.cwd);
|
|
760
|
+
const matches = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package)
|
|
761
|
+
.filter((agent) => agent.name === raw || agent.name === sanitized);
|
|
695
762
|
if (!matches.length) {
|
|
696
763
|
const msg = `Agent '${params.agent}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`;
|
|
697
764
|
if (!hasBoth) return result(msg, true);
|
|
@@ -702,7 +769,7 @@ function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolR
|
|
|
702
769
|
}
|
|
703
770
|
}
|
|
704
771
|
if (params.chainName) {
|
|
705
|
-
const matches = findChains(params.chainName, ctx.cwd,
|
|
772
|
+
const matches = findChains(params.chainName, ctx.cwd, scope);
|
|
706
773
|
if (!matches.length) {
|
|
707
774
|
const msg = `Chain '${params.chainName}' not found. Available: ${availableNames(ctx.cwd, "chain").join(", ") || "none"}.`;
|
|
708
775
|
if (!hasBoth) return result(msg, true);
|
|
@@ -770,7 +837,7 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
|
|
|
770
837
|
if (mw) warnings.push(mw);
|
|
771
838
|
const fmw = fallbackModelsWarning(ctx, agent.fallbackModels);
|
|
772
839
|
if (fmw) warnings.push(fmw);
|
|
773
|
-
const sw = skillsWarning(ctx.cwd, agent
|
|
840
|
+
const sw = skillsWarning(ctx.cwd, agent);
|
|
774
841
|
if (sw) warnings.push(sw);
|
|
775
842
|
fs.writeFileSync(targetPath, serializeAgent(agent), "utf-8");
|
|
776
843
|
return result([`Created agent '${runtimeName}' at ${targetPath}.`, ...warnings].join("\n"));
|
|
@@ -819,8 +886,8 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
819
886
|
const fmw = fallbackModelsWarning(ctx, updated.fallbackModels);
|
|
820
887
|
if (fmw) warnings.push(fmw);
|
|
821
888
|
}
|
|
822
|
-
if (hasKey(cfg, "skills")) {
|
|
823
|
-
const sw = skillsWarning(ctx.cwd, updated
|
|
889
|
+
if (hasKey(cfg, "skills") || hasKey(cfg, "skillPath")) {
|
|
890
|
+
const sw = skillsWarning(ctx.cwd, updated);
|
|
824
891
|
if (sw) warnings.push(sw);
|
|
825
892
|
}
|
|
826
893
|
if (updated.name !== oldName) {
|
|
@@ -13,8 +13,14 @@ export const KNOWN_FIELDS = new Set([
|
|
|
13
13
|
"inheritProjectContext",
|
|
14
14
|
"inheritSkills",
|
|
15
15
|
"defaultContext",
|
|
16
|
+
"async",
|
|
17
|
+
"timeoutMs",
|
|
18
|
+
"turnBudget",
|
|
19
|
+
"acceptance",
|
|
20
|
+
"acceptanceRole",
|
|
16
21
|
"skill",
|
|
17
22
|
"skills",
|
|
23
|
+
"skillPath",
|
|
18
24
|
"extensions",
|
|
19
25
|
"subagentOnlyExtensions",
|
|
20
26
|
"output",
|
|
@@ -62,9 +68,22 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
|
|
|
62
68
|
if (!preservingExistingFrontmatter || preserve("inheritProjectContext")) lines.push(`inheritProjectContext: ${config.inheritProjectContext ? "true" : "false"}`);
|
|
63
69
|
if (!preservingExistingFrontmatter || preserve("inheritSkills")) lines.push(`inheritSkills: ${config.inheritSkills ? "true" : "false"}`);
|
|
64
70
|
if (config.defaultContext || preserve("defaultContext")) lines.push(`defaultContext: ${config.defaultContext ?? ""}`);
|
|
71
|
+
if (config.defaultAsync !== undefined || preserve("async")) lines.push(`async: ${config.defaultAsync === undefined ? "" : config.defaultAsync ? "true" : "false"}`);
|
|
72
|
+
if (config.defaultTimeoutMs !== undefined || preserve("timeoutMs")) lines.push(`timeoutMs: ${config.defaultTimeoutMs ?? ""}`);
|
|
73
|
+
if (config.defaultTurnBudget || preserve("turnBudget")) lines.push(`turnBudget: ${config.defaultTurnBudget ? JSON.stringify(config.defaultTurnBudget) : ""}`);
|
|
74
|
+
if (config.defaultAcceptance !== undefined || preserve("acceptance")) {
|
|
75
|
+
lines.push(`acceptance: ${config.defaultAcceptance === undefined
|
|
76
|
+
? ""
|
|
77
|
+
: typeof config.defaultAcceptance === "object"
|
|
78
|
+
? JSON.stringify(config.defaultAcceptance)
|
|
79
|
+
: String(config.defaultAcceptance)}`);
|
|
80
|
+
}
|
|
81
|
+
if (config.acceptanceRole || preserve("acceptanceRole")) lines.push(`acceptanceRole: ${config.acceptanceRole ?? ""}`);
|
|
65
82
|
|
|
66
83
|
const skillsValue = joinComma(config.skills);
|
|
67
84
|
if (skillsValue || preserve("skill", "skills")) lines.push(`skills: ${skillsValue ?? ""}`);
|
|
85
|
+
const skillPathValue = joinComma(config.skillPath);
|
|
86
|
+
if (skillPathValue || preserve("skillPath")) lines.push(`skillPath: ${skillPathValue ?? ""}`);
|
|
68
87
|
|
|
69
88
|
if (config.extensions !== undefined) {
|
|
70
89
|
const extensionsValue = joinComma(config.extensions);
|
package/src/agents/agents.ts
CHANGED
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
|
+
import { parse as parseYaml } from "yaml";
|
|
7
8
|
import * as os from "node:os";
|
|
8
9
|
import * as path from "node:path";
|
|
9
10
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import type { AcceptanceInput, OutputMode, ToolBudgetConfig } from "../shared/types.ts";
|
|
11
|
+
import type { AcceptanceInput, AcceptanceRole, OutputMode, ToolBudgetConfig, TurnBudgetConfig } from "../shared/types.ts";
|
|
11
12
|
import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";
|
|
12
13
|
import { KNOWN_FIELDS } from "./agent-serializer.ts";
|
|
13
14
|
import { parseChain, parseJsonChain } from "./chain-serializer.ts";
|
|
14
15
|
import { mergeAgentsForScope } from "./agent-selection.ts";
|
|
15
|
-
import { parseFrontmatter } from "./frontmatter.ts";
|
|
16
|
+
import { parseFrontmatter, parseFrontmatterList } from "./frontmatter.ts";
|
|
16
17
|
import { buildRuntimeName, parsePackageName } from "./identity.ts";
|
|
17
18
|
import { parseModelScopeConfig, type ModelScopeConfig } from "../runs/shared/model-scope.ts";
|
|
18
19
|
export { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.ts";
|
|
19
20
|
import { parseMemoryFrontmatter } from "./agent-memory.ts";
|
|
21
|
+
import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
|
|
22
|
+
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
20
23
|
|
|
21
24
|
export type AgentScope = "user" | "project" | "both";
|
|
22
25
|
|
|
@@ -62,9 +65,11 @@ export interface BuiltinAgentOverrideBase {
|
|
|
62
65
|
inheritProjectContext: boolean;
|
|
63
66
|
inheritSkills: boolean;
|
|
64
67
|
defaultContext?: AgentDefaultContext;
|
|
68
|
+
acceptanceRole?: AcceptanceRole;
|
|
65
69
|
disabled?: boolean;
|
|
66
70
|
systemPrompt: string;
|
|
67
71
|
skills?: string[];
|
|
72
|
+
skillPath?: string[];
|
|
68
73
|
tools?: string[];
|
|
69
74
|
mcpDirectTools?: string[];
|
|
70
75
|
subagentOnlyExtensions?: string[];
|
|
@@ -80,6 +85,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
80
85
|
inheritProjectContext?: boolean;
|
|
81
86
|
inheritSkills?: boolean;
|
|
82
87
|
defaultContext?: AgentDefaultContext | false;
|
|
88
|
+
acceptanceRole?: AcceptanceRole | false;
|
|
83
89
|
disabled?: boolean;
|
|
84
90
|
systemPrompt?: string;
|
|
85
91
|
skills?: string[] | false;
|
|
@@ -116,10 +122,16 @@ export interface AgentConfig {
|
|
|
116
122
|
inheritProjectContext: boolean;
|
|
117
123
|
inheritSkills: boolean;
|
|
118
124
|
defaultContext?: AgentDefaultContext;
|
|
125
|
+
defaultAsync?: boolean;
|
|
126
|
+
defaultTimeoutMs?: number;
|
|
127
|
+
defaultTurnBudget?: TurnBudgetConfig;
|
|
128
|
+
defaultAcceptance?: AcceptanceInput;
|
|
129
|
+
acceptanceRole?: AcceptanceRole;
|
|
119
130
|
systemPrompt: string;
|
|
120
131
|
source: AgentSource;
|
|
121
132
|
filePath: string;
|
|
122
133
|
skills?: string[];
|
|
134
|
+
skillPath?: string[];
|
|
123
135
|
extensions?: string[];
|
|
124
136
|
subagentOnlyExtensions?: string[];
|
|
125
137
|
output?: string;
|
|
@@ -301,6 +313,8 @@ function resolveSettingsPackageRoot(source: string, baseDir: string): string | u
|
|
|
301
313
|
}
|
|
302
314
|
|
|
303
315
|
function getGlobalNpmRoot(): string | null {
|
|
316
|
+
const offline = process.env.PI_OFFLINE?.toLowerCase();
|
|
317
|
+
if (offline === "1" || offline === "true" || offline === "yes") return null;
|
|
304
318
|
if (cachedGlobalNpmRoot !== null) return cachedGlobalNpmRoot;
|
|
305
319
|
try {
|
|
306
320
|
cachedGlobalNpmRoot = fs.realpathSync(execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim());
|
|
@@ -494,9 +508,11 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
494
508
|
inheritProjectContext: agent.inheritProjectContext,
|
|
495
509
|
inheritSkills: agent.inheritSkills,
|
|
496
510
|
defaultContext: agent.defaultContext,
|
|
511
|
+
acceptanceRole: agent.acceptanceRole,
|
|
497
512
|
disabled: agent.disabled,
|
|
498
513
|
systemPrompt: agent.systemPrompt,
|
|
499
514
|
skills: agent.skills ? [...agent.skills] : undefined,
|
|
515
|
+
skillPath: agent.skillPath ? [...agent.skillPath] : undefined,
|
|
500
516
|
tools: agent.tools ? [...agent.tools] : undefined,
|
|
501
517
|
mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,
|
|
502
518
|
subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,
|
|
@@ -516,6 +532,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
516
532
|
...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
|
|
517
533
|
...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),
|
|
518
534
|
...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),
|
|
535
|
+
...(override.acceptanceRole !== undefined ? { acceptanceRole: override.acceptanceRole } : {}),
|
|
519
536
|
...(override.disabled !== undefined ? { disabled: override.disabled } : {}),
|
|
520
537
|
...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
|
|
521
538
|
...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
|
|
@@ -651,6 +668,14 @@ function parseBuiltinOverrideEntry(
|
|
|
651
668
|
}
|
|
652
669
|
}
|
|
653
670
|
|
|
671
|
+
if ("acceptanceRole" in input) {
|
|
672
|
+
if (input.acceptanceRole === "read-only" || input.acceptanceRole === "writer" || input.acceptanceRole === false) {
|
|
673
|
+
override.acceptanceRole = input.acceptanceRole;
|
|
674
|
+
} else {
|
|
675
|
+
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'acceptanceRole'; expected 'read-only', 'writer', or false.`);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
654
679
|
if ("disabled" in input) {
|
|
655
680
|
if (typeof input.disabled === "boolean") {
|
|
656
681
|
override.disabled = input.disabled;
|
|
@@ -786,6 +811,7 @@ function applyBuiltinOverride(
|
|
|
786
811
|
if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
|
|
787
812
|
if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;
|
|
788
813
|
if (override.defaultContext !== undefined) next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;
|
|
814
|
+
if (override.acceptanceRole !== undefined) next.acceptanceRole = override.acceptanceRole === false ? undefined : override.acceptanceRole;
|
|
789
815
|
if (override.disabled !== undefined) next.disabled = override.disabled;
|
|
790
816
|
if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
|
|
791
817
|
if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];
|
|
@@ -867,7 +893,7 @@ function applyBuiltinOverrides(
|
|
|
867
893
|
});
|
|
868
894
|
}
|
|
869
895
|
|
|
870
|
-
function
|
|
896
|
+
export function agentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {
|
|
871
897
|
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
872
898
|
return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;
|
|
873
899
|
}
|
|
@@ -890,7 +916,7 @@ function applyCustomAgentOverride(
|
|
|
890
916
|
frontmatterFields: string[],
|
|
891
917
|
value: AgentConfig[K],
|
|
892
918
|
): void => {
|
|
893
|
-
if (
|
|
919
|
+
if (agentHasFrontmatterField(agent, ...frontmatterFields)) return;
|
|
894
920
|
mutable()[field] = value;
|
|
895
921
|
anyFilled = true;
|
|
896
922
|
};
|
|
@@ -920,6 +946,9 @@ function applyCustomAgentOverride(
|
|
|
920
946
|
if (override.defaultContext !== undefined) {
|
|
921
947
|
fill("defaultContext", ["defaultContext"], override.defaultContext === false ? undefined : override.defaultContext);
|
|
922
948
|
}
|
|
949
|
+
if (override.acceptanceRole !== undefined) {
|
|
950
|
+
fill("acceptanceRole", ["acceptanceRole"], override.acceptanceRole === false ? undefined : override.acceptanceRole);
|
|
951
|
+
}
|
|
923
952
|
if (override.disabled !== undefined && agent.disabled === undefined) {
|
|
924
953
|
mutable().disabled = override.disabled;
|
|
925
954
|
anyFilled = true;
|
|
@@ -927,7 +956,7 @@ function applyCustomAgentOverride(
|
|
|
927
956
|
if (override.skills !== undefined) {
|
|
928
957
|
fill("skills", ["skill", "skills"], override.skills === false ? undefined : [...override.skills]);
|
|
929
958
|
}
|
|
930
|
-
if (override.tools !== undefined && !
|
|
959
|
+
if (override.tools !== undefined && !agentHasFrontmatterField(agent, "tools")) {
|
|
931
960
|
const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
|
|
932
961
|
const target = mutable();
|
|
933
962
|
target.tools = tools;
|
|
@@ -950,6 +979,8 @@ function applyCustomAgentOverride(
|
|
|
950
979
|
|
|
951
980
|
if (!anyFilled || !next) return agent;
|
|
952
981
|
next.override = { ...meta, base: cloneOverrideBase(agent) };
|
|
982
|
+
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
983
|
+
if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
|
|
953
984
|
return next;
|
|
954
985
|
}
|
|
955
986
|
|
|
@@ -977,7 +1008,7 @@ function applyCustomAgentOverrides(
|
|
|
977
1008
|
|
|
978
1009
|
export function buildBuiltinOverrideConfig(
|
|
979
1010
|
base: BuiltinAgentOverrideBase,
|
|
980
|
-
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget">,
|
|
1011
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget">,
|
|
981
1012
|
): BuiltinAgentOverrideConfig | undefined {
|
|
982
1013
|
const override: BuiltinAgentOverrideConfig = {};
|
|
983
1014
|
|
|
@@ -988,6 +1019,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
988
1019
|
if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
|
|
989
1020
|
if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;
|
|
990
1021
|
if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;
|
|
1022
|
+
if (draft.acceptanceRole !== base.acceptanceRole) override.acceptanceRole = draft.acceptanceRole ?? false;
|
|
991
1023
|
if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;
|
|
992
1024
|
if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;
|
|
993
1025
|
if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;
|
|
@@ -1160,6 +1192,19 @@ function isLegacyAgentSkillPath(rootDir: string, filePath: string): boolean {
|
|
|
1160
1192
|
return parts.some((part, index) => part === ".agents" && parts[index + 1] === "skills");
|
|
1161
1193
|
}
|
|
1162
1194
|
|
|
1195
|
+
function parseAgentAcceptanceFrontmatter(raw: string | undefined, agentName: string): AcceptanceInput | undefined {
|
|
1196
|
+
if (raw === undefined || !raw.trim()) return undefined;
|
|
1197
|
+
let parsed: unknown;
|
|
1198
|
+
try {
|
|
1199
|
+
parsed = parseYaml(raw);
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
throw new Error(`Agent '${agentName}' has invalid acceptance frontmatter: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
1202
|
+
}
|
|
1203
|
+
const errors = validateAcceptanceInput(parsed, `Agent '${agentName}' acceptance frontmatter`);
|
|
1204
|
+
if (errors.length > 0) throw new Error(errors.join(" "));
|
|
1205
|
+
return parsed as AcceptanceInput;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1163
1208
|
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
1164
1209
|
const agents: AgentConfig[] = [];
|
|
1165
1210
|
|
|
@@ -1187,37 +1232,13 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
1187
1232
|
const packageName = parsedPackage.packageName;
|
|
1188
1233
|
const runtimeName = buildRuntimeName(localName, packageName);
|
|
1189
1234
|
|
|
1190
|
-
const rawTools = frontmatter.tools
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
.filter(Boolean);
|
|
1194
|
-
|
|
1195
|
-
const mcpDirectTools: string[] = [];
|
|
1196
|
-
const tools: string[] = [];
|
|
1197
|
-
if (rawTools) {
|
|
1198
|
-
for (const tool of rawTools) {
|
|
1199
|
-
if (tool.startsWith("mcp:")) {
|
|
1200
|
-
mcpDirectTools.push(tool.slice(4));
|
|
1201
|
-
} else {
|
|
1202
|
-
tools.push(tool);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
const defaultReads = frontmatter.defaultReads
|
|
1208
|
-
?.split(",")
|
|
1209
|
-
.map((f) => f.trim())
|
|
1210
|
-
.filter(Boolean);
|
|
1211
|
-
|
|
1235
|
+
const rawTools = parseFrontmatterList(frontmatter.tools);
|
|
1236
|
+
const { tools = [], mcpDirectTools = [] } = splitToolList(rawTools);
|
|
1237
|
+
const defaultReads = parseFrontmatterList(frontmatter.defaultReads);
|
|
1212
1238
|
const skillStr = frontmatter.skill || frontmatter.skills;
|
|
1213
|
-
const skills = skillStr
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
.filter(Boolean);
|
|
1217
|
-
const fallbackModels = frontmatter.fallbackModels
|
|
1218
|
-
?.split(",")
|
|
1219
|
-
.map((model) => model.trim())
|
|
1220
|
-
.filter(Boolean);
|
|
1239
|
+
const skills = parseFrontmatterList(skillStr);
|
|
1240
|
+
const skillPath = parseFrontmatterList(frontmatter.skillPath);
|
|
1241
|
+
const fallbackModels = parseFrontmatterList(frontmatter.fallbackModels);
|
|
1221
1242
|
const systemPromptMode = frontmatter.systemPromptMode === "replace"
|
|
1222
1243
|
? "replace"
|
|
1223
1244
|
: frontmatter.systemPromptMode === "append"
|
|
@@ -1238,21 +1259,36 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
1238
1259
|
: frontmatter.defaultContext === "fresh"
|
|
1239
1260
|
? "fresh" as const
|
|
1240
1261
|
: undefined;
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1262
|
+
let defaultAsync: boolean | undefined;
|
|
1263
|
+
if (frontmatter.async !== undefined) {
|
|
1264
|
+
if (frontmatter.async === "true") defaultAsync = true;
|
|
1265
|
+
else if (frontmatter.async === "false") defaultAsync = false;
|
|
1266
|
+
else throw new Error(`Agent '${localName}' has invalid async frontmatter; expected true or false.`);
|
|
1267
|
+
}
|
|
1268
|
+
let defaultTimeoutMs: number | undefined;
|
|
1269
|
+
if (frontmatter.timeoutMs !== undefined) {
|
|
1270
|
+
const parsed = Number(frontmatter.timeoutMs);
|
|
1271
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
1272
|
+
throw new Error(`Agent '${localName}' has invalid timeoutMs frontmatter; expected a positive integer.`);
|
|
1273
|
+
}
|
|
1274
|
+
defaultTimeoutMs = parsed;
|
|
1248
1275
|
}
|
|
1249
|
-
let
|
|
1250
|
-
if (frontmatter.
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1276
|
+
let defaultTurnBudget: TurnBudgetConfig | undefined;
|
|
1277
|
+
if (frontmatter.turnBudget !== undefined && frontmatter.turnBudget.trim()) {
|
|
1278
|
+
const parsed = JSON.parse(frontmatter.turnBudget) as unknown;
|
|
1279
|
+
const resolved = resolveTurnBudgetConfig(parsed, `Agent '${localName}' turnBudget frontmatter`);
|
|
1280
|
+
if (resolved.error) throw new Error(resolved.error);
|
|
1281
|
+
defaultTurnBudget = resolved.turnBudget;
|
|
1255
1282
|
}
|
|
1283
|
+
const defaultAcceptance = parseAgentAcceptanceFrontmatter(frontmatter.acceptance, localName);
|
|
1284
|
+
let acceptanceRole: AcceptanceRole | undefined;
|
|
1285
|
+
if (frontmatter.acceptanceRole !== undefined && frontmatter.acceptanceRole.trim()) {
|
|
1286
|
+
if (frontmatter.acceptanceRole === "read-only" || frontmatter.acceptanceRole === "writer") acceptanceRole = frontmatter.acceptanceRole;
|
|
1287
|
+
else throw new Error(`Agent '${localName}' has invalid acceptanceRole frontmatter; expected 'read-only' or 'writer'.`);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
const extensions = parseFrontmatterList(frontmatter.extensions);
|
|
1291
|
+
const subagentOnlyExtensions = parseFrontmatterList(frontmatter.subagentOnlyExtensions);
|
|
1256
1292
|
|
|
1257
1293
|
const extraFields: Record<string, string> = {};
|
|
1258
1294
|
for (const [key, value] of Object.entries(frontmatter)) {
|
|
@@ -1288,10 +1324,16 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
1288
1324
|
inheritProjectContext,
|
|
1289
1325
|
inheritSkills,
|
|
1290
1326
|
defaultContext,
|
|
1327
|
+
defaultAsync,
|
|
1328
|
+
defaultTimeoutMs,
|
|
1329
|
+
defaultTurnBudget,
|
|
1330
|
+
defaultAcceptance,
|
|
1331
|
+
acceptanceRole,
|
|
1291
1332
|
systemPrompt: body,
|
|
1292
1333
|
source,
|
|
1293
1334
|
filePath,
|
|
1294
1335
|
skills: skills && skills.length > 0 ? skills : undefined,
|
|
1336
|
+
skillPath: skillPath && skillPath.length > 0 ? skillPath : undefined,
|
|
1295
1337
|
extensions,
|
|
1296
1338
|
subagentOnlyExtensions,
|
|
1297
1339
|
output: frontmatter.output,
|