@clanker-code/pi-subagents 0.10.5 → 0.10.7

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.
@@ -30,12 +30,14 @@ export function formatTaskNotification(record: AgentRecord, resultMaxLen: number
30
30
 
31
31
  const resultPreview = record.result
32
32
  ? record.result.length > resultMaxLen
33
- ? record.result.slice(0, resultMaxLen) + "\n...(truncated, use get_subagent_result for full output)"
33
+ ? record.result.slice(0, resultMaxLen) + (record.outputFile
34
+ ? "\n...(truncated, use get_subagent_result for a bounded preview or inspect the transcript file)"
35
+ : "\n...(truncated, use get_subagent_result for a bounded preview)")
34
36
  : record.result
35
37
  : "No output.";
36
38
  const fullOutputInstruction = record.outputFile
37
- ? `Read the full output/log with get_subagent_result for agent ${record.id}, or inspect the transcript file: ${record.outputFile}`
38
- : `Read the full output/log with get_subagent_result for agent ${record.id}.`;
39
+ ? `Read a bounded preview with get_subagent_result for agent ${record.id}, or inspect the full transcript file: ${record.outputFile}`
40
+ : `Read a bounded preview with get_subagent_result for agent ${record.id}.`;
39
41
 
40
42
  return [
41
43
  `<task-notification>`,
@@ -106,8 +108,8 @@ export function registerSubagentNotificationRenderer(pi: ExtensionAPI): void {
106
108
  }
107
109
 
108
110
  const fullOutputHint = d.outputFile
109
- ? `full output: get_subagent_result ${d.id} or transcript: ${d.outputFile}`
110
- : `full output: get_subagent_result ${d.id}`;
111
+ ? `bounded preview: get_subagent_result ${d.id}; full transcript: ${d.outputFile}`
112
+ : `bounded preview: get_subagent_result ${d.id}`;
111
113
  line += "\n " + theme.fg("muted", fullOutputHint);
112
114
 
113
115
  return line;
package/src/peek.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import { existsSync, readFileSync } from "node:fs";
18
+ import { clampPeekLines, formatOutputFileHint, limitText, MAX_PEEK_CHARS, MAX_PEEK_LINES } from "./bounded-output.js";
18
19
  import type { AgentRecord } from "./types.js";
19
20
 
20
21
  export interface PeekOptions {
@@ -35,9 +36,6 @@ export interface PeekResult {
35
36
  source: "outputFile" | "result";
36
37
  }
37
38
 
38
- /** Default number of tail lines when neither `after` nor `lines` is given. */
39
- const DEFAULT_LINES = 20;
40
-
41
39
  /**
42
40
  * Produce a peek view of an agent's output. Returns null when there is no
43
41
  * source content at all (the caller renders a "no output yet" message).
@@ -48,27 +46,39 @@ export function peekAgentOutput(record: AgentRecord, opts: PeekOptions = {}): Pe
48
46
 
49
47
  const regex = opts.regex ? compileRegex(opts.regex) : undefined;
50
48
  const after = typeof opts.after === "number" ? opts.after : -1;
51
- const tail = typeof opts.lines === "number" && opts.lines >= 1 ? opts.lines : DEFAULT_LINES;
49
+ const tail = clampPeekLines(opts.lines);
52
50
 
53
51
  // Index each source line with its original position (1-based for display).
54
52
  const indexed = lines.map((text, i) => ({ no: i + 1, text }));
55
53
 
56
54
  // Filter-then-select.
57
55
  const filtered = regex ? indexed.filter((l) => regex.test(l.text)) : indexed;
58
- const selected =
59
- after >= 0
60
- ? filtered.filter((l) => l.no > after)
61
- : filtered.slice(-tail);
56
+ const matching = after >= 0 ? filtered.filter((l) => l.no > after) : filtered;
57
+ const selected = after >= 0 ? matching.slice(0, MAX_PEEK_LINES) : matching.slice(-tail);
58
+ const lineLimited = matching.length > selected.length;
62
59
 
63
60
  const totalLines = lines.length;
64
61
  const isRunning = record.status === "running" || record.status === "queued";
65
62
  const source: PeekResult["source"] =
66
63
  isRunning && record.outputFile && existsSync(record.outputFile) ? "outputFile" : "result";
67
64
 
68
- const header = buildHeader(opts, selected.length, totalLines, source);
65
+ const header = buildHeader(opts, selected.length, totalLines, source, tail, lineLimited, matching.length);
69
66
  const body = selected.map((l) => `[${l.no}] ${l.text}`).join("\n");
67
+ const limited = limitText(body, MAX_PEEK_CHARS);
68
+ const lastLine = selected.at(-1)?.no;
69
+ const notices: string[] = [];
70
+ if (lineLimited && lastLine !== undefined) {
71
+ notices.push(`Peek limited to ${MAX_PEEK_LINES} lines from ${matching.length} matching lines. Use peek.after: ${lastLine} to continue.`);
72
+ }
73
+ if (limited.truncated) {
74
+ notices.push(`Peek output truncated by ${limited.omittedChars} chars. Use a smaller lines value or regex filter.${formatOutputFileHint(record.outputFile)}`);
75
+ }
70
76
 
71
- return { text: `${header}\n\n${body}`, totalLines, source };
77
+ return {
78
+ text: `${header}\n\n${limited.text}${notices.length ? `\n\n---\n${notices.join("\n")}` : ""}`,
79
+ totalLines,
80
+ source,
81
+ };
72
82
  }
73
83
 
74
84
  /** Read the most useful text lines from the agent's output. */
@@ -141,13 +151,16 @@ function buildHeader(
141
151
  shown: number,
142
152
  total: number,
143
153
  source: PeekResult["source"],
154
+ tail: number,
155
+ lineLimited: boolean,
156
+ matching: number,
144
157
  ): string {
145
158
  const parts: string[] = [];
146
159
  if (typeof opts.after === "number") {
147
160
  parts.push(`after line number ${opts.after}`);
161
+ if (lineLimited) parts.push(`limited to ${MAX_PEEK_LINES} lines from ${matching} matching lines`);
148
162
  } else {
149
- const n = typeof opts.lines === "number" && opts.lines >= 1 ? opts.lines : DEFAULT_LINES;
150
- parts.push(`last ${n} lines`);
163
+ parts.push(`last ${tail} lines`);
151
164
  }
152
165
  if (opts.regex) parts.push(`filtered by regex /${opts.regex}/`);
153
166
  parts.push(`of ${total} total (${source === "outputFile" ? "live output file" : "result"})`);
@@ -1,183 +0,0 @@
1
- # Plan: Next pi-subagents improvements
2
-
3
- This plan covers four coordinated changes:
4
- 1. **Abort-detach + interruptible/timeout wait** for `get_subagent_result wait:true` (Escape no longer wedges the turn; default 4.5 min timeout).
5
- 2. Configurable `waitTimeoutSeconds` setting (default 270s) exposed in `/agents → Settings`.
6
- 3. `get_subagent_result` peek/tail capability with line numbers, regex filter, and `after` line offset.
7
- 4. Description polish for `inherit_context` and `isolation: "worktree"`.
8
-
9
- All changes are scoped to keep diffs minimal and upstream-merge-friendly.
10
-
11
- ### Key finding (abort-detach)
12
- Background spawns already do NOT pass the parent abort signal to `manager.spawn()` — only the now-dead foreground path did. So background subagents are already detached from Escape. The Escape-wedge symptom comes from `get_subagent_result wait:true`, which awaits `record.promise` and ignores the parent `signal`. Fix: race the wait against (a) the parent abort `signal` and (b) the configurable timeout, returning current status on either WITHOUT aborting the subagent.
13
-
14
- ---
15
-
16
- ## 1. Settings foundation: configurable `waitTimeoutSeconds`
17
-
18
- ### Motivation
19
- Users want to avoid typical 5-minute LLM cache expiry; 4.5 minutes is a safe default. We also want this exposed in `/agents → Settings` so it is discoverable and adjustable per project.
20
-
21
- ### Files touched
22
- - `src/types.ts` — add `waitTimeoutSeconds` to `AgentRecord`? No, this is runtime/config, not per-agent. Keep it in settings only.
23
- - `src/settings.ts`:
24
- - Add `waitTimeoutSeconds?: number` to `SubagentsSettings`.
25
- - Add `setWaitTimeoutSeconds: (seconds: number) => void` to `SettingsAppliers`.
26
- - Add sanitize rule: integer, min 30, max 3600 (30s–1h). Default 270 (4.5m) when absent.
27
- - Add `applySettings` wiring.
28
- - `src/index.ts`:
29
- - Add `let waitTimeoutSeconds = 270` and getter/setter.
30
- - Add to `snapshotSettings()`.
31
- - Add `/agents → Settings` item with id `waitTimeoutSeconds`.
32
- - Wire `applyValue` for `waitTimeoutSeconds` (numeric prompt, 30–3600).
33
- - Add to `applyAndEmitLoaded` appliers object.
34
- - Pass timeout value into `get_subagent_result` execute closure.
35
-
36
- ### Return value / UI impact
37
- - `get_subagent_result` `wait` description: mention current configured timeout.
38
- - On timeout, return message:
39
- ```
40
- Agent is still running after 4 minutes 30 seconds.
41
- This wait timed out to avoid blocking the parent session longer than the configured limit.
42
- Call get_subagent_result with wait: true again to keep waiting, or omit wait to check status.
43
- ```
44
-
45
- ### Tests
46
- - `test/settings.test.ts` (if exists) or new assertions in `test/settings.test.ts`: sanitize boundaries.
47
- - New/updated test for `get_subagent_result` timeout behavior using a mocked promise and fake timers.
48
-
49
- ---
50
-
51
- ## 2. `get_subagent_result` peek parameter
52
-
53
- ### Motivation
54
- Agents should be able to cheaply check recent output / logs without fetching the full result or conversation.
55
-
56
- ### Proposed schema
57
- ```ts
58
- peek: Type.Optional(
59
- Type.Object({
60
- lines: Type.Optional(Type.Number({ minimum: 1, description: "Number of trailing lines to return. Default: 20." })),
61
- regex: Type.Optional(Type.String({ description: "Optional regex filter. Only lines matching this regex are included." })),
62
- after: Type.Optional(Type.Number({ minimum: 0, description: "Return all lines after this 0-based line index. Overrides lines when set." })),
63
- }, {
64
- description: "Return a peek (tail/filter/update) of the agent result or streaming output file. Ignored when verbose is true.",
65
- }),
66
- ),
67
- ```
68
-
69
- ### Behavior
70
- - `peek` is ignored when `verbose: true`.
71
- - If `after` is set, return all lines with index > `after` (or >= `after+1`). Include line numbers.
72
- - Else, return the last `lines` lines (default 20). Include line numbers.
73
- - If `regex` is provided, filter matching lines **first**, then apply tail/after semantics. Include line numbers of the original source.
74
- - Source precedence:
75
- 1. If agent is running and `record.outputFile` exists, read from the streaming output file. Parse JSONL and extract assistant/toolResult text (most useful live content).
76
- 2. Else if `record.result` exists, split it into lines.
77
- 3. Else return: "No output yet."
78
-
79
- ### Return format
80
- ```
81
- Showing last 20 lines of agent output (line numbers from full output):
82
-
83
- [42] some line
84
- [43] another line
85
- ...
86
-
87
- ---
88
- Use verbose: true for the full conversation, or omit peek for the complete result.
89
- ```
90
-
91
- If `regex` is used, add: `(filtered by regex: /.../)`.
92
- If `after` is used, add: `(lines after index N)`.
93
-
94
- ### Implementation notes
95
- - Add helper `peekAgentOutput(record, peek)` in a new file or in `src/index.ts` near `get_subagent_result`.
96
- - For output-file JSONL parsing, reuse/extract from `output-file.ts` or read the file line-by-line.
97
- - Handle regex parse errors gracefully — return a clear error message.
98
- - Avoid reading the whole file into memory if possible; for now `readFileSync` is acceptable because output files are bounded by session length and the tail case is common.
99
-
100
- ### Tests
101
- - New test file `test/get-subagent-result-peek.test.ts` or extend existing `status-note-wiring.test.ts`:
102
- - Peek tail of result.
103
- - Peek with regex.
104
- - Peek `after` offset.
105
- - Peek ignored when verbose true.
106
- - Peek on running agent with output file.
107
- - Regex parse error handling.
108
-
109
- ---
110
-
111
- ## 3. Description polish
112
-
113
- ### `inherit_context`
114
- Current:
115
- > "If true, fork parent conversation into the agent. Default: false (fresh context)."
116
-
117
- New:
118
- > "If true, fork the parent conversation into the agent so it sees the chat history. Recommended for questions or requests that require current context. Default: false (fresh context)."
119
-
120
- Also update the eject template line in `src/index.ts`.
121
-
122
- ### `isolation: "worktree"`
123
- Current:
124
- > "Set to 'worktree' to run the agent in a temporary git worktree (isolated copy of the repo). Changes are saved to a branch on completion."
125
-
126
- New:
127
- > "Set to 'worktree' to run the agent in a temporary git worktree that is automatically created from the current repo state at HEAD and removed on completion. Changes are saved to a branch. Requires the working directory to be a git repo with at least one commit."
128
-
129
- Also update README.md and `examples/agent-tool-description.md` to match.
130
-
131
- ---
132
-
133
- ## 4. Coordination / cross-cutting concerns
134
-
135
- - `get_subagent_result` description must mention the configurable timeout, e.g.:
136
- > "If true, wait for the agent to complete before returning. Blocks up to the configured wait timeout (default 4.5 minutes). If the agent is still running when the timeout is reached, returns current status with instructions to call again. Default: false."
137
-
138
- - The settings UI should present `waitTimeoutSeconds` as "Wait timeout (30–3600s, default 270 = 4m30s)".
139
-
140
- - Tests for the timeout should use Vitest fake timers so they run fast and deterministically.
141
-
142
- - Peek should not duplicate verbose. Keep the contract: `verbose` = full conversation, `peek` = lightweight tail/filter.
143
-
144
- ---
145
-
146
- ## 5. Things to consider removing / avoiding
147
-
148
- Before jumping in, review whether any existing code becomes dead weight:
149
-
150
- - The foreground execution path in `src/index.ts` is now dead code after the background-by-default change. We currently left it in place but unreachable. Consider whether to remove it in a separate cleanup pass (it complicates future changes).
151
- - `run_in_background` param is deprecated but still in the schema. Keep it for prompt compatibility; do not remove.
152
- - `AgentConfig.runInBackground` frontmatter default is still consulted by `resolveAgentInvocationConfig`. Since `index.ts` now forces `runInBackground = true`, the frontmatter field is effectively ignored. Consider whether to deprecate it in docs or leave it as a no-op.
153
-
154
- ---
155
-
156
- ## 6. Acceptance criteria
157
-
158
- - [ ] `get_subagent_result wait:true` times out after the configured number of seconds and returns a clear message.
159
- - [ ] Timeout duration is configurable via `/agents → Settings` and persists to `.pi/subagents.json`.
160
- - [ ] `get_subagent_result` supports `peek` with `lines`, `regex`, and `after`.
161
- - [ ] `peek` returns line numbers and respects `verbose: true` (is ignored).
162
- - [ ] `inherit_context` and `isolation` descriptions are updated in tool schema, eject template, README, and example template.
163
- - [ ] All existing tests pass; new tests cover timeout + peek.
164
- - [ ] `npm run typecheck` and `npm run lint` clean.
165
-
166
- ---
167
-
168
- ## 7. Implementation order
169
-
170
- 1. Add `waitTimeoutSeconds` setting (settings.ts + index.ts wiring).
171
- 2. Implement wait timeout in `get_subagent_result` execute.
172
- 3. Add `peek` parameter + helper.
173
- 4. Update descriptions for `inherit_context` and `isolation`.
174
- 5. Update README + example template.
175
- 6. Write tests.
176
- 7. Run full test/typecheck/lint.
177
-
178
- --- SUMMARY ---
179
-
180
- - Add a configurable `waitTimeoutSeconds` setting (default 270s) wired into `/agents → Settings` and `get_subagent_result wait:true`.
181
- - Add a `peek` object param to `get_subagent_result` for tail/filter/offset access to result/output-file with line numbers, ignored when `verbose` is true.
182
- - Polish `inherit_context` and `isolation: "worktree"` descriptions across schema, template, README, and example.
183
- - Tests, typecheck, lint.
package/.plans/README.md DELETED
@@ -1,14 +0,0 @@
1
- # `.plans/` — local planning scratch
2
-
3
- This directory holds ad-hoc plans, notes, and design sketches for this fork. It is gitignored so it does not pollute upstream diffs.
4
-
5
- ## Conventions
6
-
7
- - One plan per file: `PLAN-<short-name>.md`.
8
- - Plans should include: motivation, files touched, behavior changes, UI/message changes, tests, acceptance criteria, implementation order, and a summary.
9
- - When a plan graduates to implementation, update the plan file with progress or archive it.
10
- - Delete obsolete plans to avoid stale guidance.
11
-
12
- ## Active plans
13
-
14
- - `PLAN-next-changes.md` — configurable `get_subagent_result` wait timeout, peek/tail/regex/after parameter, and description polish for `inherit_context` / `isolation: "worktree"`.
@@ -1,135 +0,0 @@
1
- # Proposal: Structured JSON Output for Subagents
2
-
3
- **Date:** 2026-06-19
4
- **Status:** Proposal / draft
5
- **Related:** `reviews/subagent-features-comparison.md`
6
-
7
- ## Problem
8
-
9
- Today, subagents return free-form text. The parent model must parse the text to extract lists, file paths, decisions, or any other structured data before it can feed results into the next tool or agent. This is slow, unreliable, and becomes a bottleneck when chaining multiple agents together.
10
-
11
- ## Goal
12
-
13
- Add an optional `output_schema` parameter to the `Agent` tool. When supplied, the agent must return JSON matching the schema. The extension validates the output and surfaces either the parsed object or a clear validation error.
14
-
15
- The feature must be strictly opt-in: the default is free-form text, and agents should only use structured output when the caller explicitly needs it.
16
-
17
- ## Design
18
-
19
- ### Tool parameter
20
-
21
- Add to the `Agent` tool schema:
22
-
23
- ```ts
24
- output_schema: Type.Optional(
25
- Type.Union([Type.String(), Type.Object({})], {
26
- description:
27
- 'Optional JSON Schema the agent\'s final answer must match. "": free-form text (recommended default; only use this when the consumer needs structured data).',
28
- })
29
- ),
30
- ```
31
-
32
- - Accepts either a JSON Schema object or a JSON-stringified schema.
33
- - Default is `undefined` / `""` → free-form text, no validation.
34
- - The schema type is JSON Schema, the same standard already used for tool-call schemas in pi.
35
-
36
- ### Frontmatter support
37
-
38
- Optionally allow agent `.md` files to pin a default schema:
39
-
40
- ```yaml
41
- ---
42
- output_schema:
43
- type: object
44
- properties:
45
- files:
46
- type: array
47
- items:
48
- type: object
49
- properties:
50
- path: { type: string }
51
- reason: { type: string }
52
- required: [path, reason]
53
- required: [files]
54
- ---
55
- ```
56
-
57
- The caller-supplied `output_schema` overrides the frontmatter value. This lets custom agent types advertise a structured contract without every caller repeating it.
58
-
59
- ### Prompt injection
60
-
61
- When `output_schema` is non-empty, append a concise instruction to the agent system prompt:
62
-
63
- > Your final answer must be a single JSON object matching the provided JSON Schema. Do not wrap the JSON in markdown fences, do not add commentary outside the JSON, and do not emit any text after the JSON object.
64
-
65
- ### Validation
66
-
67
- On agent completion:
68
-
69
- 1. Extract the last JSON object from the final assistant message.
70
- 2. If the output is wrapped in markdown fences, strip them.
71
- 3. Validate the parsed object against the JSON Schema using a lightweight validator (e.g. `ajv` or TypeBox's `Value.Check`).
72
- 4. If validation fails:
73
- - Mark the agent status as `failed`.
74
- - Return an error message containing the schema validation errors and a snippet of the raw output.
75
- 5. If validation passes:
76
- - Include the parsed object in the result under a `structured` field.
77
- - Keep the normal textual result preview so the notification UI stays readable.
78
-
79
- ### Result shape
80
-
81
- For a structured agent, the result should expose:
82
-
83
- ```json
84
- {
85
- "ok": true,
86
- "data": { "files": [...] },
87
- "preview": "Found 5 auth-related files..."
88
- }
89
- ```
90
-
91
- For failures:
92
-
93
- ```json
94
- {
95
- "ok": false,
96
- "error": "Output did not match the provided JSON Schema",
97
- "validationErrors": [...],
98
- "rawPreview": "..."
99
- }
100
- ```
101
-
102
- ### Notification and join modes
103
-
104
- Structured output does not change the notification path. Background agents still send steering-style notifications. The XML payload can include a `<structured-output>` block with the serialized JSON so the parent model can reason about it directly.
105
-
106
- ### Interaction with existing features
107
-
108
- - **Worktree isolation:** unchanged; the structured result is still returned through the normal completion path.
109
- - **Scheduling:** scheduled agents may use `output_schema` so recurring jobs produce machine-readable results.
110
- - **Resume:** resuming a structured agent does not re-validate old output; only the final completion is validated.
111
- - **Cross-extension RPC:** `output_schema` is serializable JSON and can be passed through the RPC spawn envelope.
112
-
113
- ## Acceptance criteria
114
-
115
- - [ ] `Agent` tool accepts optional `output_schema` as JSON Schema object or string.
116
- - [ ] Default behavior remains free-form text; no validation when omitted.
117
- - [ ] Frontmatter supports optional `output_schema`.
118
- - [ ] System prompt instructs the agent to emit only matching JSON.
119
- - [ ] Output is parsed and validated strictly on completion.
120
- - [ ] Validation failures produce a clear error with the raw output snippet.
121
- - [ ] Validation successes expose parsed data in result notifications and RPC responses.
122
- - [ ] Tests cover valid schema, invalid schema, fenced JSON, and missing schema cases.
123
- - [ ] `npm run typecheck` and `npm run lint` pass.
124
-
125
- ## Open questions
126
-
127
- 1. Should we add a small built-in helper agent type that demonstrates structured output (e.g. `json-explorer`)?
128
- 2. Should `output_schema` be surfaced in `/agents` agent-type descriptions so the orchestrator knows which agents return structured data?
129
- 3. Should failed validation trigger automatic retry with a steering message, or fail fast?
130
-
131
- ## Rationale
132
-
133
- JSON Schema was chosen because it is the same standard already used for pi tool definitions. Agents and users do not need to learn a new format, and existing tooling (TypeBox, `ajv`) integrates cleanly.
134
-
135
- Strict validation keeps the contract trustworthy. If a consumer asks for structured output, receiving invalid data is worse than receiving no data, because the consumer is likely to feed it into code that assumes correctness.
@@ -1,137 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Recursive Subagents Widget Preview</title>
7
- <style>
8
- :root {
9
- color-scheme: dark;
10
- --bg: #0b1020;
11
- --panel: #121a2d;
12
- --terminal: #070b13;
13
- --line: #29364f;
14
- --text: #dbe7ff;
15
- --muted: #7f8daa;
16
- --dim: #566176;
17
- --accent: #82aaff;
18
- --green: #8bdc9f;
19
- --yellow: #ffd479;
20
- --red: #ff7f8f;
21
- --cyan: #7ee7f5;
22
- --purple: #c099ff;
23
- }
24
- * { box-sizing: border-box; }
25
- body {
26
- margin: 0;
27
- background: radial-gradient(circle at 20% 0%, #1b2b50 0, transparent 35%), var(--bg);
28
- color: var(--text);
29
- font: 14px/1.45 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
30
- }
31
- main { max-width: 1220px; margin: 0 auto; padding: 32px; }
32
- h1 { font-size: 28px; margin: 0 0 8px; }
33
- h2 { font-size: 18px; margin: 28px 0 12px; color: #f0f5ff; }
34
- h3 { font-size: 14px; margin: 0 0 10px; color: var(--accent); text-transform: uppercase; letter-spacing: .08em; }
35
- p { color: #b9c6df; margin: 0 0 14px; }
36
- .grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; align-items: start; }
37
- .card { background: color-mix(in srgb, var(--panel) 92%, white 8%); border: 1px solid var(--line); border-radius: 16px; padding: 18px; box-shadow: 0 12px 40px rgba(0,0,0,.25); }
38
- .recommended { border-color: color-mix(in srgb, var(--accent) 70%, white 10%); box-shadow: 0 0 0 1px rgba(130,170,255,.15), 0 18px 55px rgba(22,41,82,.5); }
39
- .terminal { background: var(--terminal); border: 1px solid #1e2a3e; border-radius: 12px; padding: 12px 14px; font: 13px/1.35 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; white-space: pre; overflow: hidden; min-height: 260px; }
40
- .term-title { display: flex; gap: 6px; align-items: center; margin-bottom: 10px; color: var(--muted); font: 12px ui-monospace, monospace; }
41
- .dot { width: 9px; height: 9px; border-radius: 999px; display: inline-block; }
42
- .green { color: var(--green); } .yellow { color: var(--yellow); } .red { color: var(--red); } .cyan { color: var(--cyan); } .purple { color: var(--purple); }
43
- .muted { color: var(--muted); } .dim { color: var(--dim); } .accent { color: var(--accent); }
44
- .legend { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; color: var(--muted); font-size: 12px; }
45
- .plan { display: grid; grid-template-columns: 1.2fr .8fr; gap: 18px; }
46
- ol, ul { margin: 0; padding-left: 20px; color: #c4cee2; }
47
- li { margin: 6px 0; }
48
- .pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 999px; background: #1b2740; border: 1px solid #344260; color: #cfd9ef; font-size: 12px; }
49
- .wide { grid-column: 1 / -1; }
50
- @media (max-width: 980px) { .grid, .plan { grid-template-columns: 1fr; } }
51
- </style>
52
- </head>
53
- <body>
54
- <main>
55
- <h1>Recursive Subagents Widget — Plan + Visual Preview</h1>
56
- <p>Verified issue: the current widget is flat and bound to one manager instance. It does not assemble a durable parent → child → grandchild tree from recursive agent metadata.</p>
57
-
58
- <section class="plan">
59
- <div class="card">
60
- <h2>Implementation plan, once design is approved</h2>
61
- <ol>
62
- <li>Create a small tree model layer: records keyed by id, linked by <code>parentAgentId</code>, sorted by start time/status.</li>
63
- <li>Teach lifecycle events / shared manager state to surface descendants to the root widget, not only direct children.</li>
64
- <li>Add configurable widget modes: <code>compact</code> (Option A), <code>rich</code> (Option B), and <code>auto</code>.</li>
65
- <li>Use the focused path view (Option C) when rendering from inside a subagent, or as an optional display mode later.</li>
66
- <li>Add deterministic tests for parent → child → grandchild rendering, overflow, width truncation, completed/error linger, and mode switching.</li>
67
- <li>Keep status bar compact: aggregate running/queued counts across the full tree.</li>
68
- </ol>
69
- </div>
70
- <div class="card">
71
- <h2>Acceptance criteria</h2>
72
- <ul>
73
- <li>Grandchildren and deeper descendants are visible.</li>
74
- <li>Users can choose compact vs rich rendering in settings.</li>
75
- <li>Subagents can see their location in the larger recursive tree.</li>
76
- <li>Width/height bounded; no TUI overflow.</li>
77
- <li>Running activity remains live and readable.</li>
78
- <li>Completed/error states linger briefly in context.</li>
79
- </ul>
80
- </div>
81
- </section>
82
-
83
- <h2>Design options</h2>
84
- <div class="grid">
85
- <article class="card">
86
- <h3>Mode: Compact tree</h3>
87
- <div class="terminal"><div class="term-title"><span class="dot" style="background:#ff5f57"></span><span class="dot" style="background:#ffbd2e"></span><span class="dot" style="background:#28c840"></span><span>Agents widget</span></div><span class="accent">● Agents</span>
88
- ├─ <span class="green">⠋</span> <b>Plan</b> split task <span class="dim">· ↻3 · 2 tools · 1m 12s</span>
89
- │ ├─ <span class="green">⠹</span> <b>Explore</b> inspect UI <span class="dim">· reading…</span>
90
- │ └─ <span class="yellow">✓</span> <b>auditor</b> review paths <span class="dim">· 42s</span>
91
- └─ <span class="green">⠼</span> <b>general-purpose</b> write tests <span class="dim">· editing…</span></div>
92
- <p><b>Use as:</b> configurable mode <code>compact</code>. <b>Pros:</b> dense, low-risk, close to current widget. <b>Cons:</b> less rich for deep trees.</p>
93
- </article>
94
-
95
- <article class="card recommended">
96
- <h3>Mode: Rich tree (default)</h3>
97
- <div class="terminal"><div class="term-title"><span class="dot" style="background:#ff5f57"></span><span class="dot" style="background:#ffbd2e"></span><span class="dot" style="background:#28c840"></span><span>Agents widget</span></div><span class="accent">● Agents</span> <span class="muted">3 running · 1 queued · depth 3/4</span>
98
- ├─ <span class="green">⠋</span> <b>Plan</b> <span class="purple">opus</span> <span class="muted">split task</span>
99
- │ <span class="dim">⎿ ↻3≤20 · 4 tools · 22.8k token (38%) · 1m 12s</span>
100
- │ ├─ <span class="green">⠹</span> <b>Explore</b> <span class="muted">inspect widget data flow</span>
101
- │ │ <span class="dim">⎿ reading agent-widget.ts · 14.2s</span>
102
- │ │ └─ <span class="green">⠼</span> <b>general-purpose</b> <span class="muted">trace manager ownership</span>
103
- │ │ <span class="dim">⎿ searching tests · 4.8s</span>
104
- │ └─ <span class="yellow">✓</span> <b>auditor</b> <span class="muted">review plan</span> <span class="dim">· 31s</span>
105
- ├─ <span class="cyan">◦</span> <b>Explore</b> <span class="muted">queued after concurrency cap</span>
106
- └─ <span class="red">✗</span> <b>Plan</b> <span class="muted">old attempt</span> <span class="red">error: model unavailable</span></div>
107
- <p><b>Use as:</b> configurable mode <code>rich</code>, recommended default. <b>Pros:</b> best visibility; activity/stats are readable; relationships are obvious. <b>Cons:</b> needs careful overflow rules.</p>
108
- </article>
109
-
110
- <article class="card">
111
- <h3>Focused subagent location view</h3>
112
- <div class="terminal"><div class="term-title"><span class="dot" style="background:#ff5f57"></span><span class="dot" style="background:#ffbd2e"></span><span class="dot" style="background:#28c840"></span><span>Agents widget</span></div><span class="accent">● Agents</span> <span class="muted">7 total · showing active branch</span>
113
- ├─ <span class="green">⠋</span> <b>Plan</b> split task <span class="dim">· 1m 12s</span>
114
- │ └─ <span class="green">⠹</span> <b>Explore</b> inspect UI <span class="dim">· reading…</span>
115
- │ └─ <span class="green">⠼</span> <b>general-purpose</b> trace manager <span class="dim">· searching…</span>
116
- ├─ <span class="dim">+2 completed siblings hidden</span>
117
- └─ <span class="dim">+2 queued / stale descendants hidden</span></div>
118
- <p><b>Use as:</b> view shown inside a subagent session to answer “where am I in the tree?” It can also become a selectable mode later. <b>Pros:</b> very compact under load.</p>
119
- </article>
120
- </div>
121
-
122
- <section class="card wide" style="margin-top:18px;">
123
- <h2>Settled direction so far</h2>
124
- <p><span class="pill">rich default</span> <span class="pill">compact setting</span> <span class="pill">focused subagent view</span></p>
125
- <p>Build one recursive tree model, then render it through multiple views. The root TUI widget can use <code>rich</code>, <code>compact</code>, or <code>auto</code>. A subagent-local widget can render the focused path view so the agent/operator sees the current subagent’s location in the whole delegation tree.</p>
126
- <ul>
127
- <li><b>Header:</b> aggregate full-tree counts and max observed depth.</li>
128
- <li><b>Rich node:</b> connector + status icon/spinner + agent type + model/tag chips + description, plus detail line.</li>
129
- <li><b>Compact node:</b> same tree structure, one line per agent, fewer stats.</li>
130
- <li><b>Focused node:</b> ancestor path + current active branch + hidden sibling summaries.</li>
131
- <li><b>Overflow:</b> collapse by subtree: <code>└─ +4 descendants hidden (2 running, 1 queued, 1 finished)</code>.</li>
132
- <li><b>Fallback:</b> orphan records render under <code>Other agents</code> with a dim warning marker.</li>
133
- </ul>
134
- </section>
135
- </main>
136
- </body>
137
- </html>