pi-subagents 0.25.0 → 0.27.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 +21 -0
- package/README.md +129 -17
- package/package.json +1 -1
- package/prompts/parallel-context-build.md +3 -1
- package/prompts/parallel-handoff-plan.md +3 -1
- package/skills/pi-subagents/SKILL.md +32 -17
- package/src/agents/agent-management.ts +57 -15
- package/src/agents/agent-serializer.ts +3 -2
- package/src/agents/agents.ts +47 -16
- package/src/agents/chain-serializer.ts +120 -0
- package/src/extension/fanout-child.ts +1 -0
- package/src/extension/index.ts +1 -0
- package/src/extension/schemas.ts +138 -5
- package/src/runs/background/async-execution.ts +84 -6
- package/src/runs/background/async-status.ts +11 -1
- package/src/runs/background/run-status.ts +10 -1
- package/src/runs/background/subagent-runner.ts +600 -31
- package/src/runs/foreground/chain-execution.ts +325 -118
- package/src/runs/foreground/execution.ts +222 -10
- package/src/runs/foreground/subagent-executor.ts +67 -0
- package/src/runs/shared/acceptance-contract.ts +291 -0
- package/src/runs/shared/acceptance-evaluation.ts +221 -0
- package/src/runs/shared/acceptance-finalization.ts +161 -0
- package/src/runs/shared/acceptance-reports.ts +127 -0
- package/src/runs/shared/acceptance.ts +22 -0
- package/src/runs/shared/chain-outputs.ts +101 -0
- package/src/runs/shared/completion-guard.ts +26 -3
- package/src/runs/shared/dynamic-fanout.ts +293 -0
- package/src/runs/shared/parallel-utils.ts +31 -1
- package/src/runs/shared/pi-args.ts +11 -0
- package/src/runs/shared/structured-output.ts +77 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +53 -3
- package/src/runs/shared/workflow-graph.ts +206 -0
- package/src/shared/formatters.ts +2 -2
- package/src/shared/settings.ts +53 -4
- package/src/shared/types.ts +250 -0
- package/src/slash/slash-commands.ts +41 -3
- package/src/tui/render.ts +162 -34
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.27.0] - 2026-05-30
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Reworked public acceptance config to be object-only and evidence-driven, removing public `level`/disable shorthands. Explicit acceptance now triggers a same-session self-review/repair finalization loop, with `maxFinalizationTurns` controlling the cap.
|
|
9
|
+
- Documented goal-style acceptance guidance so `/goal`, “active goal”, and “work until evidence says done” requests map to run-scoped `acceptance` contracts.
|
|
10
|
+
- Refined acceptance finalization prompts and status output to emphasize evidence, blockers, stop rules, and finalization progress such as `completed after 1/3 turns`.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Treat explicit acceptance as the completion contract for acceptance-enabled runs, avoiding implementation completion-guard false positives when the visible output is only an `acceptance-report` or a finalization self-review turn does not need a repair edit.
|
|
14
|
+
|
|
15
|
+
## [0.26.0] - 2026-05-29
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Added first-wave acceptance gates with optional public `acceptance` config, inferred effective policies, structured child reports, provenance ledgers, checked evidence gates, explicit runtime verification commands, async/status persistence, and saved `.chain.json` validation.
|
|
19
|
+
- Added chain step metadata (`phase`, `label`), named outputs (`as` with `{outputs.name}`), workflow graph snapshots, and strict `outputSchema` structured-output contracts across foreground and async chain execution.
|
|
20
|
+
- Added dynamic chain fanout with `expand`/single-template `parallel`/`collect`, structured named-output sources, bounded item expansion, collected result outputs, async status graph persistence, and saved `.chain.json` support.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Fixed dynamic fanout acceptance blockers around real `structured_output` tool validation, malformed dynamic-like chain rejection, async dynamic failure status/details, dynamic child intercom target indexing, and saved `.chain.json` management diagnostics.
|
|
24
|
+
- Fixed acceptance-gate semantics so reviewed status requires an independent reviewer result, required criteria must be reported as satisfied, only fenced `acceptance-report` blocks satisfy attestation, malformed reports preserve parse errors, `{ level: "none", reason }` disables inferred gates, and zero-child dynamic aggregates no longer fabricate evidence.
|
|
25
|
+
|
|
5
26
|
## [0.25.0] - 2026-05-21
|
|
6
27
|
|
|
7
28
|
### Added
|
package/README.md
CHANGED
|
@@ -246,7 +246,7 @@ Skip this section until you want exact syntax.
|
|
|
246
246
|
| `/run <agent> [task]` | Run one agent; omit the task for self-contained agents |
|
|
247
247
|
| `/chain agent1 "task1" -> agent2 "task2"` | Run agents in sequence |
|
|
248
248
|
| `/parallel agent1 "task1" -> agent2 "task2"` | Run agents in parallel |
|
|
249
|
-
| `/run-chain <chainName> -- <task>` | Launch a saved `.chain.md` workflow |
|
|
249
|
+
| `/run-chain <chainName> -- <task>` | Launch a saved `.chain.md` or `.chain.json` workflow |
|
|
250
250
|
| `/subagents-doctor` | Show read-only setup diagnostics |
|
|
251
251
|
|
|
252
252
|
Commands validate agent names locally, support tab completion, and send results back into the conversation.
|
|
@@ -492,14 +492,14 @@ When `extensions` is present, it takes precedence over extension paths implied b
|
|
|
492
492
|
|
|
493
493
|
## Chain files
|
|
494
494
|
|
|
495
|
-
Chains are reusable
|
|
495
|
+
Chains are reusable workflows stored separately from agent files. Use `.chain.md` for simple sequential saved chains. Use `.chain.json` when a chain needs dynamic fanout.
|
|
496
496
|
|
|
497
497
|
| Scope | Path |
|
|
498
498
|
|-------|------|
|
|
499
|
-
| User | `~/.pi/agent/chains/**/*.chain.md` |
|
|
500
|
-
| Project | `.pi/chains/**/*.chain.md` |
|
|
499
|
+
| User | `~/.pi/agent/chains/**/*.chain.md`, `~/.pi/agent/chains/**/*.chain.json` |
|
|
500
|
+
| Project | `.pi/chains/**/*.chain.md`, `.pi/chains/**/*.chain.json` |
|
|
501
501
|
|
|
502
|
-
Nested subdirectories are discovered recursively. If user and project scopes define the same parsed runtime chain name, the project chain wins. Chains support the same optional `package` frontmatter as agents; `name: review-flow` plus `package: code-analysis` runs as `code-analysis.review-flow`.
|
|
502
|
+
Nested subdirectories are discovered recursively. If both `.chain.md` and `.chain.json` define the same parsed runtime chain name in the same scope, `.chain.json` wins. If user and project scopes define the same parsed runtime chain name, the project chain wins. Chains support the same optional `package` frontmatter as agents; `name: review-flow` plus `package: code-analysis` runs as `code-analysis.review-flow`.
|
|
503
503
|
|
|
504
504
|
Example:
|
|
505
505
|
|
|
@@ -510,23 +510,67 @@ description: Gather context then plan implementation
|
|
|
510
510
|
---
|
|
511
511
|
|
|
512
512
|
## scout
|
|
513
|
+
phase: Context
|
|
514
|
+
label: Map auth flow
|
|
515
|
+
as: context
|
|
513
516
|
output: context.md
|
|
514
517
|
|
|
515
518
|
Analyze the codebase for {task}
|
|
516
519
|
|
|
517
520
|
## planner
|
|
521
|
+
phase: Planning
|
|
522
|
+
label: Implementation plan
|
|
518
523
|
reads: context.md
|
|
519
524
|
model: anthropic/claude-sonnet-4-5:high
|
|
520
525
|
progress: true
|
|
521
526
|
|
|
522
|
-
Create an implementation plan based on {
|
|
527
|
+
Create an implementation plan based on {outputs.context}
|
|
523
528
|
```
|
|
524
529
|
|
|
525
|
-
Each `## agent-name` section is a step. Config lines such as `output`, `outputMode`, `reads`, `model`, `skills`, and `progress` go immediately after the header. A blank line separates config from task text.
|
|
530
|
+
Each `.chain.md` `## agent-name` section is a step. Config lines such as `phase`, `label`, `as`, `outputSchema`, `output`, `outputMode`, `reads`, `model`, `skills`, and `progress` go immediately after the header. A blank line separates config from task text. In saved `.chain.md` files, `outputSchema` is a path to a JSON Schema file; direct tool calls and `.chain.json` files can pass the schema object inline.
|
|
526
531
|
|
|
527
532
|
For `output`, `reads`, `skills`, and `progress`, chain behavior is three-state: omitted inherits from the agent, a value overrides, and `false` disables.
|
|
528
533
|
|
|
529
|
-
|
|
534
|
+
Use `phase` to group related work in status output, `label` for a readable step name, and `as` to store a successful step or parallel task result for later `{outputs.name}` references. Duplicate `as` names, invalid identifiers, and unknown output references fail before child execution.
|
|
535
|
+
|
|
536
|
+
Dynamic fanout is available only through direct `subagent({ chain: [...] })` JSON or saved `.chain.json` files. It expands an array from a prior structured named output, runs one child template per item, and stores the ordered collection under `collect.as`. The source must be structured output; prose is never parsed. `expand.maxItems` is required, over-limit arrays fail, nested fanout and arbitrary expressions are not supported, and `.chain.md` has no dynamic syntax in this release.
|
|
537
|
+
|
|
538
|
+
```json
|
|
539
|
+
{
|
|
540
|
+
"name": "dynamic-review",
|
|
541
|
+
"description": "Find review targets, fan out reviewers, then synthesize.",
|
|
542
|
+
"chain": [
|
|
543
|
+
{
|
|
544
|
+
"agent": "scout",
|
|
545
|
+
"task": "Return {\"items\":[{\"path\":\"...\",\"reason\":\"...\"}]} via structured_output.",
|
|
546
|
+
"as": "targets",
|
|
547
|
+
"outputSchema": { "type": "object" }
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
"expand": {
|
|
551
|
+
"from": { "output": "targets", "path": "/items" },
|
|
552
|
+
"item": "target",
|
|
553
|
+
"key": "/path",
|
|
554
|
+
"maxItems": 12
|
|
555
|
+
},
|
|
556
|
+
"parallel": {
|
|
557
|
+
"agent": "reviewer",
|
|
558
|
+
"label": "Review {target.path}",
|
|
559
|
+
"task": "Review {target.path}. Reason: {target.reason}",
|
|
560
|
+
"outputSchema": { "type": "object" }
|
|
561
|
+
},
|
|
562
|
+
"collect": { "as": "reviews" },
|
|
563
|
+
"concurrency": 4
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"agent": "worker",
|
|
567
|
+
"task": "Synthesize fixes from {outputs.reviews}"
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Create simple `.chain.md` chains by writing files directly or with the `subagent({ action: "create", config: ... })` management action. Create dynamic `.chain.json` chains by writing the JSON file directly. Run saved chains with natural language or:
|
|
530
574
|
|
|
531
575
|
```text
|
|
532
576
|
/run-chain scout-planner -- refactor authentication
|
|
@@ -541,6 +585,7 @@ Task templates support:
|
|
|
541
585
|
| `{task}` | Original task from the first step. |
|
|
542
586
|
| `{previous}` | Output from the prior step, or aggregated output from a parallel step. |
|
|
543
587
|
| `{chain_dir}` | Path to the chain artifact directory. |
|
|
588
|
+
| `{outputs.name}` | Text value from a prior step or completed parallel task with `as: "name"`. |
|
|
544
589
|
|
|
545
590
|
Parallel outputs are aggregated with clear separators before being passed to the next step:
|
|
546
591
|
|
|
@@ -634,14 +679,49 @@ These are the parameters the LLM passes when it calls the `subagent` tool. Most
|
|
|
634
679
|
|
|
635
680
|
// Chain with fan-out/fan-in
|
|
636
681
|
{ chain: [
|
|
637
|
-
{ agent: "scout", task: "Gather context" },
|
|
682
|
+
{ agent: "scout", task: "Gather context", phase: "Context", label: "Map code", as: "context" },
|
|
638
683
|
{ parallel: [
|
|
639
|
-
{ agent: "worker", task: "Implement feature A from {
|
|
640
|
-
{ agent: "worker", task: "Implement feature B from {
|
|
684
|
+
{ agent: "worker", task: "Implement feature A from {outputs.context}", label: "Feature A", as: "featureA" },
|
|
685
|
+
{ agent: "worker", task: "Implement feature B from {outputs.context}", label: "Feature B", as: "featureB" }
|
|
641
686
|
], concurrency: 2, failFast: true },
|
|
642
|
-
{ agent: "reviewer", task: "Review
|
|
687
|
+
{ agent: "reviewer", task: "Review {outputs.featureA} and {outputs.featureB}" }
|
|
643
688
|
]}
|
|
644
689
|
|
|
690
|
+
// Dynamic fanout from structured output
|
|
691
|
+
{ chain: [
|
|
692
|
+
{
|
|
693
|
+
agent: "scout",
|
|
694
|
+
task: "Return review targets as structured_output: { items: [{ path, reason }] }",
|
|
695
|
+
as: "targets",
|
|
696
|
+
outputSchema: { type: "object" }
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
expand: { from: { output: "targets", path: "/items" }, item: "target", key: "/path", maxItems: 12 },
|
|
700
|
+
parallel: { agent: "reviewer", task: "Review {target.path}. Reason: {target.reason}", outputSchema: { type: "object" } },
|
|
701
|
+
collect: { as: "reviews" },
|
|
702
|
+
concurrency: 4
|
|
703
|
+
},
|
|
704
|
+
{ agent: "worker", task: "Synthesize fixes from {outputs.reviews}" }
|
|
705
|
+
] }
|
|
706
|
+
|
|
707
|
+
// Strict structured output for reliable handoff data
|
|
708
|
+
{ chain: [
|
|
709
|
+
{
|
|
710
|
+
agent: "scout",
|
|
711
|
+
task: "Return the key files and risks for {task}",
|
|
712
|
+
as: "scan",
|
|
713
|
+
outputSchema: {
|
|
714
|
+
type: "object",
|
|
715
|
+
required: ["files", "risks"],
|
|
716
|
+
properties: {
|
|
717
|
+
files: { type: "array", items: { type: "string" } },
|
|
718
|
+
risks: { type: "array", items: { type: "string" } }
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
{ agent: "planner", task: "Plan from this scan: {outputs.scan}" }
|
|
723
|
+
] }
|
|
724
|
+
|
|
645
725
|
// Worktree isolation
|
|
646
726
|
{ tasks: [
|
|
647
727
|
{ agent: "worker", task: "Implement auth" },
|
|
@@ -711,10 +791,10 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
711
791
|
| `outputMode` | `"inline" \| "file-only"` | `inline` | Return saved output inline or as a concise saved-file reference. `file-only` requires an `output` path. |
|
|
712
792
|
| `skill` | `string \| string[] \| false` | agent default | Override skills or disable all. |
|
|
713
793
|
| `model` | string | agent default | Override model. |
|
|
714
|
-
| `tasks` | array | - | Top-level parallel tasks. Supports `agent`, `task`, `cwd`, `count`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `
|
|
794
|
+
| `tasks` | array | - | Top-level parallel tasks. Supports `agent`, `task`, `cwd`, `count`, `output`, `outputMode`, `reads`, `progress`, `skill`, `model`, and `acceptance`. |
|
|
715
795
|
| `concurrency` | number | config or `4` | Top-level parallel concurrency. |
|
|
716
796
|
| `worktree` | boolean | false | Create isolated git worktrees for parallel tasks. |
|
|
717
|
-
| `chain` | array | - | Sequential
|
|
797
|
+
| `chain` | array | - | Sequential, static parallel, and dynamic fanout chain steps. Sequential steps and parallel child tasks support `phase`, `label`, `as`, `outputSchema`, and `acceptance` in addition to the usual execution fields. Dynamic fanout uses `expand`, one child `parallel` template, and `collect`; group-level acceptance is not supported because there is no child session to finalize. |
|
|
718
798
|
| `context` | `fresh \| fork` | agent default or `fresh` | `fork` creates real branched sessions from the parent leaf. Packaged `planner`, `worker`, and `oracle` default to `fork`. |
|
|
719
799
|
| `chainDir` | string | temp chain dir | Persistent directory for chain artifacts. |
|
|
720
800
|
| `clarify` | boolean | true for chains | Show TUI preview/edit flow. |
|
|
@@ -726,12 +806,13 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
726
806
|
| `includeProgress` | boolean | false | Include full progress in result. |
|
|
727
807
|
| `share` | boolean | false | Upload session export to GitHub Gist. |
|
|
728
808
|
| `sessionDir` | string | derived | Override session log directory. |
|
|
809
|
+
| `acceptance` | object | omitted | Explicit acceptance contract. When present, the child gets a structured contract, then the runtime continues the same session for a bounded self-review/repair loop before evaluating acceptance. |
|
|
729
810
|
|
|
730
811
|
`context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created. It never silently downgrades to `fresh`. In multi-agent runs, if any requested agent has `defaultContext: fork` and the launch omits `context`, the whole invocation uses forked context; pass `context: "fresh"` when you intentionally want a fresh run.
|
|
731
812
|
|
|
732
813
|
Use `outputMode: "file-only"` when a saved output may be large and the parent only needs a pointer. The returned text is a compact reference like `Output saved to: /abs/report.md (48.2 KB, 2847 lines). Read this file if needed.` Failed runs and save errors still return normal inline output for debugging. In chains, later `{previous}` steps receive the same compact reference when the prior step used file-only mode.
|
|
733
814
|
|
|
734
|
-
Sequential and parallel chain tasks accept `agent`, `task`, `cwd`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `model`. Parallel tasks also accept `count`. Parallel step groups accept `parallel`, `concurrency`, `failFast`, and `worktree`.
|
|
815
|
+
Sequential and parallel chain tasks accept `agent`, `task`, `phase`, `label`, `as`, `outputSchema`, `cwd`, `output`, `outputMode`, `reads`, `progress`, `skill`, and `model`. Parallel tasks also accept `count`. Parallel step groups accept `parallel`, `concurrency`, `failFast`, and `worktree`. If `outputSchema` is present, the child must call `structured_output` with schema-valid JSON; prose-only completion or invalid JSON fails the step. Validated structured values are preserved on the step result, and `as` also exposes a compact text representation through `{outputs.name}`.
|
|
735
816
|
|
|
736
817
|
Status and control actions:
|
|
737
818
|
|
|
@@ -906,13 +987,44 @@ Async runs write:
|
|
|
906
987
|
|
|
907
988
|
`status.json` powers the widget and `subagent({ action: "status" })` output. `events.jsonl` contains wrapper events plus child Pi JSON events annotated with run and step metadata. Nested fanout status is stored as compact sidecar event/registry metadata and merged into parent status views and result/intercom payloads; full recursive status snapshots are not embedded in parent result files. `output-<n>.log` is a live human-readable tail. Fallback information is persisted so background runs are debuggable after completion.
|
|
908
989
|
|
|
990
|
+
## Acceptance Gates
|
|
991
|
+
|
|
992
|
+
`acceptance` is an explicit contract. Omit it for lightweight runs. Set it on single runs, top-level parallel task items, sequential chain steps, static parallel task items, and dynamic fanout child templates when the child must prove the work meets concrete criteria. Do not set it on static parallel groups or dynamic fanout aggregate groups; those groups do not own a same-session child turn.
|
|
993
|
+
|
|
994
|
+
If you are coming from Codex Goals, `acceptance` is the subagent equivalent for one delegated run. When a user says `/goal`, “goal”, “active goal”, “continue until evidence says done”, or “verify against a goal”, translate that into an acceptance contract: `criteria` are the target, `evidence` and `verify` are proof, `stopRules` are constraints, and `maxFinalizationTurns` is the bounded loop budget.
|
|
995
|
+
|
|
996
|
+
```ts
|
|
997
|
+
{
|
|
998
|
+
agent: "worker",
|
|
999
|
+
task: "Implement the fix",
|
|
1000
|
+
acceptance: {
|
|
1001
|
+
criteria: ["Patch the bug without widening scope"],
|
|
1002
|
+
evidence: ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"],
|
|
1003
|
+
verify: [{ id: "focused", command: "npm test", timeoutMs: 120000 }],
|
|
1004
|
+
maxFinalizationTurns: 3
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
When `acceptance` is present, the initial child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block. After the child’s initial completion, the runtime continues the same persisted child session with an acceptance finalization prompt. The child can repair omissions in that same session, then must return the final `acceptance-report`. Missing or malformed finalization reports reject the run when the loop limit is reached.
|
|
1010
|
+
|
|
1011
|
+
Public acceptance config is evidence-driven. There is no public `level` field and no `acceptance: "checked"` shorthand. Runtime provenance is derived from what actually happened:
|
|
1012
|
+
|
|
1013
|
+
- `attested`: the child returned a structured acceptance report.
|
|
1014
|
+
- `checked`: runtime structural checks passed, such as required criteria, required evidence, and no staged files.
|
|
1015
|
+
- `verified`: configured runtime verification commands passed. Child-reported command success does not count.
|
|
1016
|
+
- `reviewed`: an independent reviewer result is present.
|
|
1017
|
+
- `rejected`: attestation, structural checks, verification, review, or finalization failed.
|
|
1018
|
+
|
|
1019
|
+
Self-review finalization never counts as `reviewed`, and it never counts as `verified` unless configured runtime verification commands actually pass. The visible child output remains the initial answer; finalization reports and residual risks are stored in the acceptance ledger and async/status details.
|
|
1020
|
+
|
|
909
1021
|
## Live progress
|
|
910
1022
|
|
|
911
|
-
Foreground runs show compact live progress for single, chain, and parallel modes: current tool, recent output, token counts, duration, activity freshness,
|
|
1023
|
+
Foreground runs show compact live progress for single, chain, and parallel modes: current tool, recent output, token counts, duration, activity freshness, current-tool duration, and chain graph metadata when available.
|
|
912
1024
|
|
|
913
1025
|
Press `Ctrl+O` to expand the full streaming view with complete output per step.
|
|
914
1026
|
|
|
915
|
-
Sequential chains show a flow line like `done scout → running planner`. Chains with parallel steps show per-step cards instead.
|
|
1027
|
+
Sequential chains show a flow line like `done scout → running planner`. Chains with parallel steps show per-step cards instead. Chain status uses `label` and `phase` metadata when present, while falling back to agent names for older chains.
|
|
916
1028
|
|
|
917
1029
|
## Session sharing
|
|
918
1030
|
|
package/package.json
CHANGED
|
@@ -4,12 +4,14 @@ description: Parallel context builders for planning handoff
|
|
|
4
4
|
|
|
5
5
|
Launch fresh-context `context-builder` subagents in parallel to build grounded handoff context for planning or implementation.
|
|
6
6
|
|
|
7
|
-
Use the `subagent` tool in chain mode with a single parallel step, not top-level parallel tasks, so relative output files live under the temporary chain directory. Use `context: "fresh"` unless I explicitly ask for forked context. Give every parallel task a distinct `output` path, for example:
|
|
7
|
+
Use the `subagent` tool in chain mode with a single parallel step, not top-level parallel tasks, so relative output files live under the temporary chain directory. Use `context: "fresh"` unless I explicitly ask for forked context. Give every parallel task a distinct `output` path, `label`, and `as` name, for example:
|
|
8
8
|
|
|
9
9
|
- `context-build/request-and-scope.md`
|
|
10
10
|
- `context-build/codebase-and-patterns.md`
|
|
11
11
|
- `context-build/validation-and-risks.md`
|
|
12
12
|
|
|
13
|
+
Use one phase such as `phase: "Context build"` for the parallel tasks so async status is readable. A later synthesis step can reference specific outputs with `{outputs.requestScope}`, `{outputs.codebasePatterns}`, and `{outputs.validationRisks}` instead of relying only on `{previous}`.
|
|
14
|
+
|
|
13
15
|
Do not write these context artifacts into the repository unless I explicitly ask for persistent files.
|
|
14
16
|
|
|
15
17
|
Treat the slash command arguments as the primary request, target, or focus:
|
|
@@ -19,12 +19,14 @@ Use the `subagent` tool in chain mode:
|
|
|
19
19
|
|
|
20
20
|
2. Second step: a synthesis `context-builder` that reads the parallel findings and writes the final handoff plan and meta-prompt.
|
|
21
21
|
|
|
22
|
-
Use distinct output paths under the chain directory. Example outputs:
|
|
22
|
+
Use distinct output paths, `label` values, and `as` names under the chain directory. Example outputs:
|
|
23
23
|
- `handoff/external-reference.md`
|
|
24
24
|
- `handoff/local-context.md`
|
|
25
25
|
- `handoff/implementation-strategy.md`
|
|
26
26
|
- `handoff/final-handoff-plan.md`
|
|
27
27
|
|
|
28
|
+
Use phases such as `Research`, `Local context`, and `Synthesis` so async status is readable. Prefer `{outputs.externalReference}`, `{outputs.localContext}`, and `{outputs.implementationStrategy}` in the synthesis task when those specific inputs are available; keep `{previous}` only when the whole parallel fan-in summary is the desired input.
|
|
29
|
+
|
|
28
30
|
Do not write these artifacts into the repository unless I explicitly ask for persistent files.
|
|
29
31
|
|
|
30
32
|
Role guidance:
|
|
@@ -32,7 +32,7 @@ Humans often use the slash-command layer instead:
|
|
|
32
32
|
- `/run` — launch a single agent
|
|
33
33
|
- `/chain` — launch a chain of steps
|
|
34
34
|
- `/parallel` — launch top-level parallel tasks
|
|
35
|
-
- `/run-chain` — launch a saved `.chain.md` workflow
|
|
35
|
+
- `/run-chain` — launch a saved `.chain.md` or `.chain.json` workflow
|
|
36
36
|
- `/subagents-doctor` — diagnose setup, discovery, async paths, and intercom bridge state
|
|
37
37
|
|
|
38
38
|
Prefer the tool when you are writing agent logic. Prefer the slash commands when
|
|
@@ -118,7 +118,9 @@ Use this when a broad diff has known reviewer findings across several items and
|
|
|
118
118
|
2. One writer worker. It receives the planner summaries through `{previous}`, the parent’s accepted scope, stop rules, and verification contract. It is the only child allowed to edit the active worktree.
|
|
119
119
|
3. A parallel read-only validation fanout. Validators inspect the worker diff from fresh context with distinct angles, report pass/fail, remaining blockers, and missing verification.
|
|
120
120
|
|
|
121
|
-
Prefer `async: true`, `context: "fresh"` for planners/validators, `outputMode: "file-only"` for large summaries, and per-stage output names that will not collide. 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.
|
|
121
|
+
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.
|
|
122
|
+
|
|
123
|
+
When the first step can return a structured target list, prefer dynamic fanout instead of hand-authoring a static parallel group. Use `outputSchema` and `as` on the producer, then an `expand` step with `from: { output, path }`, an explicit `maxItems`, one `parallel` child template, and `collect.as`. Item templates may use `{item}` or a named item such as `{target.path}`. Do not use dynamic fanout for prose outputs, nested fanout, dynamic agent selection, reducers, `when` conditions, or arbitrary expressions; `.chain.md` does not support this syntax, so use direct JSON or a saved `.chain.json`.
|
|
122
124
|
|
|
123
125
|
Example shape:
|
|
124
126
|
|
|
@@ -128,14 +130,14 @@ subagent({
|
|
|
128
130
|
context: "fresh",
|
|
129
131
|
chain: [
|
|
130
132
|
{ parallel: [
|
|
131
|
-
{ agent: "reviewer", task: "Plan fixes for deploy docs/workflow. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/deploy.md", outputMode: "file-only" },
|
|
132
|
-
{ agent: "reviewer", task: "Plan fixes for scheduler contract. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/scheduler.md", outputMode: "file-only" },
|
|
133
|
-
{ agent: "reviewer", task: "Plan fixes for sandbox/security. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/sandbox.md", outputMode: "file-only" }
|
|
133
|
+
{ agent: "reviewer", phase: "Planning", label: "Deploy docs", as: "deployPlan", task: "Plan fixes for deploy docs/workflow. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/deploy.md", outputMode: "file-only" },
|
|
134
|
+
{ agent: "reviewer", phase: "Planning", label: "Scheduler contract", as: "schedulerPlan", task: "Plan fixes for scheduler contract. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/scheduler.md", outputMode: "file-only" },
|
|
135
|
+
{ agent: "reviewer", phase: "Planning", label: "Sandbox/security", as: "sandboxPlan", task: "Plan fixes for sandbox/security. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/sandbox.md", outputMode: "file-only" }
|
|
134
136
|
], concurrency: 3 },
|
|
135
|
-
{ agent: "worker", task: "Apply only the accepted fixes from these planning summaries. You are the sole writer for the active worktree. Run focused validation and report changed files, commands, failures, and remaining issues.\n\
|
|
137
|
+
{ agent: "worker", phase: "Implementation", label: "Apply accepted fixes", as: "workerResult", task: "Apply only the accepted fixes from these planning summaries. You are the sole writer for the active worktree. Run focused validation and report changed files, commands, failures, and remaining issues.\n\nDeploy plan:\n{outputs.deployPlan}\n\nScheduler plan:\n{outputs.schedulerPlan}\n\nSandbox plan:\n{outputs.sandboxPlan}", output: "worker/fixes.md", outputMode: "file-only", progress: true },
|
|
136
138
|
{ parallel: [
|
|
137
|
-
{ agent: "reviewer", task: "Validate the post-worker diff for deploy and scheduler fixes. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/deploy-scheduler.md", outputMode: "file-only" },
|
|
138
|
-
{ agent: "reviewer", task: "Validate the post-worker diff for sandbox/security fixes. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/sandbox.md", outputMode: "file-only" }
|
|
139
|
+
{ agent: "reviewer", phase: "Validation", label: "Deploy/scheduler validation", task: "Validate the post-worker diff for deploy and scheduler fixes. Start from the worker result: {outputs.workerResult}. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/deploy-scheduler.md", outputMode: "file-only" },
|
|
140
|
+
{ agent: "reviewer", phase: "Validation", label: "Sandbox validation", task: "Validate the post-worker diff for sandbox/security fixes. Start from the worker result: {outputs.workerResult}. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/sandbox.md", outputMode: "file-only" }
|
|
139
141
|
], concurrency: 2 }
|
|
140
142
|
]
|
|
141
143
|
})
|
|
@@ -217,10 +219,10 @@ Agent files can live in:
|
|
|
217
219
|
- legacy `.agents/**/*.md` — still read for compatibility, but `.pi/agents/` wins on conflicts
|
|
218
220
|
|
|
219
221
|
Chains live in:
|
|
220
|
-
- `~/.pi/agent/chains/**/*.chain.md` — user scope
|
|
221
|
-
- `.pi/chains/**/*.chain.md` — project scope
|
|
222
|
+
- `~/.pi/agent/chains/**/*.chain.md` and `~/.pi/agent/chains/**/*.chain.json` — user scope
|
|
223
|
+
- `.pi/chains/**/*.chain.md` and `.pi/chains/**/*.chain.json` — project scope
|
|
222
224
|
|
|
223
|
-
Discovery is recursive. `.chain.md` files do not define agents. Agents and chains can set optional frontmatter
|
|
225
|
+
Discovery is recursive. `.chain.md` files do not define agents. Use `.chain.md` for simple saved chains and `.chain.json` for dynamic fanout or inline schema objects. Agents and chains can set optional frontmatter/package metadata; `name: scout` plus `package: code-analysis` registers as runtime name `code-analysis.scout` while serialization keeps `name` and `package` separate.
|
|
224
226
|
|
|
225
227
|
Precedence is by parsed runtime name:
|
|
226
228
|
1. project scope
|
|
@@ -290,9 +292,13 @@ subagent({
|
|
|
290
292
|
})
|
|
291
293
|
```
|
|
292
294
|
|
|
293
|
-
Chain steps can use templated variables such as `{task}`, `{previous}`,
|
|
294
|
-
`{chain_dir}
|
|
295
|
-
|
|
295
|
+
Chain steps can use templated variables such as `{task}`, `{previous}`,
|
|
296
|
+
`{chain_dir}`, and `{outputs.name}`. Use `as: "name"` on a successful step or
|
|
297
|
+
parallel task to make that output available to later steps. Prefer named outputs
|
|
298
|
+
when a later step needs one specific result; keep `{previous}` for simple linear
|
|
299
|
+
handoffs or full fan-in summaries. Use `phase` and `label` for status readability.
|
|
300
|
+
Use `outputSchema` when later steps need reliable structured data; the child must
|
|
301
|
+
call `structured_output` with schema-valid JSON, or the step fails.
|
|
296
302
|
|
|
297
303
|
### Async/background
|
|
298
304
|
|
|
@@ -684,7 +690,11 @@ For feature work, use this sequence as scaffolding for parent-agent behavior:
|
|
|
684
690
|
clarify → validation contract → planner → async worker → parallel async fresh-context reviewers/validators → async fix worker → follow-up review when warranted → parent review
|
|
685
691
|
```
|
|
686
692
|
|
|
687
|
-
The validation contract defines
|
|
693
|
+
The validation contract defines acceptance before code is written: expected behavior, acceptance checks, commands or user flows to exercise, and evidence the worker should return. Keep it lightweight for small tasks, but make it explicit enough that reviewers and validators are checking the intended outcome rather than the worker’s own assumptions.
|
|
694
|
+
|
|
695
|
+
Use the structured `acceptance` field when the run should carry an explicit acceptance contract. If omitted, the run stays lightweight. When present, acceptance is object-only: define concrete `criteria`, required `evidence`, optional runtime `verify` commands, optional independent `review`, and optionally `maxFinalizationTurns`. The runtime continues the same child session for a bounded self-review/repair loop before evaluating the final report, so set `acceptance` on single runs, sequential chain steps, parallel task items, and dynamic fanout child templates, not on static parallel or dynamic fanout groups. Do not call a run reviewed just because the worker says it is done; reviewed means a reviewer gate returned a result. Child-reported command success is evidence, not runtime verification.
|
|
696
|
+
|
|
697
|
+
Goal-style requests map to `acceptance`. If the user says `/goal`, “goal”, “active goal”, “continue until evidence says done”, or “verify against a goal” for a subagent run, create an explicit run-scoped acceptance contract: `criteria` for the target, `evidence` and `verify` for proof, `stopRules` for constraints, and `maxFinalizationTurns` for the bounded loop budget.
|
|
688
698
|
|
|
689
699
|
The first `worker` implements the approved plan. The parent continues with independent inspection or validation prep while it runs, not parallel edits to the same worktree. When the async worker completes, treat its handoff as the transition into review, not as final completion, unless the user explicitly asked for worker-only work, review-only output, or to stop after implementation. Parallel reviewers inspect the resulting diff from fresh context. Validators check behavior with the best available evidence: commands, tests, browser/CLI interaction, screenshots, logs, or manual reproduction notes. The final `worker` applies synthesized review fixes in forked context, then the parent looks over the final diff before completing. The parent may launch these steps as an initial async chain when the workflow is already clear, or as follow-up subagent runs after each async completion. Initial chains should pass `async: true` so the main chat is unblocked; avoid `clarify: true` unless the user asked for foreground clarification. Do not stop after parallel review unless the user explicitly asked for review-only output or the review surfaced a decision that needs approval first.
|
|
690
700
|
|
|
@@ -697,7 +707,7 @@ For very large work, split into serial milestones instead of launching a swarm o
|
|
|
697
707
|
Keep orchestration authority in the parent session. Child subagents should not launch more subagents, read this skill, or run their own orchestration loops unless the parent intentionally selected a fanout agent whose builtin `tools` includes `subagent`. Spawned subagents do not receive the `pi-subagents` skill, parent-only status/control/slash messages, or prior parent `subagent` tool-call/tool-result artifacts. Ordinary children also do not receive the `subagent` extension tool. Child context filtering strips old hidden orchestration-instruction messages when they appear in inherited history. Every child receives a boundary instruction: ordinary children are told the parent owns orchestration and they must not propose or run subagents; explicit fanout children are told to use `subagent` only for the assigned fanout work, with `maxSubagentDepth` still enforced. Implementation children must call real edit/write tools instead of printing pseudo tool calls. Pass children concrete role-specific work instead.
|
|
698
708
|
|
|
699
709
|
1. Clarify first. This is mandatory. Gather code context with `scout` or `context-builder`, add `researcher` only when external evidence matters, then ask the user clarifying questions with `interview` until scope, acceptance criteria, constraints, and non-goals are clear.
|
|
700
|
-
2. Define the validation contract. State
|
|
710
|
+
2. Define the validation contract. State acceptance before implementation: expected behavior, checks to run, user flows to exercise, and evidence required in the worker handoff. For UI, CLI, integration, or workflow changes, include at least one validator angle that uses the product the way a user would rather than only reading code.
|
|
701
711
|
3. Plan when useful. For complex work, call `planner` or write a plan doc yourself and get approval before implementation. For simple work, confirm shared understanding and explicitly note why planning is skipped.
|
|
702
712
|
4. Implement with one writer. After approval, launch `worker` asynchronously with a proper meta prompt that includes clarified requirements, relevant context, plan path or summary, the validation contract, and output expectations. Packaged `worker` defaults to forked context; pass `context: "fresh"` only when you intentionally want a fresh child. While it runs, prepare validation or inspect adjacent code instead of editing the same worktree.
|
|
703
713
|
5. Require a useful worker handoff. Ask the worker to report changed files, what was implemented, what was left undone, commands run with exit codes, validation evidence, surprises or new risks, decisions made inside approved scope, and decisions needing parent approval.
|
|
@@ -712,6 +722,11 @@ Example implementation handoff after clarification and optional planning:
|
|
|
712
722
|
subagent({
|
|
713
723
|
agent: "worker",
|
|
714
724
|
task: "Implement the approved feature.\n\nClarified requirements:\n- ...\n\nPlan: see ~/Documents/docs/...-plan.md\n\nValidation contract:\n- ...\n\nReturn a handoff with changed files, what was implemented, what was left undone, commands run with exit codes, validation evidence, surprises/new risks, and decisions needing parent approval.",
|
|
725
|
+
acceptance: {
|
|
726
|
+
criteria: ["Implement the approved feature without widening scope"],
|
|
727
|
+
evidence: ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"],
|
|
728
|
+
maxFinalizationTurns: 3
|
|
729
|
+
},
|
|
715
730
|
async: true
|
|
716
731
|
})
|
|
717
732
|
```
|
|
@@ -766,7 +781,7 @@ subagent({
|
|
|
766
781
|
/run-chain review-chain -- review this branch
|
|
767
782
|
```
|
|
768
783
|
|
|
769
|
-
Use saved `.chain.md` workflows when the user wants a repeatable multi-agent flow without rewriting the chain each time.
|
|
784
|
+
Use saved `.chain.md` or `.chain.json` workflows when the user wants a repeatable multi-agent flow without rewriting the chain each time. Prefer `.chain.json` for dynamic fanout or inline `outputSchema` objects; `.chain.md` remains the simple sequential/static authoring format.
|
|
770
785
|
|
|
771
786
|
## Error Handling
|
|
772
787
|
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
parsePackageName,
|
|
18
18
|
} from "./agents.ts";
|
|
19
19
|
import { serializeAgent } from "./agent-serializer.ts";
|
|
20
|
-
import { serializeChain } from "./chain-serializer.ts";
|
|
20
|
+
import { serializeChain, serializeJsonChain } from "./chain-serializer.ts";
|
|
21
21
|
import { discoverAvailableSkills } from "./skills.ts";
|
|
22
22
|
import type { Details } from "../shared/types.ts";
|
|
23
23
|
|
|
@@ -169,6 +169,22 @@ function parseStepList(raw: unknown): { steps?: ChainStepConfig[]; error?: strin
|
|
|
169
169
|
const s = item as Record<string, unknown>;
|
|
170
170
|
if (typeof s.agent !== "string" || !s.agent.trim()) return { error: `config.steps[${i}].agent must be a non-empty string.` };
|
|
171
171
|
const step: ChainStepConfig = { agent: s.agent.trim(), task: typeof s.task === "string" ? s.task : "" };
|
|
172
|
+
if (hasKey(s, "phase")) {
|
|
173
|
+
if (typeof s.phase === "string") step.phase = s.phase;
|
|
174
|
+
else return { error: `config.steps[${i}].phase must be a string.` };
|
|
175
|
+
}
|
|
176
|
+
if (hasKey(s, "label")) {
|
|
177
|
+
if (typeof s.label === "string") step.label = s.label;
|
|
178
|
+
else return { error: `config.steps[${i}].label must be a string.` };
|
|
179
|
+
}
|
|
180
|
+
if (hasKey(s, "as")) {
|
|
181
|
+
if (typeof s.as === "string") step.as = s.as;
|
|
182
|
+
else return { error: `config.steps[${i}].as must be a string.` };
|
|
183
|
+
}
|
|
184
|
+
if (hasKey(s, "outputSchema")) {
|
|
185
|
+
if (typeof s.outputSchema === "string") step.outputSchema = s.outputSchema;
|
|
186
|
+
else return { error: `config.steps[${i}].outputSchema must be a schema file path string for saved chains.` };
|
|
187
|
+
}
|
|
172
188
|
if (hasKey(s, "output")) {
|
|
173
189
|
if (s.output === false) step.output = false;
|
|
174
190
|
else if (typeof s.output === "string") step.output = s.output;
|
|
@@ -339,7 +355,7 @@ function renamePath(
|
|
|
339
355
|
cwd: string,
|
|
340
356
|
): { filePath?: string; error?: string } {
|
|
341
357
|
if (nameExistsInScope(cwd, scope, newName, currentPath)) return { error: `Name '${newName}' already exists in ${scope} scope.` };
|
|
342
|
-
const ext = kind === "agent" ? ".md" : ".chain.md";
|
|
358
|
+
const ext = kind === "agent" ? ".md" : currentPath.endsWith(".chain.json") ? ".chain.json" : ".chain.md";
|
|
343
359
|
const filePath = path.join(path.dirname(currentPath), `${newName}${ext}`);
|
|
344
360
|
if (fs.existsSync(filePath) && filePath !== currentPath) {
|
|
345
361
|
return { error: `File already exists at ${filePath} but is not a valid ${kind} definition. Remove or rename it first.` };
|
|
@@ -375,6 +391,41 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
375
391
|
return lines.join("\n");
|
|
376
392
|
}
|
|
377
393
|
|
|
394
|
+
function formatChainStepDetail(step: ChainStepConfig, index: number): string[] {
|
|
395
|
+
const lines: string[] = [];
|
|
396
|
+
if (step.expand || step.collect) {
|
|
397
|
+
const parallel = step.parallel && !Array.isArray(step.parallel) && typeof step.parallel === "object" ? step.parallel as { agent?: unknown; task?: unknown; label?: unknown; outputSchema?: unknown } : undefined;
|
|
398
|
+
const expand = step.expand && typeof step.expand === "object" ? step.expand as { from?: { output?: unknown; path?: unknown }; item?: unknown; key?: unknown; maxItems?: unknown; onEmpty?: unknown } : undefined;
|
|
399
|
+
const collect = step.collect && typeof step.collect === "object" ? step.collect as { as?: unknown; outputSchema?: unknown } : undefined;
|
|
400
|
+
lines.push(`${index + 1}. Dynamic fanout${typeof collect?.as === "string" ? ` -> ${collect.as}` : ""}`);
|
|
401
|
+
if (expand?.from) lines.push(` Expand: ${String(expand.from.output ?? "?")}${String(expand.from.path ?? "")}`);
|
|
402
|
+
if (typeof expand?.item === "string") lines.push(` Item variable: ${expand.item}`);
|
|
403
|
+
if (typeof expand?.key === "string") lines.push(` Key: ${expand.key}`);
|
|
404
|
+
if (typeof expand?.maxItems === "number") lines.push(` Max items: ${expand.maxItems}`);
|
|
405
|
+
if (typeof expand?.onEmpty === "string") lines.push(` On empty: ${expand.onEmpty}`);
|
|
406
|
+
if (parallel?.agent) lines.push(` Agent: ${String(parallel.agent)}`);
|
|
407
|
+
if (typeof parallel?.label === "string") lines.push(` Label: ${parallel.label}`);
|
|
408
|
+
if (typeof parallel?.task === "string" && parallel.task.trim()) lines.push(` Task: ${parallel.task}`);
|
|
409
|
+
if (parallel?.outputSchema) lines.push(" Structured output: true");
|
|
410
|
+
if (collect?.outputSchema) lines.push(" Collect schema: true");
|
|
411
|
+
if (step.concurrency !== undefined) lines.push(` Concurrency: ${step.concurrency}`);
|
|
412
|
+
if (step.failFast !== undefined) lines.push(` Fail fast: ${step.failFast ? "true" : "false"}`);
|
|
413
|
+
return lines;
|
|
414
|
+
}
|
|
415
|
+
lines.push(`${index + 1}. ${step.agent}`);
|
|
416
|
+
if (step.task?.trim()) lines.push(` Task: ${step.task}`);
|
|
417
|
+
if (step.output === false) lines.push(" Output: false");
|
|
418
|
+
else if (step.output) lines.push(` Output: ${step.output}`);
|
|
419
|
+
if (step.outputMode) lines.push(` Output mode: ${step.outputMode}`);
|
|
420
|
+
if (step.reads === false) lines.push(" Reads: false");
|
|
421
|
+
else if (Array.isArray(step.reads) && step.reads.length > 0) lines.push(` Reads: ${step.reads.join(", ")}`);
|
|
422
|
+
if (step.model) lines.push(` Model: ${step.model}`);
|
|
423
|
+
if (step.skills === false) lines.push(" Skills: false");
|
|
424
|
+
else if (Array.isArray(step.skills) && step.skills.length > 0) lines.push(` Skills: ${step.skills.join(", ")}`);
|
|
425
|
+
if (step.progress !== undefined) lines.push(` Progress: ${step.progress ? "true" : "false"}`);
|
|
426
|
+
return lines;
|
|
427
|
+
}
|
|
428
|
+
|
|
378
429
|
function formatChainDetail(chain: ChainConfig): string {
|
|
379
430
|
const lines: string[] = [`Chain: ${chain.name} (${chain.source})`, `Path: ${chain.filePath}`, `Description: ${chain.description}`];
|
|
380
431
|
if (chain.packageName) {
|
|
@@ -383,18 +434,7 @@ function formatChainDetail(chain: ChainConfig): string {
|
|
|
383
434
|
}
|
|
384
435
|
lines.push("", "Steps:");
|
|
385
436
|
for (let i = 0; i < chain.steps.length; i++) {
|
|
386
|
-
|
|
387
|
-
lines.push(`${i + 1}. ${s.agent}`);
|
|
388
|
-
if (s.task.trim()) lines.push(` Task: ${s.task}`);
|
|
389
|
-
if (s.output === false) lines.push(" Output: false");
|
|
390
|
-
else if (s.output) lines.push(` Output: ${s.output}`);
|
|
391
|
-
if (s.outputMode) lines.push(` Output mode: ${s.outputMode}`);
|
|
392
|
-
if (s.reads === false) lines.push(" Reads: false");
|
|
393
|
-
else if (Array.isArray(s.reads) && s.reads.length > 0) lines.push(` Reads: ${s.reads.join(", ")}`);
|
|
394
|
-
if (s.model) lines.push(` Model: ${s.model}`);
|
|
395
|
-
if (s.skills === false) lines.push(" Skills: false");
|
|
396
|
-
else if (Array.isArray(s.skills) && s.skills.length > 0) lines.push(` Skills: ${s.skills.join(", ")}`);
|
|
397
|
-
if (s.progress !== undefined) lines.push(` Progress: ${s.progress ? "true" : "false"}`);
|
|
437
|
+
lines.push(...formatChainStepDetail(chain.steps[i]!, i));
|
|
398
438
|
}
|
|
399
439
|
return lines.join("\n");
|
|
400
440
|
}
|
|
@@ -405,6 +445,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
405
445
|
const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
406
446
|
const agents = scopedAgents.filter((a) => !a.disabled);
|
|
407
447
|
const chains = d.chains.filter((c) => scope === "both" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
448
|
+
const diagnostics = d.chainDiagnostics.filter((entry) => scope === "both" || entry.source === scope);
|
|
408
449
|
const lines = [
|
|
409
450
|
"Executable agents:",
|
|
410
451
|
...(agents.length
|
|
@@ -413,6 +454,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
413
454
|
"",
|
|
414
455
|
"Chains:",
|
|
415
456
|
...(chains.length ? chains.map((c) => `- ${c.name} (${c.source}): ${c.description}`) : ["- (none)"]),
|
|
457
|
+
...(diagnostics.length ? ["", "Chain diagnostics:", ...diagnostics.map((entry) => `- ${entry.filePath}: ${entry.error}`)] : []),
|
|
416
458
|
];
|
|
417
459
|
return result(lines.join("\n"));
|
|
418
460
|
}
|
|
@@ -608,7 +650,7 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
|
|
|
608
650
|
if (renamed.error) return result(renamed.error, true);
|
|
609
651
|
updated.filePath = renamed.filePath!;
|
|
610
652
|
}
|
|
611
|
-
fs.writeFileSync(updated.filePath, serializeChain(updated), "utf-8");
|
|
653
|
+
fs.writeFileSync(updated.filePath, updated.filePath.endsWith(".chain.json") ? serializeJsonChain(updated) : serializeChain(updated), "utf-8");
|
|
612
654
|
const headline = updated.name === oldName
|
|
613
655
|
? `Updated chain '${updated.name}' at ${updated.filePath}.`
|
|
614
656
|
: `Updated chain '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
|
|
@@ -67,8 +67,9 @@ export function serializeAgent(config: AgentConfig): string {
|
|
|
67
67
|
|
|
68
68
|
if (config.defaultProgress) lines.push("defaultProgress: true");
|
|
69
69
|
if (config.interactive) lines.push("interactive: true");
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const maxSubagentDepth = config.maxSubagentDepth;
|
|
71
|
+
if (typeof maxSubagentDepth === "number" && Number.isInteger(maxSubagentDepth) && maxSubagentDepth >= 0) {
|
|
72
|
+
lines.push(`maxSubagentDepth: ${maxSubagentDepth}`);
|
|
72
73
|
}
|
|
73
74
|
if (config.completionGuard === false) lines.push("completionGuard: false");
|
|
74
75
|
|