pi-subagents 0.40.0 → 0.41.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 +76 -0
- package/README.md +246 -525
- package/agents/oracle.md +1 -0
- package/package.json +12 -4
- package/prompts/parallel-context-build.md +1 -1
- package/prompts/parallel-handoff-plan.md +1 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/skills/pi-subagents/references/constraints-and-recipes.md +19 -26
- package/skills/pi-subagents/references/execution-controls.md +98 -97
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +18 -27
- package/src/agents/agent-management.ts +155 -65
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +154 -71
- package/src/agents/chain-serializer.ts +10 -7
- package/src/agents/frontmatter.ts +5 -3
- package/src/agents/identity.ts +1 -1
- package/src/agents/proactive-skills.ts +13 -10
- package/src/agents/skills.ts +23 -6
- package/src/api/control-channel.ts +4 -0
- package/src/api/delegation.ts +26 -194
- package/src/api/external-runs.ts +129 -0
- package/src/api/intercom-bridge.ts +3 -0
- package/src/api/pi-args.ts +5 -0
- package/src/api/preflight.ts +3 -3
- package/src/api/shared-types.ts +19 -0
- package/src/extension/config.ts +10 -0
- package/src/extension/control-notices.ts +5 -39
- package/src/extension/doctor.ts +10 -9
- package/src/extension/fanout-child.ts +7 -4
- package/src/extension/index.ts +232 -68
- package/src/extension/rpc.ts +18 -12
- package/src/extension/schemas.ts +48 -37
- package/src/extension/tool-description.ts +36 -86
- package/src/inspectors/herdr/actions.ts +229 -0
- package/src/inspectors/herdr/client.ts +130 -0
- package/src/inspectors/herdr/inspector-runner.ts +141 -0
- package/src/inspectors/herdr/project-panes.ts +154 -0
- package/src/integrations/herdr-status.ts +330 -0
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/result-intercom.ts +5 -1
- package/src/missions/actions.ts +372 -0
- package/src/missions/lifecycle.ts +314 -0
- package/src/missions/store.ts +442 -0
- package/src/missions/types.ts +135 -0
- package/src/policy/authority.ts +46 -0
- package/src/profiles/profiles.ts +29 -3
- package/src/runs/background/async-execution.ts +98 -49
- package/src/runs/background/async-job-tracker.ts +10 -2
- package/src/runs/background/async-resume.ts +6 -6
- package/src/runs/background/async-status.ts +29 -1
- package/src/runs/background/auto-drain.ts +3 -3
- package/src/runs/background/chain-append.ts +3 -2
- package/src/runs/background/control-channel.ts +9 -7
- package/src/runs/background/fleet-view.ts +3 -4
- package/src/runs/background/notify.ts +2 -1
- package/src/runs/background/process-terminal.ts +5 -5
- package/src/runs/background/result-watcher.ts +13 -5
- package/src/runs/background/run-id-resolver.ts +3 -3
- package/src/runs/background/run-status.ts +35 -8
- package/src/runs/background/scheduled-runs.ts +602 -375
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +608 -445
- package/src/runs/background/subagent-wait.ts +50 -9
- package/src/runs/background/wait-subscriptions.ts +253 -0
- package/src/runs/background/wait-tool.ts +12 -4
- package/src/runs/foreground/async-steering-action.ts +3 -3
- package/src/runs/foreground/chain-clarify.ts +8 -4
- package/src/runs/foreground/chain-execution.ts +56 -30
- package/src/runs/foreground/execution.ts +15 -2
- package/src/runs/foreground/subagent-executor.ts +1023 -273
- package/src/runs/shared/acceptance.ts +28 -6
- package/src/runs/shared/child-protocol.ts +302 -22
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/external-cli-runner.ts +130 -0
- package/src/runs/shared/long-running-guard.ts +42 -1
- package/src/runs/shared/nested-events.ts +59 -5
- package/src/runs/shared/nested-render.ts +9 -4
- package/src/runs/shared/parallel-handoff.ts +86 -2
- package/src/runs/shared/parallel-utils.ts +11 -2
- package/src/runs/shared/permissions.ts +95 -0
- package/src/runs/shared/pi-args.ts +11 -1
- package/src/runs/shared/pi-spawn.ts +11 -1
- package/src/runs/shared/run-history.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
- package/src/runs/shared/subagent-startup-retry.ts +5 -2
- package/src/runs/shared/turn-budget.ts +6 -6
- package/src/runs/shared/worktree.ts +122 -12
- package/src/shared/accessible-dir.ts +29 -7
- package/src/shared/artifacts.ts +18 -1
- package/src/shared/fork-context.ts +3 -2
- package/src/shared/launch-contract.ts +1 -0
- package/src/shared/settings.ts +10 -0
- package/src/shared/types.ts +156 -14
- package/src/shared/utils.ts +8 -6
- package/src/slash/delegation-adapters.ts +32 -194
- package/src/slash/delegation-request.ts +43 -126
- package/src/slash/prompt-template-bridge.ts +158 -205
- package/src/slash/prompt-workflows.ts +21 -57
- package/src/slash/slash-bridge.ts +14 -0
- package/src/slash/slash-commands.ts +31 -632
- package/src/slash/subagents-admin.ts +18 -14
- package/src/tui/fleet-status.ts +156 -21
- package/src/tui/fleet-transcript.ts +110 -5
- package/src/tui/fleet.ts +56 -24
- package/src/tui/render.ts +291 -109
- package/src/types/pi-runtime-compat.d.ts +14 -0
- package/src/watchdog/lsp-diagnostics.ts +12 -7
- package/src/watchdog/model-selection.ts +2 -2
- package/src/watchdog/permission-arbiter.ts +145 -0
- package/src/watchdog/register-child.ts +1 -1
- package/src/watchdog/register-main.ts +1 -1
- package/src/watchdog/review.ts +4 -1
- package/src/watchdog/runtime.ts +3 -2
- package/src/workflows/chat-progress.ts +140 -0
- package/src/workflows/scripted-workflow.ts +415 -0
- package/agents/advisor.md +0 -73
- package/src/extension/chain-validation.ts +0 -181
|
@@ -24,9 +24,7 @@ Agents can use the `subagent(...)` tool directly for execution, management, stat
|
|
|
24
24
|
Humans often use the slash-command layer instead:
|
|
25
25
|
|
|
26
26
|
- `/run` — launch a single agent
|
|
27
|
-
-
|
|
28
|
-
- `/parallel` — launch top-level parallel tasks
|
|
29
|
-
- `/run-chain` — launch a saved `.chain.md` or `.chain.json` workflow
|
|
27
|
+
- `workflowScript` — the sole public surface for sequence, parallelism, branching, retries, and aggregation
|
|
30
28
|
- `/subagents` — interactive admin for inspecting agents and editing model, thinking, or system prompt
|
|
31
29
|
- `/subagents-stop [run-id]` — stop a current-session top-level async run; opens a selector when no id is given
|
|
32
30
|
- `/subagents-detach [run-id]` — detach an active foreground single-subagent run without terminating its child
|
|
@@ -36,7 +34,7 @@ Humans often use the slash-command layer instead:
|
|
|
36
34
|
- `/subagents-doctor` — diagnose setup, discovery, async paths, and intercom bridge state
|
|
37
35
|
- `/subagents-models [agent]` — show the live runtime-loaded builtin model mapping
|
|
38
36
|
- `/subagents-profiles`, `/subagents-load-profile`, `/subagents-refresh-provider-models`, `/subagents-generate-profiles`, `/subagents-check-profile` — manage model profiles and provider catalogs
|
|
39
|
-
- `/prompt-workflow`
|
|
37
|
+
- `/prompt-workflow` — run a prompt template through native single-agent or workflowScript execution
|
|
40
38
|
|
|
41
39
|
Prefer the tool when you are writing agent logic. Prefer the slash commands when
|
|
42
40
|
you are guiding a human through an interactive flow.
|
|
@@ -94,21 +92,18 @@ Use this when the question needs both external evidence and local implications.
|
|
|
94
92
|
|
|
95
93
|
### Parallel context-build technique
|
|
96
94
|
|
|
97
|
-
Use this before planning or implementation when a stronger handoff is needed.
|
|
95
|
+
Use this before planning or implementation when a stronger handoff is needed. Use `workflowScript` with `runs.all` to launch distinct `context-builder` lanes, each with an explicit output path. Give every task a distinct output path such as `context-build/request-and-scope.md`, `context-build/codebase-and-patterns.md`, and `context-build/validation-and-risks.md`. Choose two or three builders: request/scope, codebase/patterns, and validation/risks. Each builder must read every relevant file needed to understand its slice, follow imports/callers/tests/docs/config, conduct tool-available web research when needed, and include a compact `meta-prompt` section. The parent synthesizes the outputs into important context, recommended next meta-prompt, open questions, assumptions, and artifact paths.
|
|
98
96
|
|
|
99
97
|
Example shape:
|
|
100
98
|
|
|
101
99
|
```typescript
|
|
102
|
-
subagent({
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}],
|
|
110
|
-
context: "fresh"
|
|
111
|
-
})
|
|
100
|
+
subagent({ workflowScript: `
|
|
101
|
+
const results = await runs.all([
|
|
102
|
+
{ key: "lane-a", agent: "reviewer", task: "Inspect lane A" },
|
|
103
|
+
{ key: "lane-b", agent: "reviewer", task: "Inspect lane B" }
|
|
104
|
+
]);
|
|
105
|
+
return results.map(result => result.output);
|
|
106
|
+
` })
|
|
112
107
|
```
|
|
113
108
|
|
|
114
109
|
### Parallel handoff-plan technique
|
|
@@ -118,17 +113,13 @@ Use this when the user needs a solution brief or implementation-ready handoff fr
|
|
|
118
113
|
Example shape:
|
|
119
114
|
|
|
120
115
|
```typescript
|
|
121
|
-
subagent({
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
{ agent: "context-builder", task: "Read {previous} and synthesize the final handoff plan and implementation-ready meta-prompt.", output: "handoff/final-handoff-plan.md" }
|
|
129
|
-
],
|
|
130
|
-
context: "fresh"
|
|
131
|
-
})
|
|
116
|
+
subagent({ workflowScript: `
|
|
117
|
+
const results = await runs.all([
|
|
118
|
+
{ key: "lane-a", agent: "reviewer", task: "Inspect lane A" },
|
|
119
|
+
{ key: "lane-b", agent: "reviewer", task: "Inspect lane B" }
|
|
120
|
+
]);
|
|
121
|
+
return results.map(result => result.output);
|
|
122
|
+
` })
|
|
132
123
|
```
|
|
133
124
|
|
|
134
125
|
### Gather-context-and-clarify technique
|
|
@@ -149,7 +140,7 @@ Use this when a broad diff has known reviewer findings across several items and
|
|
|
149
140
|
|
|
150
141
|
Prefer `async: true`, `context: "fresh"` for planners/validators, `outputMode: "file-only"` for large summaries, and per-stage output names that will not collide. Add `phase` and `label` to make async status readable, and use `as` plus `{outputs.name}` when a later step needs a specific earlier result instead of the whole `{previous}` blob. Use this pattern instead of launching several writer workers into a dirty worktree. Include non-blocking suggestions in the writer prompt only when they are small, safe, and do not expand product scope; otherwise record them as deferred.
|
|
151
142
|
|
|
152
|
-
When
|
|
143
|
+
When one child returns a structured target list, use ordinary JavaScript to validate/filter it and map bounded entries into `runs.all`; do not use the removed chain fanout DSL.
|
|
153
144
|
|
|
154
145
|
Example shape:
|
|
155
146
|
|
|
@@ -46,7 +46,7 @@ interface ManagementParams {
|
|
|
46
46
|
action?: string;
|
|
47
47
|
agent?: string;
|
|
48
48
|
chainName?: string;
|
|
49
|
-
agentScope?:
|
|
49
|
+
agentScope?: unknown;
|
|
50
50
|
config?: unknown;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -215,34 +215,54 @@ function skillsWarning(cwd: string, agent: Pick<AgentConfig, "skills" | "skillPa
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
218
|
+
const { extensions: _extensions, ...withoutExtensions } = agent;
|
|
218
219
|
const base = agent.override?.base;
|
|
220
|
+
const {
|
|
221
|
+
override: _override,
|
|
222
|
+
model: _model,
|
|
223
|
+
fallbackModels: _fallbackModels,
|
|
224
|
+
thinking: _thinking,
|
|
225
|
+
systemPromptMode: _systemPromptMode,
|
|
226
|
+
inheritProjectContext: _inheritProjectContext,
|
|
227
|
+
inheritSkills: _inheritSkills,
|
|
228
|
+
defaultContext: _defaultContext,
|
|
229
|
+
acceptanceRole: _acceptanceRole,
|
|
230
|
+
disabled: _disabled,
|
|
231
|
+
systemPrompt: _systemPrompt,
|
|
232
|
+
skills: _skills,
|
|
233
|
+
skillPath: _skillPath,
|
|
234
|
+
tools: _tools,
|
|
235
|
+
mcpDirectTools: _mcpDirectTools,
|
|
236
|
+
subagentOnlyExtensions: _subagentOnlyExtensions,
|
|
237
|
+
completionGuard: _completionGuard,
|
|
238
|
+
...editable
|
|
239
|
+
} = withoutExtensions;
|
|
219
240
|
if (!base) {
|
|
220
241
|
return {
|
|
221
|
-
...
|
|
222
|
-
|
|
242
|
+
...withoutExtensions,
|
|
243
|
+
...(agent.extensionsFromDefault ? {} : agent.extensions !== undefined ? { extensions: [...agent.extensions] } : {}),
|
|
223
244
|
};
|
|
224
245
|
}
|
|
225
246
|
|
|
226
247
|
return {
|
|
227
|
-
...
|
|
228
|
-
model: base.model,
|
|
229
|
-
|
|
230
|
-
thinking: base.thinking,
|
|
248
|
+
...editable,
|
|
249
|
+
...(base.model !== undefined ? { model: base.model } : {}),
|
|
250
|
+
...(base.fallbackModels !== undefined ? { fallbackModels: [...base.fallbackModels] } : {}),
|
|
251
|
+
...(base.thinking !== undefined ? { thinking: base.thinking } : {}),
|
|
231
252
|
systemPromptMode: base.systemPromptMode,
|
|
232
253
|
inheritProjectContext: base.inheritProjectContext,
|
|
233
254
|
inheritSkills: base.inheritSkills,
|
|
234
|
-
defaultContext: base.defaultContext,
|
|
235
|
-
acceptanceRole: base.acceptanceRole,
|
|
236
|
-
disabled: base.disabled,
|
|
255
|
+
...(base.defaultContext !== undefined ? { defaultContext: base.defaultContext } : {}),
|
|
256
|
+
...(base.acceptanceRole !== undefined ? { acceptanceRole: base.acceptanceRole } : {}),
|
|
257
|
+
...(base.disabled !== undefined ? { disabled: base.disabled } : {}),
|
|
237
258
|
systemPrompt: base.systemPrompt,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
completionGuard: base.completionGuard,
|
|
245
|
-
override: undefined,
|
|
259
|
+
...(base.skills !== undefined ? { skills: [...base.skills] } : {}),
|
|
260
|
+
...(base.skillPath !== undefined ? { skillPath: [...base.skillPath] } : {}),
|
|
261
|
+
...(base.tools !== undefined ? { tools: [...base.tools] } : {}),
|
|
262
|
+
...(base.mcpDirectTools !== undefined ? { mcpDirectTools: [...base.mcpDirectTools] } : {}),
|
|
263
|
+
...(base.extensions !== undefined ? { extensions: [...base.extensions] } : {}),
|
|
264
|
+
...(base.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...base.subagentOnlyExtensions] } : {}),
|
|
265
|
+
...(base.completionGuard !== undefined ? { completionGuard: base.completionGuard } : {}),
|
|
246
266
|
};
|
|
247
267
|
}
|
|
248
268
|
|
|
@@ -266,6 +286,7 @@ export function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<
|
|
|
266
286
|
if (hasKey(cfg, "description")) changed("description");
|
|
267
287
|
if (hasKey(cfg, "aliases")) changed("alias", "aliases");
|
|
268
288
|
if (hasKey(cfg, "systemPrompt")) changed("systemPrompt");
|
|
289
|
+
if (hasKey(cfg, "runner")) changed("runner");
|
|
269
290
|
if (hasKey(cfg, "model")) changed("model");
|
|
270
291
|
if (hasKey(cfg, "fallbackModels")) changed("fallbackModels");
|
|
271
292
|
if (hasKey(cfg, "tools")) changed("tools");
|
|
@@ -364,7 +385,7 @@ function parseStepList(raw: unknown): { steps?: ChainStepConfig[]; error?: strin
|
|
|
364
385
|
if (hasKey(s, "toolBudget")) {
|
|
365
386
|
const validation = validateToolBudgetConfig(s.toolBudget, `config.steps[${i}].toolBudget`);
|
|
366
387
|
if (validation.error) return { error: validation.error };
|
|
367
|
-
step.toolBudget = s.toolBudget as
|
|
388
|
+
if (s.toolBudget !== undefined) step.toolBudget = s.toolBudget as ToolBudgetConfig;
|
|
368
389
|
}
|
|
369
390
|
steps.push(step);
|
|
370
391
|
}
|
|
@@ -380,18 +401,23 @@ function parseTools(raw: string): { tools?: string[]; mcpDirectTools?: string[]
|
|
|
380
401
|
if (direct) mcpDirectTools.push(direct);
|
|
381
402
|
} else tools.push(item);
|
|
382
403
|
}
|
|
383
|
-
return {
|
|
404
|
+
return {
|
|
405
|
+
...(tools.length ? { tools } : {}),
|
|
406
|
+
...(mcpDirectTools.length ? { mcpDirectTools } : {}),
|
|
407
|
+
};
|
|
384
408
|
}
|
|
385
409
|
|
|
386
410
|
function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): string | undefined {
|
|
387
411
|
if (hasKey(cfg, "aliases")) {
|
|
388
|
-
if (cfg.aliases === false || cfg.aliases === "") target.aliases
|
|
412
|
+
if (cfg.aliases === false || cfg.aliases === "") delete target.aliases;
|
|
389
413
|
else if (typeof cfg.aliases === "string") {
|
|
390
414
|
const aliases = parseCsv(cfg.aliases).filter((alias) => alias !== target.name);
|
|
391
|
-
|
|
415
|
+
if (aliases.length) target.aliases = aliases;
|
|
416
|
+
else delete target.aliases;
|
|
392
417
|
} else if (Array.isArray(cfg.aliases) && cfg.aliases.every((entry) => typeof entry === "string")) {
|
|
393
418
|
const aliases = [...new Set(cfg.aliases.map((entry) => entry.trim()).filter(Boolean).filter((alias) => alias !== target.name))];
|
|
394
|
-
|
|
419
|
+
if (aliases.length) target.aliases = aliases;
|
|
420
|
+
else delete target.aliases;
|
|
395
421
|
} else return "config.aliases must be a comma-separated string, string array, or false when provided.";
|
|
396
422
|
}
|
|
397
423
|
if (hasKey(cfg, "systemPrompt")) {
|
|
@@ -399,58 +425,92 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
399
425
|
else if (typeof cfg.systemPrompt === "string") target.systemPrompt = cfg.systemPrompt;
|
|
400
426
|
else return "config.systemPrompt must be a string or false when provided.";
|
|
401
427
|
}
|
|
428
|
+
if (hasKey(cfg, "runner")) {
|
|
429
|
+
if (cfg.runner === false || cfg.runner === "") delete target.runner;
|
|
430
|
+
else if (cfg.runner && typeof cfg.runner === "object" && !Array.isArray(cfg.runner)) {
|
|
431
|
+
const runner = cfg.runner as Record<string, unknown>;
|
|
432
|
+
if (runner.type === "pi" && Object.keys(runner).every((key) => key === "type")) target.runner = { type: "pi" };
|
|
433
|
+
else if (runner.type === "external-cli" && typeof runner.command === "string" && runner.command.trim()
|
|
434
|
+
&& (runner.args === undefined || (Array.isArray(runner.args) && runner.args.every((arg) => typeof arg === "string")))
|
|
435
|
+
&& (runner.promptDelivery === undefined || runner.promptDelivery === "stdin")
|
|
436
|
+
&& Object.keys(runner).every((key) => ["type", "command", "args", "promptDelivery"].includes(key))) {
|
|
437
|
+
const runnerArgs = Array.isArray(runner.args) ? runner.args.filter((arg): arg is string => typeof arg === "string") : undefined;
|
|
438
|
+
target.runner = { type: "external-cli", command: runner.command.trim(), ...(runnerArgs?.length ? { args: runnerArgs } : {}), ...(runner.promptDelivery ? { promptDelivery: "stdin" } : {}) };
|
|
439
|
+
} else return "config.runner must be { type: 'pi' } or { type: 'external-cli', command: string, args?: string[], promptDelivery?: 'stdin' }.";
|
|
440
|
+
} else return "config.runner must be an object, false, or empty string when provided.";
|
|
441
|
+
}
|
|
402
442
|
if (hasKey(cfg, "model")) {
|
|
403
|
-
if (cfg.model === false || cfg.model === "") target.model
|
|
404
|
-
else if (typeof cfg.model === "string")
|
|
405
|
-
|
|
443
|
+
if (cfg.model === false || cfg.model === "") delete target.model;
|
|
444
|
+
else if (typeof cfg.model === "string") {
|
|
445
|
+
const model = cfg.model.trim();
|
|
446
|
+
if (model) target.model = model;
|
|
447
|
+
else delete target.model;
|
|
448
|
+
} else return "config.model must be a string or false when provided.";
|
|
406
449
|
}
|
|
407
450
|
if (hasKey(cfg, "fallbackModels")) {
|
|
408
|
-
if (cfg.fallbackModels === false || cfg.fallbackModels === "") target.fallbackModels
|
|
451
|
+
if (cfg.fallbackModels === false || cfg.fallbackModels === "") delete target.fallbackModels;
|
|
409
452
|
else if (typeof cfg.fallbackModels === "string") {
|
|
410
453
|
const models = parseCsv(cfg.fallbackModels);
|
|
411
|
-
target.fallbackModels = models
|
|
454
|
+
if (models.length) target.fallbackModels = models;
|
|
455
|
+
else delete target.fallbackModels;
|
|
412
456
|
} else if (Array.isArray(cfg.fallbackModels)) {
|
|
413
457
|
const models = cfg.fallbackModels
|
|
414
458
|
.filter((value): value is string => typeof value === "string")
|
|
415
459
|
.map((value) => value.trim())
|
|
416
460
|
.filter(Boolean);
|
|
417
|
-
target.fallbackModels =
|
|
461
|
+
if (models.length) target.fallbackModels = [...new Set(models)];
|
|
462
|
+
else delete target.fallbackModels;
|
|
418
463
|
} else return "config.fallbackModels must be a comma-separated string, string array, or false when provided.";
|
|
419
464
|
}
|
|
420
465
|
if (hasKey(cfg, "tools")) {
|
|
421
|
-
if (cfg.tools === false || cfg.tools === "") { target.tools
|
|
422
|
-
else if (typeof cfg.tools === "string") {
|
|
423
|
-
|
|
466
|
+
if (cfg.tools === false || cfg.tools === "") { delete target.tools; delete target.mcpDirectTools; }
|
|
467
|
+
else if (typeof cfg.tools === "string") {
|
|
468
|
+
const parsed = parseTools(cfg.tools);
|
|
469
|
+
if (parsed.tools) target.tools = parsed.tools;
|
|
470
|
+
else delete target.tools;
|
|
471
|
+
if (parsed.mcpDirectTools) target.mcpDirectTools = parsed.mcpDirectTools;
|
|
472
|
+
else delete target.mcpDirectTools;
|
|
473
|
+
} else return "config.tools must be a comma-separated string or false when provided.";
|
|
424
474
|
}
|
|
425
475
|
if (hasKey(cfg, "skills")) {
|
|
426
|
-
if (cfg.skills === false || cfg.skills === "") target.skills
|
|
427
|
-
else if (typeof cfg.skills === "string") {
|
|
428
|
-
|
|
476
|
+
if (cfg.skills === false || cfg.skills === "") delete target.skills;
|
|
477
|
+
else if (typeof cfg.skills === "string") {
|
|
478
|
+
const skills = parseCsv(cfg.skills);
|
|
479
|
+
if (skills.length) target.skills = skills;
|
|
480
|
+
else delete target.skills;
|
|
481
|
+
} else return "config.skills must be a comma-separated string or false when provided.";
|
|
429
482
|
}
|
|
430
483
|
if (hasKey(cfg, "skillPath")) {
|
|
431
|
-
if (cfg.skillPath === false || cfg.skillPath === "") target.skillPath
|
|
432
|
-
else if (typeof cfg.skillPath === "string") {
|
|
433
|
-
|
|
484
|
+
if (cfg.skillPath === false || cfg.skillPath === "") delete target.skillPath;
|
|
485
|
+
else if (typeof cfg.skillPath === "string") {
|
|
486
|
+
const skillPath = parseCsv(cfg.skillPath);
|
|
487
|
+
if (skillPath.length) target.skillPath = skillPath;
|
|
488
|
+
else delete target.skillPath;
|
|
489
|
+
} else if (Array.isArray(cfg.skillPath) && cfg.skillPath.every((entry) => typeof entry === "string")) {
|
|
434
490
|
const skillPath = [...new Set(cfg.skillPath.map((entry) => entry.trim()).filter(Boolean))];
|
|
435
|
-
|
|
491
|
+
if (skillPath.length) target.skillPath = skillPath;
|
|
492
|
+
else delete target.skillPath;
|
|
436
493
|
} else return "config.skillPath must be a comma-separated string, string array, or false when provided.";
|
|
437
494
|
}
|
|
438
495
|
if (hasKey(cfg, "extensions")) {
|
|
439
|
-
if (cfg.extensions === false) target.extensions
|
|
496
|
+
if (cfg.extensions === false) delete target.extensions;
|
|
440
497
|
else if (cfg.extensions === "") target.extensions = [];
|
|
441
498
|
else if (typeof cfg.extensions === "string") target.extensions = parseCsv(cfg.extensions);
|
|
442
499
|
else return "config.extensions must be a comma-separated string, empty string, or false when provided.";
|
|
443
500
|
}
|
|
444
501
|
if (hasKey(cfg, "subagentOnlyExtensions")) {
|
|
445
|
-
if (cfg.subagentOnlyExtensions === false) target.subagentOnlyExtensions
|
|
502
|
+
if (cfg.subagentOnlyExtensions === false) delete target.subagentOnlyExtensions;
|
|
446
503
|
else if (cfg.subagentOnlyExtensions === "") target.subagentOnlyExtensions = [];
|
|
447
504
|
else if (typeof cfg.subagentOnlyExtensions === "string") target.subagentOnlyExtensions = parseCsv(cfg.subagentOnlyExtensions);
|
|
448
505
|
else return "config.subagentOnlyExtensions must be a comma-separated string, empty string, or false when provided.";
|
|
449
506
|
}
|
|
450
507
|
if (hasKey(cfg, "thinking")) {
|
|
451
|
-
if (cfg.thinking === false || cfg.thinking === "") target.thinking
|
|
452
|
-
else if (typeof cfg.thinking === "string")
|
|
453
|
-
|
|
508
|
+
if (cfg.thinking === false || cfg.thinking === "") delete target.thinking;
|
|
509
|
+
else if (typeof cfg.thinking === "string") {
|
|
510
|
+
const thinking = cfg.thinking.trim();
|
|
511
|
+
if (thinking) target.thinking = thinking;
|
|
512
|
+
else delete target.thinking;
|
|
513
|
+
} else return "config.thinking must be a string or false when provided.";
|
|
454
514
|
}
|
|
455
515
|
if (hasKey(cfg, "systemPromptMode")) {
|
|
456
516
|
if (cfg.systemPromptMode === "append" || cfg.systemPromptMode === "replace") target.systemPromptMode = cfg.systemPromptMode;
|
|
@@ -465,30 +525,31 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
465
525
|
target.inheritSkills = cfg.inheritSkills;
|
|
466
526
|
}
|
|
467
527
|
if (hasKey(cfg, "defaultContext")) {
|
|
468
|
-
if (cfg.defaultContext === false || cfg.defaultContext === "") target.defaultContext
|
|
528
|
+
if (cfg.defaultContext === false || cfg.defaultContext === "") delete target.defaultContext;
|
|
469
529
|
else if (cfg.defaultContext === "fresh" || cfg.defaultContext === "fork") target.defaultContext = cfg.defaultContext;
|
|
470
530
|
else return "config.defaultContext must be 'fresh', 'fork', or false when provided.";
|
|
471
531
|
}
|
|
472
532
|
if (hasKey(cfg, "async")) {
|
|
473
|
-
if (cfg.async === "") target.defaultAsync
|
|
533
|
+
if (cfg.async === "") delete target.defaultAsync;
|
|
474
534
|
else if (typeof cfg.async === "boolean") target.defaultAsync = cfg.async;
|
|
475
535
|
else return "config.async must be a boolean or empty string when provided.";
|
|
476
536
|
}
|
|
477
537
|
if (hasKey(cfg, "timeoutMs")) {
|
|
478
|
-
if (cfg.timeoutMs === false || cfg.timeoutMs === "") target.defaultTimeoutMs
|
|
538
|
+
if (cfg.timeoutMs === false || cfg.timeoutMs === "") delete target.defaultTimeoutMs;
|
|
479
539
|
else if (typeof cfg.timeoutMs === "number" && Number.isInteger(cfg.timeoutMs) && cfg.timeoutMs > 0) target.defaultTimeoutMs = cfg.timeoutMs;
|
|
480
540
|
else return "config.timeoutMs must be a positive integer or false when provided.";
|
|
481
541
|
}
|
|
482
542
|
if (hasKey(cfg, "turnBudget")) {
|
|
483
|
-
if (cfg.turnBudget === false || cfg.turnBudget === "") target.defaultTurnBudget
|
|
543
|
+
if (cfg.turnBudget === false || cfg.turnBudget === "") delete target.defaultTurnBudget;
|
|
484
544
|
else {
|
|
485
545
|
const resolved = resolveTurnBudgetConfig(cfg.turnBudget, "config.turnBudget");
|
|
486
546
|
if (resolved.error) return resolved.error;
|
|
487
|
-
target.defaultTurnBudget = resolved.turnBudget;
|
|
547
|
+
if (resolved.turnBudget !== undefined) target.defaultTurnBudget = resolved.turnBudget;
|
|
548
|
+
else delete target.defaultTurnBudget;
|
|
488
549
|
}
|
|
489
550
|
}
|
|
490
551
|
if (hasKey(cfg, "acceptance")) {
|
|
491
|
-
if (cfg.acceptance === "") target.defaultAcceptance
|
|
552
|
+
if (cfg.acceptance === "") delete target.defaultAcceptance;
|
|
492
553
|
else {
|
|
493
554
|
const errors = validateAcceptanceInput(cfg.acceptance, "config.acceptance");
|
|
494
555
|
if (errors.length > 0) return errors.join(" ");
|
|
@@ -496,20 +557,21 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
496
557
|
}
|
|
497
558
|
}
|
|
498
559
|
if (hasKey(cfg, "acceptanceRole")) {
|
|
499
|
-
if (cfg.acceptanceRole === false || cfg.acceptanceRole === "") target.acceptanceRole
|
|
560
|
+
if (cfg.acceptanceRole === false || cfg.acceptanceRole === "") delete target.acceptanceRole;
|
|
500
561
|
else if (cfg.acceptanceRole === "read-only" || cfg.acceptanceRole === "writer") target.acceptanceRole = cfg.acceptanceRole;
|
|
501
562
|
else return "config.acceptanceRole must be 'read-only', 'writer', or false when provided.";
|
|
502
563
|
}
|
|
503
564
|
if (hasKey(cfg, "output")) {
|
|
504
|
-
if (cfg.output === false || cfg.output === "") target.output
|
|
565
|
+
if (cfg.output === false || cfg.output === "") delete target.output;
|
|
505
566
|
else if (typeof cfg.output === "string") target.output = cfg.output;
|
|
506
567
|
else return "config.output must be a string or false when provided.";
|
|
507
568
|
}
|
|
508
569
|
if (hasKey(cfg, "reads")) {
|
|
509
|
-
if (cfg.reads === false || cfg.reads === "") target.defaultReads
|
|
570
|
+
if (cfg.reads === false || cfg.reads === "") delete target.defaultReads;
|
|
510
571
|
else if (typeof cfg.reads === "string") {
|
|
511
572
|
const reads = parseCsv(cfg.reads);
|
|
512
|
-
target.defaultReads = reads
|
|
573
|
+
if (reads.length) target.defaultReads = reads;
|
|
574
|
+
else delete target.defaultReads;
|
|
513
575
|
} else return "config.reads must be a comma-separated string or false when provided.";
|
|
514
576
|
}
|
|
515
577
|
if (hasKey(cfg, "progress")) {
|
|
@@ -517,7 +579,7 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
517
579
|
target.defaultProgress = cfg.progress;
|
|
518
580
|
}
|
|
519
581
|
if (hasKey(cfg, "maxSubagentDepth")) {
|
|
520
|
-
if (cfg.maxSubagentDepth === false || cfg.maxSubagentDepth === "") target.maxSubagentDepth
|
|
582
|
+
if (cfg.maxSubagentDepth === false || cfg.maxSubagentDepth === "") delete target.maxSubagentDepth;
|
|
521
583
|
else if (typeof cfg.maxSubagentDepth === "number" && Number.isInteger(cfg.maxSubagentDepth) && cfg.maxSubagentDepth >= 0) {
|
|
522
584
|
target.maxSubagentDepth = cfg.maxSubagentDepth;
|
|
523
585
|
} else return "config.maxSubagentDepth must be an integer >= 0 or false when provided.";
|
|
@@ -527,13 +589,28 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
527
589
|
target.completionGuard = cfg.completionGuard;
|
|
528
590
|
}
|
|
529
591
|
if (hasKey(cfg, "toolBudget")) {
|
|
530
|
-
if (cfg.toolBudget === false || cfg.toolBudget === "") target.toolBudget
|
|
592
|
+
if (cfg.toolBudget === false || cfg.toolBudget === "") delete target.toolBudget;
|
|
531
593
|
else {
|
|
532
594
|
const validation = validateToolBudgetConfig(cfg.toolBudget, "config.toolBudget");
|
|
533
595
|
if (validation.error) return validation.error;
|
|
534
596
|
target.toolBudget = cfg.toolBudget as ToolBudgetConfig;
|
|
535
597
|
}
|
|
536
598
|
}
|
|
599
|
+
if (target.runner?.type === "external-cli") {
|
|
600
|
+
const unsupported = [
|
|
601
|
+
target.tools?.length || target.mcpDirectTools?.length ? "tools" : undefined,
|
|
602
|
+
target.model ? "model" : undefined,
|
|
603
|
+
target.fallbackModels?.length ? "fallbackModels" : undefined,
|
|
604
|
+
target.thinking ? "thinking" : undefined,
|
|
605
|
+
target.extensions?.length ? "extensions" : undefined,
|
|
606
|
+
target.subagentOnlyExtensions?.length ? "subagentOnlyExtensions" : undefined,
|
|
607
|
+
target.skills?.length || target.skillPath?.length ? "skills" : undefined,
|
|
608
|
+
target.maxSubagentDepth !== undefined ? "maxSubagentDepth" : undefined,
|
|
609
|
+
target.completionGuard !== undefined ? "completionGuard" : undefined,
|
|
610
|
+
target.toolBudget ? "toolBudget" : undefined,
|
|
611
|
+
].filter((field): field is string => Boolean(field));
|
|
612
|
+
if (unsupported.length > 0) return `config.runner type 'external-cli' does not support Pi-only fields: ${unsupported.join(", ")}.`;
|
|
613
|
+
}
|
|
537
614
|
return undefined;
|
|
538
615
|
}
|
|
539
616
|
|
|
@@ -599,6 +676,7 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
599
676
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
600
677
|
if (agent.skillPath?.length) lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
|
|
601
678
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
679
|
+
if (agent.runner) lines.push(`Runner: ${JSON.stringify(agent.runner)}`);
|
|
602
680
|
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
603
681
|
lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
|
|
604
682
|
if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
|
|
@@ -687,7 +765,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
687
765
|
const proactiveSuggestions = buildProactiveSkillSubagentRecommendationLines({
|
|
688
766
|
agents,
|
|
689
767
|
chains,
|
|
690
|
-
config: ctx.config
|
|
768
|
+
...(ctx.config?.proactiveSkillSubagents !== undefined ? { config: ctx.config.proactiveSkillSubagents } : {}),
|
|
691
769
|
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
692
770
|
});
|
|
693
771
|
const lines = [
|
|
@@ -857,7 +935,15 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
|
|
|
857
935
|
if (isChain) {
|
|
858
936
|
const parsed = parseStepList(cfg.steps);
|
|
859
937
|
if (parsed.error) return result(parsed.error, true);
|
|
860
|
-
const chain: ChainConfig = {
|
|
938
|
+
const chain: ChainConfig = {
|
|
939
|
+
name: runtimeName,
|
|
940
|
+
localName: name,
|
|
941
|
+
...(parsedPackage.packageName !== undefined ? { packageName: parsedPackage.packageName } : {}),
|
|
942
|
+
description: cfg.description.trim(),
|
|
943
|
+
source: scope,
|
|
944
|
+
filePath: targetPath,
|
|
945
|
+
steps: parsed.steps!,
|
|
946
|
+
};
|
|
861
947
|
fs.writeFileSync(targetPath, serializeChain(chain), "utf-8");
|
|
862
948
|
const missing = unknownChainAgents(ctx.cwd, chain.steps);
|
|
863
949
|
if (missing.length) warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
|
|
@@ -867,7 +953,7 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
|
|
|
867
953
|
const agent: AgentConfig = {
|
|
868
954
|
name: runtimeName,
|
|
869
955
|
localName: name,
|
|
870
|
-
packageName: parsedPackage.packageName,
|
|
956
|
+
...(parsedPackage.packageName !== undefined ? { packageName: parsedPackage.packageName } : {}),
|
|
871
957
|
description: cfg.description.trim(),
|
|
872
958
|
source: scope,
|
|
873
959
|
filePath: targetPath,
|
|
@@ -898,9 +984,10 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
898
984
|
const warnings: string[] = [];
|
|
899
985
|
if (params.agent) {
|
|
900
986
|
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
901
|
-
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd,
|
|
987
|
+
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
|
|
902
988
|
if ("content" in targetOrError) return targetOrError;
|
|
903
989
|
const target = targetOrError;
|
|
990
|
+
if (target.source !== "user" && target.source !== "project") return result(`Cannot update ${target.source} agent '${target.name}'. Eject it to user or project scope first.`, true);
|
|
904
991
|
const updated = editableAgentConfig(target);
|
|
905
992
|
const oldName = target.name;
|
|
906
993
|
if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
|
|
@@ -920,7 +1007,8 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
920
1007
|
if (applyError) return result(applyError, true);
|
|
921
1008
|
const preserveFrontmatterFields = preservedAgentFrontmatterFields(target, cfg);
|
|
922
1009
|
updated.localName = newLocalName;
|
|
923
|
-
updated.packageName = newPackageName;
|
|
1010
|
+
if (newPackageName !== undefined) updated.packageName = newPackageName;
|
|
1011
|
+
else delete updated.packageName;
|
|
924
1012
|
updated.name = buildRuntimeName(newLocalName, newPackageName);
|
|
925
1013
|
if (hasKey(cfg, "description")) updated.description = (cfg.description as string).trim();
|
|
926
1014
|
if (hasKey(cfg, "model")) {
|
|
@@ -951,9 +1039,10 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
951
1039
|
return result([headline, ...warnings].join("\n"));
|
|
952
1040
|
}
|
|
953
1041
|
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
954
|
-
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd,
|
|
1042
|
+
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
|
|
955
1043
|
if ("content" in targetOrError) return targetOrError;
|
|
956
1044
|
const target = targetOrError;
|
|
1045
|
+
if (target.source !== "user" && target.source !== "project") return result(`Cannot update ${target.source} chain '${target.name}'. Eject it to user or project scope first.`, true);
|
|
957
1046
|
const updated: ChainConfig = { ...target, steps: [...target.steps] };
|
|
958
1047
|
const oldName = target.name;
|
|
959
1048
|
if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
|
|
@@ -976,7 +1065,8 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
976
1065
|
parsedSteps = parsed.steps!;
|
|
977
1066
|
}
|
|
978
1067
|
updated.localName = newLocalName;
|
|
979
|
-
updated.packageName = newPackageName;
|
|
1068
|
+
if (newPackageName !== undefined) updated.packageName = newPackageName;
|
|
1069
|
+
else delete updated.packageName;
|
|
980
1070
|
updated.name = buildRuntimeName(newLocalName, newPackageName);
|
|
981
1071
|
if (hasKey(cfg, "description")) updated.description = (cfg.description as string).trim();
|
|
982
1072
|
if (parsedSteps) {
|
|
@@ -1002,7 +1092,7 @@ function handleDelete(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
1002
1092
|
if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
|
|
1003
1093
|
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
1004
1094
|
if (params.agent) {
|
|
1005
|
-
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd,
|
|
1095
|
+
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
|
|
1006
1096
|
if ("content" in targetOrError) return targetOrError;
|
|
1007
1097
|
const target = targetOrError;
|
|
1008
1098
|
fs.unlinkSync(target.filePath);
|
|
@@ -1011,7 +1101,7 @@ function handleDelete(params: ManagementParams, ctx: ManagementContext): AgentTo
|
|
|
1011
1101
|
if (refs.length) lines.push(`Warning: chains reference deleted agent '${target.name}': ${refs.join(", ")}.`);
|
|
1012
1102
|
return result(lines.join("\n"));
|
|
1013
1103
|
}
|
|
1014
|
-
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd,
|
|
1104
|
+
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd, scopeHint);
|
|
1015
1105
|
if ("content" in targetOrError) return targetOrError;
|
|
1016
1106
|
const target = targetOrError;
|
|
1017
1107
|
fs.unlinkSync(target.filePath);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
1
2
|
import type { AgentConfig } from "./agents.ts";
|
|
2
3
|
import { frontmatterNameForConfig } from "./identity.ts";
|
|
3
4
|
|
|
@@ -32,7 +33,10 @@ export const KNOWN_FIELDS = new Set([
|
|
|
32
33
|
"maxSubagentDepth",
|
|
33
34
|
"completionGuard",
|
|
34
35
|
"toolBudget",
|
|
36
|
+
"permission",
|
|
37
|
+
"permissions",
|
|
35
38
|
"memory",
|
|
39
|
+
"runner",
|
|
36
40
|
]);
|
|
37
41
|
|
|
38
42
|
function joinComma(values: string[] | undefined): string | undefined {
|
|
@@ -72,6 +76,14 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
|
|
|
72
76
|
if (!preservingExistingFrontmatter || preserve("inheritProjectContext")) lines.push(`inheritProjectContext: ${config.inheritProjectContext ? "true" : "false"}`);
|
|
73
77
|
if (!preservingExistingFrontmatter || preserve("inheritSkills")) lines.push(`inheritSkills: ${config.inheritSkills ? "true" : "false"}`);
|
|
74
78
|
if (config.defaultContext || preserve("defaultContext")) lines.push(`defaultContext: ${config.defaultContext ?? ""}`);
|
|
79
|
+
if (config.runner || preserve("runner")) {
|
|
80
|
+
if (config.runner) {
|
|
81
|
+
lines.push("runner:");
|
|
82
|
+
for (const line of stringifyYaml(config.runner).trimEnd().split("\n")) lines.push(` ${line}`);
|
|
83
|
+
} else {
|
|
84
|
+
lines.push("runner:");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
75
87
|
if (config.defaultAsync !== undefined || preserve("async")) lines.push(`async: ${config.defaultAsync === undefined ? "" : config.defaultAsync ? "true" : "false"}`);
|
|
76
88
|
if (config.defaultTimeoutMs !== undefined || preserve("timeoutMs")) lines.push(`timeoutMs: ${config.defaultTimeoutMs ?? ""}`);
|
|
77
89
|
if (config.defaultTurnBudget || preserve("turnBudget")) lines.push(`turnBudget: ${config.defaultTurnBudget ? JSON.stringify(config.defaultTurnBudget) : ""}`);
|
|
@@ -115,6 +127,13 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
|
|
|
115
127
|
if (config.toolBudget || preserve("toolBudget")) {
|
|
116
128
|
lines.push(`toolBudget: ${config.toolBudget ? JSON.stringify(config.toolBudget) : ""}`);
|
|
117
129
|
}
|
|
130
|
+
if (config.permissions || preserve("permission", "permissions")) {
|
|
131
|
+
const key = preserve("permission") && !preserve("permissions") ? "permission" : "permissions";
|
|
132
|
+
lines.push(`${key}:`);
|
|
133
|
+
if (config.permissions) {
|
|
134
|
+
for (const line of stringifyYaml(config.permissions).trimEnd().split("\n")) lines.push(` ${line}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
118
137
|
|
|
119
138
|
if (config.memory) {
|
|
120
139
|
lines.push("memory:");
|