@d3ara1n/pi-subagent 2.2.0 → 3.1.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/README.md +45 -14
- package/package.json +1 -1
- package/src/config.test.ts +19 -1
- package/src/config.ts +13 -0
- package/src/history.ts +3 -0
- package/src/index.ts +93 -110
- package/src/inheritance.test.ts +217 -0
- package/src/inheritance.ts +188 -0
- package/src/reminder.test.ts +101 -52
- package/src/reminder.ts +15 -9
- package/src/render-async.ts +70 -2
- package/src/render.test.ts +63 -0
- package/src/render.ts +25 -3
- package/src/run.test.ts +51 -1
- package/src/run.ts +28 -6
- package/src/spawn.test.ts +34 -10
- package/src/spawn.ts +44 -10
- package/src/types.ts +28 -9
- package/src/utils.test.ts +51 -0
- package/src/utils.ts +48 -5
- package/src/view.ts +15 -3
package/README.md
CHANGED
|
@@ -8,22 +8,30 @@ Provides a `subagent_delegate` tool that lets the main model offload tasks to sp
|
|
|
8
8
|
|
|
9
9
|
**The main model is the decision maker; subagents are executors.**
|
|
10
10
|
|
|
11
|
-
Your primary AI has the most complete context — it knows the full conversation history, project structure, and task at hand. Subagents are
|
|
11
|
+
Your primary AI has the most complete context — it knows the full conversation history, project structure, and task at hand. Subagents are isolated by default to handle specific, well-defined tasks without polluting the main model's context window. A caller can explicitly opt into a filtered, text-only snapshot of the active parent branch when a task genuinely depends on prior dialogue.
|
|
12
12
|
|
|
13
13
|
This means:
|
|
14
14
|
- **Subagents don't plan** — the main model decides what needs to be done and provides a clear task description
|
|
15
15
|
- **Subagents don't orchestrate the overall plan** — the main model decides what to do and examines each result to pick the next move; nested delegation (worker → explorer) only offloads self-contained exploration/research inside one task
|
|
16
|
-
- **Subagents
|
|
16
|
+
- **Subagents are isolated by default** — give them a precise, self-contained task; use `inheritConversation` only when prior dialogue is necessary
|
|
17
17
|
- **Multiple subagents can run in parallel** — emit multiple `subagent_delegate` calls in one turn; pi executes them concurrently
|
|
18
18
|
- **Subagents can nest subagents** — a `worker` can delegate exploration to `explorer` without returning to the main model
|
|
19
19
|
|
|
20
20
|
> This design currently focuses on single-task delegation rather than chain pipelines or context-forking — those patterns fit better when subagents act as advisors (planner, oracle) rather than executors.
|
|
21
21
|
|
|
22
|
+
## Model Compatibility
|
|
23
|
+
|
|
24
|
+
Observations on how main models behave with this plugin, one family per subsection. Subagent role models are configured separately via [pi-model-roles](../pi-model-roles); the notes below concern the **main model** — the orchestrator that decides when to delegate.
|
|
25
|
+
|
|
26
|
+
### GPT family
|
|
27
|
+
|
|
28
|
+
**Not recommended.** As of GPT 5.6, GPT models delegate pathologically: they abandon built-in tools (`read`, `edit`, `write`) and MCP entirely and route everything through subagents — `explorer` to read files, `worker` to modify them, `reviewer` to verify each change, then worker → reviewer → worker correction loops over and over. Every task a direct handful of tool calls would finish turns into a long delegate chain, wasting large amounts of time and tokens. Use a main model that treats direct tool calls as the default and delegation as the exception.
|
|
29
|
+
|
|
22
30
|
## How it works
|
|
23
31
|
|
|
24
32
|
1. Main model calls the `subagent_delegate` tool with a role and task description
|
|
25
33
|
2. The extension resolves the role to a model via pi-model-roles
|
|
26
|
-
3. Spawns
|
|
34
|
+
3. Spawns a pi child process in RPC mode (`--mode rpc`, always without a reused parent session) with the configured model, tools, and system prompt. It is isolated by default; `inheritConversation: true` adds a filtered parent-branch snapshot to its initial stdin prompt. Agent events stream back over stdout while stdin carries the initial prompt and mid-run steering commands. Children are headless: interactive extension dialogs (`ctx.ui.select/confirm/input`) are answered automatically with `cancelled` (standard "user declined" semantics), so an extension that asks never hangs the run
|
|
27
35
|
4. **Real-time TUI progress** shows tool calls, turns, and elapsed time as the subagent runs
|
|
28
36
|
5. After completion, an **AI-generated one-line summary** is produced for compact display
|
|
29
37
|
6. Returns the result to the main model with usage statistics (turns, tokens, cost)
|
|
@@ -45,7 +53,7 @@ This means:
|
|
|
45
53
|
|
|
46
54
|
## TUI Display
|
|
47
55
|
|
|
48
|
-
- **During execution**: the task's first line with a ⏳ (or ⏸ queued) indicator, a live stream of thinking blocks and tool calls (latest 5 collapsed, everything expanded), and a usage line (elapsed/budget time, turns, tokens, peak context, cost, model)
|
|
56
|
+
- **During execution**: the task's first line with a ⏳ (or ⏸ queued) indicator, a live stream of thinking blocks and tool calls (latest 5 collapsed, everything expanded), and a usage line (elapsed/budget time, turns, tokens, peak context, cost, model). Delegates using inherited conversation are marked in the tool-call title.
|
|
49
57
|
- **Collapsed result**: the task's first line, then `✓` + the AI-generated summary (or the first line of the output), then the usage line — no activity replay
|
|
50
58
|
- **Expanded result** (Ctrl+O): reference files, context size, the full task, the complete activity stream, the final output as rendered Markdown, and usage details
|
|
51
59
|
- **Fallback trace**: when a provider error (429, quota, timeout, ...) kills a run and it is retried on the role's `fallbackRole`, a `⚠ fallback: first attempt <model> failed (<reason>)` line appears in both views — also while the retry is running (see [Fallback observability](#fallback-observability))
|
|
@@ -56,7 +64,7 @@ This means:
|
|
|
56
64
|
|---------|-------------|
|
|
57
65
|
| `/subagent:view` | Open the live view: a tabbed overlay with a per-run activity feed and a brief detail page (inputs, files, stats), plus modal steer input for the focused run |
|
|
58
66
|
| `/subagent:doctor` | Diagnose pi invocation, model-role resolution, configuration, and role references |
|
|
59
|
-
| `/subagent:status` | List background runs
|
|
67
|
+
| `/subagent:status` | List background runs and their current state |
|
|
60
68
|
| `/subagent:cancel <id\|all> [reason]` | Cancel a live background run (or every live run); the optional reason is recorded with the run |
|
|
61
69
|
|
|
62
70
|
### Live view (`/subagent:view`)
|
|
@@ -65,7 +73,7 @@ A centered overlay covering most of the screen. A tab row across the top lists e
|
|
|
65
73
|
|
|
66
74
|
The **activity page** (default) is the run's live feed: a continuous, append-only list where each entry is static text with a state icon, and the only animated thing is the ellipsis on a running entry (`.` → `..` → `...`). Finishing freezes an entry in place — its position never changes, only the icon flips. Streamed assistant text grows in place as the run's last line and settles into plain terminal-colored text at the turn boundary. The feed is scrollable (`↑↓`, `PgUp/PgDn`, `Home`/`End`): the view pins to the end and auto-follows new entries; scrolling up unpins (a `⋮ N earlier` marker appears), and reaching the bottom again re-pins. Both foreground and background runs appear here; a foreground run stays listed while its delegate call blocks the main agent. A run leaves the view once its result is in the conversation — when the last one goes, the overlay shows a centered empty notice (with `Esc close` hinted) rather than shrinking away.
|
|
67
75
|
|
|
68
|
-
The **brief page** shows the run's inputs and vitals at full width: the task and context verbatim (wrapped; head+tail elided beyond 20k chars), the reference file list annotated with `✓`/`·` for whether the child's tool calls actually touched each file, usage and time stats, the fallback trace, and a stderr tail on failures.
|
|
76
|
+
The **brief page** shows the run's inputs and vitals at full width: the task and context verbatim (wrapped; head+tail elided beyond 20k chars), inherited-conversation size and truncation status when enabled (never its text), the reference file list annotated with `✓`/`·` for whether the child's tool calls actually touched each file, usage and time stats, the fallback trace, and a stderr tail on failures.
|
|
69
77
|
|
|
70
78
|
Steer input is modal so keys never conflict with typing: in browse mode `s` opens the editor, `Enter` queues the message into the focused run (only while it is running) and returns to browse, `Esc` cancels and clears. The message appears immediately in the feed as an `↩ steer:` entry and is delivered to the child after its current tool batch, before its next LLM call — the run keeps its progress. `Esc` in browse mode closes the overlay.
|
|
71
79
|
|
|
@@ -108,16 +116,19 @@ Edit `~/.pi/agent/settings.json`:
|
|
|
108
116
|
"summary": {
|
|
109
117
|
"role": "utility",
|
|
110
118
|
"enabled": true
|
|
119
|
+
},
|
|
120
|
+
"inheritance": {
|
|
121
|
+
"maxChars": 50000
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
```
|
|
115
126
|
|
|
116
|
-
All fields are optional. Defaults: `maxConcurrency: 4`, `maxDepth: 3`, `maxTurns: 0` (unlimited), `maxCost: 0` (unlimited), `history.enabled: true`, `summary.role: "utility"`, `summary.enabled: true`.
|
|
127
|
+
All fields are optional. Defaults: `maxConcurrency: 4`, `maxDepth: 3`, `maxTurns: 0` (unlimited), `maxCost: 0` (unlimited), `history.enabled: true`, `summary.role: "utility"`, `summary.enabled: true`, and `inheritance.maxChars: 50000`.
|
|
117
128
|
|
|
118
129
|
Timeouts are defined per role. Built-in defaults are `explorer: 900`, `reviewer: 3600`, `worker: 2400`, and `researcher: 2400` seconds. The timeout is active time — the clock pauses while the child is inside a nested `subagent_delegate` call, so delegate-capable roles need no extra headroom.
|
|
119
130
|
|
|
120
|
-
|
|
131
|
+
`maxConcurrency`, `maxDepth`, `maxTurns`, `maxCost`, and per-role `timeout` accept `0` for unlimited. Negative values are normalized to `0`; non-numeric or non-finite values fall back to their defaults. `inheritance.maxChars` is different: it must be a positive finite integer, and zero, negative, invalid, or non-finite values use the default. `maxConcurrency: 0` runs delegates without queuing, and `maxDepth: 0` permits unrestricted nesting.
|
|
121
132
|
|
|
122
133
|
### Agent Overrides
|
|
123
134
|
|
|
@@ -194,6 +205,26 @@ Delegate tasks that would generate many tool calls or verbose output to keep you
|
|
|
194
205
|
]
|
|
195
206
|
```
|
|
196
207
|
|
|
208
|
+
### Inheriting parent conversation
|
|
209
|
+
|
|
210
|
+
`inheritConversation` is opt-in and defaults to `false`. Use it when a delta task relies on the active parent dialogue and repeating that material would be impractical:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"role": "worker",
|
|
215
|
+
"task": "Implement the approved approach and satisfy the acceptance checklist above.",
|
|
216
|
+
"inheritConversation": true
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
At delegate execution, pi-subagent snapshots `buildContextEntries()` for the active branch. It serializes only compaction summaries, branch summaries, and text blocks from user and assistant messages. Thinking, images, tool calls and arguments, tool results, custom UI/state messages, bash messages, and model metadata are excluded. Newer compaction entries with a `retainedTail` are supported; older `firstKeptEntryId` compactions use the separately returned kept entries.
|
|
221
|
+
|
|
222
|
+
The inherited body is mechanically limited by `inheritance.maxChars` (default 50,000). When it is too long, pi-subagent retains summary context and the newest dialogue, inserting an omission marker. No model call summarizes this input. The child receives it in an independent `<inherited_conversation>` block after `files` and before explicit `context` and `task`; the task remains authoritative. Inherited conversation can be incomplete, so the child must report missing material rather than guess.
|
|
223
|
+
|
|
224
|
+
The tool-call title marks inherited runs. Expanded delegate input and `/subagent:view`'s brief page show the delivered character count plus `truncated` when applicable, next to the other input metadata; an enabled snapshot with no eligible text is shown as empty. The inherited body is never rendered or written to subagent history. History records only the safe inheritance flag, character count, and truncation status.
|
|
225
|
+
|
|
226
|
+
Keep the default isolated mode for focused work. Without inheritance, `task`, `context`, and `files` must be self-contained.
|
|
227
|
+
|
|
197
228
|
## Background Delegation
|
|
198
229
|
|
|
199
230
|
Three execution properties, kept separate:
|
|
@@ -229,15 +260,15 @@ Typical flow:
|
|
|
229
260
|
|
|
230
261
|
Semantics worth knowing:
|
|
231
262
|
|
|
232
|
-
- **Results are pull-only for the model.** A purple completion notice is shown to the user, but nothing delivers the result to the model or wakes it up. The notice is a pure notification in the same visual family as pi's `[compaction]` card — a `[subagent] id (role) outcome` header with the bare task preview beneath, each line truncated to the terminal width — and deliberately unlike the tool rows, so it never reads as model behavior; the result itself never appears in the notice, only in `subagent_check` (model) or `/subagent:status` (user). The model owns the collection point: `subagent_wait`, then `subagent_check` each run. The inbox reminder (below) lists
|
|
263
|
+
- **Results are pull-only for the model.** A purple completion notice is shown to the user, but nothing delivers the result to the model or wakes it up. The notice is a pure notification in the same visual family as pi's `[compaction]` card — a `[subagent] id (role) outcome` header with the bare task preview beneath, each line truncated to the terminal width — and deliberately unlike the tool rows, so it never reads as model behavior; the result itself never appears in the notice, only in `subagent_check` (model) or `/subagent:status` (user). The model owns the collection point: `subagent_wait`, then `subagent_check` each run. The inbox reminder (below) lists runs not yet checked on the active branch on every request, but it never pushes results.
|
|
233
264
|
- **Background runs survive turn cancellation** and are unaffected by a cancelled `subagent_wait` — cancelling the wait never cancels the runs; call `subagent_wait` or `subagent_check` again later.
|
|
234
|
-
- **
|
|
235
|
-
- **Cancellation keeps the partial output.** `subagent_cancel(id, reason?)` kills the child (SIGTERM, escalating to SIGKILL) and settles the run as `cancelled` — its own stop reason in the same family as `timeout`/`budget_exceeded` (TUI warning styling ⏹, not the error-red ✗ of real failures) — with whatever it had produced. The `reason` becomes the error message verbatim, so whoever reads the partial output later via `subagent_check` — or the audit history — sees `cancelled — <reason>`; the source is distinguishable too (`user: ...` for `/subagent:cancel`, the model's own words for the tool, `session shutdown` for reaping). Cancelling does not
|
|
236
|
-
- **Inbox reminder:** every LLM call carries a `[background subagent runs]` system reminder listing the
|
|
265
|
+
- **Idempotent check, session-tree delivery state:** `subagent_check` re-delivers the same terminal snapshot on every call — runs stay in the registry for the whole session, so no result can ever be stranded by branch navigation or compaction. Whether a run still needs collecting is not tracked in the registry: it derives from the session tree itself. The session is append-only, so branching back past a check entry drops it from the active path — the inbox reminder re-arms and the model simply checks again (the id still resolves; the run is still there). Branching forward to the original branch restores the check entry and silences the reminder again.
|
|
266
|
+
- **Cancellation keeps the partial output.** `subagent_cancel(id, reason?)` kills the child (SIGTERM, escalating to SIGKILL) and settles the run as `cancelled` — its own stop reason in the same family as `timeout`/`budget_exceeded` (TUI warning styling ⏹, not the error-red ✗ of real failures) — with whatever it had produced. The `reason` becomes the error message verbatim, so whoever reads the partial output later via `subagent_check` — or the audit history — sees `cancelled — <reason>`; the source is distinguishable too (`user: ...` for `/subagent:cancel`, the model's own words for the tool, `session shutdown` for reaping). Cancelling does not remove the run: `subagent_check` still returns the partial output, and `subagent_wait` reports the run as `cancelled (partial output kept)`.
|
|
267
|
+
- **Inbox reminder:** every LLM call carries a `[background subagent runs]` system reminder listing the runs not yet checked on the active branch (queued, running, and finished-but-unchecked alike, including cancelled ones — shown as `cancelled — <reason>`), injected at a cache-stable head position. Runs missing from the list were already checked on this branch — so a finished run the model forgot to check keeps surfacing until it does. Branch navigation keeps this honest: the list derives from the session tree, not registry bookkeeping.
|
|
237
268
|
- **`timeout_ms` is optional.** Without it, `subagent_wait` blocks until every run finishes; each run is still bounded by its own role timeout.
|
|
238
269
|
- Background runs share the global `maxConcurrency` gate — extra runs show up as `queued` in wait/check views.
|
|
239
270
|
- **Top-level only:** nested subagents cannot delegate in the background (a subagent process exits when its task finishes, which would orphan the run).
|
|
240
|
-
- The run registry lives in the pi process: a `/reload` or restart orphans in-flight background runs (their ids stop resolving). `/subagent:status` lists every registered run
|
|
271
|
+
- The run registry lives in the pi process: a `/reload` or restart orphans in-flight background runs (their ids stop resolving). `/subagent:status` lists every registered run and its current state.
|
|
241
272
|
|
|
242
273
|
### Steering a running subagent
|
|
243
274
|
|
|
@@ -254,7 +285,7 @@ Queued steers are visible in neither wait nor check results — they shape the r
|
|
|
254
285
|
|
|
255
286
|
Each tool row renders one aspect of the same decomposition the foreground row shows all at once (input · process · result · usage):
|
|
256
287
|
|
|
257
|
-
- **Background subagent_delegate row = input only.** Collapsed: `▶ sub-1 <task first line>`. Expanded: plus `@file` references, context size, and the full task text. Static — the run progresses invisibly until a subagent_wait/subagent_check row picks it up.
|
|
288
|
+
- **Background subagent_delegate row = input only.** Collapsed: `▶ sub-1 <task first line>`. Expanded: plus `@file` references, context size, inherited-conversation size/truncation metadata when enabled, and the full task text. Static — the run progresses invisibly until a subagent_wait/subagent_check row picks it up.
|
|
258
289
|
- **subagent_wait row = process + usage.** The input line shows the id list (or `(all)`) plus the timeout ceiling (`≤30s`) when one was given. One block per watched run: status line (`⏸ queued / ⏳ running` + id + task preview; bare, icon-free once terminal), a live activity stream (collapsed keeps the latest 5 items with a leading ellipsis; expanded shows everything) and a ticking usage bar. Once a run finishes, its process stream is replaced by a **status-only** result line (`✓ finished` / `⏲ budget-exceeded with the reason` / `⏱ timed out` / `⏹ cancelled with the reason` / `✗ <reason>`) — the output itself never appears in a subagent_wait row; expanded keeps the full process stream instead. A timed-out wait freezes the view.
|
|
259
290
|
- **subagent_check row = the result view.** Same block shape as subagent_wait's single-run view (no id — there is only one), but the result line shows `✓ <AI summary>` (or the budget/failure reason when the run stopped early) and the expanded view renders the **full output** — subagent_check is where the conclusion lives.
|
|
260
291
|
- **subagent_cancel row = confirmation only.** Collapsed: `⏹ sub-1 (worker): cancelled after 1 turn (~29s)` (or `• sub-1 (worker) already finished — nothing to cancel` for a no-op). Expanded adds the reason and the pointer to `subagent_check` — the partial output **never renders here**; it stays in the registry until a check row fetches it (layer contract: delegate = input, wait = process, cancel = intervention, check = result).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d3ara1n/pi-subagent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Role-based subagent orchestration for pi — delegates tasks to specialized pi child processes with configurable model roles",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/config.test.ts
CHANGED
|
@@ -43,6 +43,19 @@ afterEach(() => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
describe("loadSubagentConfig", () => {
|
|
46
|
+
test("uses the inheritance default and accepts only positive finite integer maxChars", () => {
|
|
47
|
+
const { agentDir } = makeRoot();
|
|
48
|
+
writeSettings(agentDir, { subagent: { inheritance: { maxChars: 12 } } });
|
|
49
|
+
assert.equal(loadSubagentConfig().inheritance.maxChars, 12);
|
|
50
|
+
|
|
51
|
+
for (const maxChars of [0, -1, 0.5, 12.9, "500", false, null]) {
|
|
52
|
+
writeSettings(agentDir, { subagent: { inheritance: { maxChars } } });
|
|
53
|
+
assert.equal(loadSubagentConfig().inheritance.maxChars, DEFAULT_CONFIG.inheritance.maxChars);
|
|
54
|
+
}
|
|
55
|
+
writeSettingsText(agentDir, '{"subagent":{"inheritance":{"maxChars":1e999}}}');
|
|
56
|
+
assert.equal(loadSubagentConfig().inheritance.maxChars, DEFAULT_CONFIG.inheritance.maxChars);
|
|
57
|
+
});
|
|
58
|
+
|
|
46
59
|
test("preserves zero limits and clamps negative numeric limits to unlimited", () => {
|
|
47
60
|
const { agentDir } = makeRoot();
|
|
48
61
|
writeSettings(agentDir, {
|
|
@@ -99,6 +112,7 @@ describe("loadSubagentConfig", () => {
|
|
|
99
112
|
maxCost: 5,
|
|
100
113
|
history: { enabled: false },
|
|
101
114
|
summary: { enabled: false, role: "global-summary" },
|
|
115
|
+
inheritance: { maxChars: 1234 },
|
|
102
116
|
agentOverrides: { global: { disabled: true } },
|
|
103
117
|
},
|
|
104
118
|
});
|
|
@@ -112,7 +126,11 @@ describe("loadSubagentConfig", () => {
|
|
|
112
126
|
assert.equal(config.maxTurns, DEFAULT_CONFIG.maxTurns);
|
|
113
127
|
assert.equal(config.maxCost, DEFAULT_CONFIG.maxCost);
|
|
114
128
|
assert.deepEqual(config.history, DEFAULT_CONFIG.history);
|
|
115
|
-
assert.deepEqual(config.summary, {
|
|
129
|
+
assert.deepEqual(config.summary, {
|
|
130
|
+
enabled: DEFAULT_CONFIG.summary.enabled,
|
|
131
|
+
role: "project-summary",
|
|
132
|
+
});
|
|
133
|
+
assert.deepEqual(config.inheritance, DEFAULT_CONFIG.inheritance);
|
|
116
134
|
assert.deepEqual(config.agentOverrides, {});
|
|
117
135
|
});
|
|
118
136
|
});
|
package/src/config.ts
CHANGED
|
@@ -12,6 +12,12 @@ import type { SubagentConfig } from "./types.ts";
|
|
|
12
12
|
import { DEFAULT_CONFIG } from "./types.ts";
|
|
13
13
|
import { normalizeNonNegativeInteger, normalizeNonNegativeNumber } from "./utils.ts";
|
|
14
14
|
|
|
15
|
+
function normalizePositiveInteger(value: unknown, fallback: number): number {
|
|
16
|
+
return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value > 0
|
|
17
|
+
? value
|
|
18
|
+
: fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
function readSettingsFile(filePath: string): any {
|
|
16
22
|
try {
|
|
17
23
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -39,6 +45,7 @@ export function loadSubagentConfig(cwd?: string): SubagentConfig {
|
|
|
39
45
|
|
|
40
46
|
const rawSummary = raw?.summary;
|
|
41
47
|
const rawHistory = raw?.history;
|
|
48
|
+
const rawInheritance = raw?.inheritance;
|
|
42
49
|
return {
|
|
43
50
|
maxConcurrency: normalizeNonNegativeInteger(raw.maxConcurrency, DEFAULT_CONFIG.maxConcurrency),
|
|
44
51
|
maxDepth: normalizeNonNegativeInteger(raw.maxDepth, DEFAULT_CONFIG.maxDepth),
|
|
@@ -51,6 +58,12 @@ export function loadSubagentConfig(cwd?: string): SubagentConfig {
|
|
|
51
58
|
role: rawSummary?.role ?? DEFAULT_CONFIG.summary.role,
|
|
52
59
|
enabled: rawSummary?.enabled ?? DEFAULT_CONFIG.summary.enabled,
|
|
53
60
|
},
|
|
61
|
+
inheritance: {
|
|
62
|
+
maxChars: normalizePositiveInteger(
|
|
63
|
+
rawInheritance?.maxChars,
|
|
64
|
+
DEFAULT_CONFIG.inheritance.maxChars,
|
|
65
|
+
),
|
|
66
|
+
},
|
|
54
67
|
agentOverrides: raw.agentOverrides ?? {},
|
|
55
68
|
};
|
|
56
69
|
}
|
package/src/history.ts
CHANGED
|
@@ -38,6 +38,9 @@ export function persistSubagentHistory(
|
|
|
38
38
|
role,
|
|
39
39
|
task,
|
|
40
40
|
timestamp: Date.now(),
|
|
41
|
+
inheritConversation: r.inheritConversation === true,
|
|
42
|
+
inheritedConversationChars: r.inheritedConversationChars,
|
|
43
|
+
inheritedConversationTruncated: r.inheritedConversationTruncated,
|
|
41
44
|
exitCode: r.exitCode,
|
|
42
45
|
stopReason: r.stopReason,
|
|
43
46
|
model: r.model,
|