@bacnh85/pi-subagent 0.6.1 → 0.7.1
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/agent-format.md +12 -0
- package/agents/general-purpose.md +1 -0
- package/agents/reviewer.md +2 -0
- package/agents/scout.md +2 -0
- package/agents/worker.md +1 -0
- package/extensions/agents.ts +30 -0
- package/extensions/index.ts +80 -26
- package/extensions/render.ts +3 -2
- package/extensions/thread-viewer.ts +2 -1
- package/extensions/threads.ts +3 -0
- package/package.json +1 -1
package/agent-format.md
CHANGED
|
@@ -21,9 +21,21 @@ description: ... # Required. When to use this agent.
|
|
|
21
21
|
tools: read, grep, ... # Optional. Comma-separated tool names. Defaults to all.
|
|
22
22
|
model: provider/model # Optional. Defaults to parent's model.
|
|
23
23
|
thinking: low # Optional: off|minimal|low|medium|high|xhigh|max.
|
|
24
|
+
sandbox: read-only # Optional: read-only | workspace-write. Auto-derives tool restrictions.
|
|
25
|
+
color: cyan # Optional: red|blue|green|yellow|purple|orange|pink|cyan.
|
|
24
26
|
---
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
### `sandbox`
|
|
30
|
+
|
|
31
|
+
- `read-only`: Restricts tools to `read`, `grep`, `find`, `ls`. Overrides any `tools` field.
|
|
32
|
+
- `workspace-write` (default): Uses the agent's `tools` list or defaults to all tools.
|
|
33
|
+
|
|
34
|
+
### `color`
|
|
35
|
+
|
|
36
|
+
Display color for the agent name in the TUI thread picker, viewer, and result summary.
|
|
37
|
+
Accepted values: `red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, `cyan`.
|
|
38
|
+
|
|
27
39
|
Only `name` and `description` are required.
|
|
28
40
|
|
|
29
41
|
## Body
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: general-purpose
|
|
3
3
|
description: General-purpose sub-agent for any delegated task. Use when no specialized agent fits. Good for complex research, multi-step operations, and code modifications.
|
|
4
4
|
tools: read, bash, edit, write, grep, find, ls
|
|
5
|
+
color: yellow
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
You are a capable coding assistant running as a sub-agent. Complete the delegated task efficiently and return a concise summary of your findings or changes.
|
package/agents/reviewer.md
CHANGED
|
@@ -3,6 +3,8 @@ name: reviewer
|
|
|
3
3
|
description: Code review specialist. Use for correctness, security, regression, and meaningful test-gap review.
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
5
|
thinking: high
|
|
6
|
+
color: purple
|
|
7
|
+
sandbox: read-only
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
You are an independent senior code reviewer. Inspect the requested Git scope with read-only tools.
|
package/agents/scout.md
CHANGED
|
@@ -3,6 +3,8 @@ name: scout
|
|
|
3
3
|
description: Fast codebase recon that returns compressed context for handoff. Use for finding files, understanding structure, locating symbols.
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
5
|
thinking: low
|
|
6
|
+
color: cyan
|
|
7
|
+
sandbox: read-only
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
You are a scout. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
|
package/agents/worker.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: worker
|
|
3
3
|
description: General-purpose coding agent with full tool access. Use only when explicitly requested for isolated implementation.
|
|
4
4
|
thinking: medium
|
|
5
|
+
color: green
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
You are a skilled software engineer. Implement the requested task with care and precision.
|
package/extensions/agents.ts
CHANGED
|
@@ -13,12 +13,16 @@ import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/
|
|
|
13
13
|
|
|
14
14
|
export type AgentScope = "user" | "project" | "both";
|
|
15
15
|
|
|
16
|
+
export type AgentColor = "red" | "blue" | "green" | "yellow" | "purple" | "orange" | "pink" | "cyan";
|
|
17
|
+
|
|
16
18
|
export interface AgentConfig {
|
|
17
19
|
name: string;
|
|
18
20
|
description: string;
|
|
19
21
|
tools?: string[];
|
|
20
22
|
model?: string;
|
|
21
23
|
thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
24
|
+
sandbox?: "read-only" | "workspace-write";
|
|
25
|
+
color?: AgentColor;
|
|
22
26
|
systemPrompt: string;
|
|
23
27
|
source: "user" | "project" | "bundled";
|
|
24
28
|
filePath: string;
|
|
@@ -160,6 +164,26 @@ function loadAgentsFromDir(
|
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
|
|
167
|
+
if (typeof frontmatter.sandbox === "string" && frontmatter.sandbox) {
|
|
168
|
+
const validSandboxes = ["read-only", "workspace-write"];
|
|
169
|
+
if (!validSandboxes.includes(frontmatter.sandbox)) {
|
|
170
|
+
diagnostics.push({
|
|
171
|
+
filePath,
|
|
172
|
+
issue: `Invalid sandbox mode "${frontmatter.sandbox}". Valid values: ${validSandboxes.join(", ")}. Ignoring.`,
|
|
173
|
+
severity: "warn",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const VALID_COLORS = ["red", "blue", "green", "yellow", "purple", "orange", "pink", "cyan"] as const;
|
|
179
|
+
if (typeof frontmatter.color === "string" && frontmatter.color && !VALID_COLORS.includes(frontmatter.color as any)) {
|
|
180
|
+
diagnostics.push({
|
|
181
|
+
filePath,
|
|
182
|
+
issue: `Invalid color "${frontmatter.color}". Valid values: ${VALID_COLORS.join(", ")}. Ignoring.`,
|
|
183
|
+
severity: "warn",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
163
187
|
agents.push({
|
|
164
188
|
name: frontmatter.name,
|
|
165
189
|
description: frontmatter.description,
|
|
@@ -168,6 +192,12 @@ function loadAgentsFromDir(
|
|
|
168
192
|
thinking: typeof frontmatter.thinking === "string" && ["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(frontmatter.thinking)
|
|
169
193
|
? frontmatter.thinking as AgentConfig["thinking"]
|
|
170
194
|
: undefined,
|
|
195
|
+
sandbox: typeof frontmatter.sandbox === "string" && ["read-only", "workspace-write"].includes(frontmatter.sandbox)
|
|
196
|
+
? frontmatter.sandbox as "read-only" | "workspace-write"
|
|
197
|
+
: undefined,
|
|
198
|
+
color: typeof frontmatter.color === "string" && VALID_COLORS.includes(frontmatter.color as any)
|
|
199
|
+
? frontmatter.color as AgentColor
|
|
200
|
+
: undefined,
|
|
171
201
|
systemPrompt: body,
|
|
172
202
|
source,
|
|
173
203
|
filePath,
|
package/extensions/index.ts
CHANGED
|
@@ -25,11 +25,12 @@ import {
|
|
|
25
25
|
getAgentDir,
|
|
26
26
|
getMarkdownTheme,
|
|
27
27
|
ModelRegistry,
|
|
28
|
+
type ThemeColor,
|
|
28
29
|
} from "@earendil-works/pi-coding-agent";
|
|
29
30
|
import { Container, Markdown, SelectList, Spacer, Text } from "@earendil-works/pi-tui";
|
|
30
31
|
import { Type } from "typebox";
|
|
31
32
|
|
|
32
|
-
import { type AgentConfig, type AgentScope, discoverAgents, formatAgentList, invalidateAgentCache } from "./agents.ts";
|
|
33
|
+
import { type AgentColor, type AgentConfig, type AgentScope, discoverAgents, formatAgentList, invalidateAgentCache } from "./agents.ts";
|
|
33
34
|
import {
|
|
34
35
|
type SubAgentResult,
|
|
35
36
|
getFinalOutput,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
validateAgentTools,
|
|
45
46
|
truncateParallelOutput,
|
|
46
47
|
validateExecutionRequest,
|
|
48
|
+
READ_ONLY_TOOLS,
|
|
47
49
|
MAX_CONCURRENCY,
|
|
48
50
|
MAX_PARALLEL_TASKS,
|
|
49
51
|
MAX_CHAIN_LENGTH,
|
|
@@ -153,21 +155,34 @@ export default function (pi: ExtensionAPI) {
|
|
|
153
155
|
threadStore.clear();
|
|
154
156
|
});
|
|
155
157
|
|
|
156
|
-
// Proactively steer agents toward sub-agent delegation when users mention it
|
|
157
|
-
pi.on("before_agent_start", async (event) => {
|
|
158
|
-
const prompt = event.prompt.toLowerCase();
|
|
159
|
-
if (/\b(delegate to|use a subagent|run in parallel|spawn an agent|scout|review this|chain|worker agent)\b/.test(prompt)) {
|
|
160
|
-
return {
|
|
161
|
-
systemPrompt:
|
|
162
|
-
event.systemPrompt +
|
|
163
|
-
"\n\nThe subagent tool is available for delegating tasks to specialized agents with isolated context. Use /subagent to list available agents. Bundled: scout (fast recon), reviewer (code review), worker (implementation), general-purpose (fallback). Modes: single, parallel (max 8), chain.",
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
158
|
// Resolve bundled agents directory relative to this extension file
|
|
169
159
|
const bundledAgentsDir = path.resolve(__dirname, "../agents");
|
|
170
160
|
|
|
161
|
+
// Inject available agent catalog into system prompt for semantic auto-delegation
|
|
162
|
+
pi.on("before_agent_start", async (event) => {
|
|
163
|
+
const ctx = currentCtx;
|
|
164
|
+
const discovery = discoverAgents(ctx?.cwd ?? process.cwd(), "both", bundledAgentsDir);
|
|
165
|
+
const catalog = discovery.agents
|
|
166
|
+
.map((a) => {
|
|
167
|
+
const modelInfo = a.model ? ` (model: ${a.model})` : " (inherits parent)";
|
|
168
|
+
const thinkingInfo = a.thinking ? `, thinking: ${a.thinking}` : "";
|
|
169
|
+
const sandboxInfo = a.sandbox ? `, sandbox: ${a.sandbox}` : "";
|
|
170
|
+
return `- **${a.name}**: ${a.description}${modelInfo}${thinkingInfo}${sandboxInfo}`;
|
|
171
|
+
})
|
|
172
|
+
.join("\n");
|
|
173
|
+
return {
|
|
174
|
+
systemPrompt:
|
|
175
|
+
event.systemPrompt +
|
|
176
|
+
`\n\n## Available Subagents\n${catalog}\n\n` +
|
|
177
|
+
"The subagent tool can delegate tasks to these specialized agents with isolated context. " +
|
|
178
|
+
"Use for read-heavy exploration, parallel analysis, or work that would flood the main context.\n" +
|
|
179
|
+
"Prefer **scout** for fast read-only exploration. " +
|
|
180
|
+
"Prefer **reviewer** for code review (high thinking, read-only). " +
|
|
181
|
+
"Prefer **worker** for implementation (medium thinking, all tools). " +
|
|
182
|
+
"Modes: single, parallel (max 8 tasks, 4 concurrent), chain.",
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
|
|
171
186
|
// Public one-request/one-response service used by pi-review.
|
|
172
187
|
pi.events.on(SUBAGENT_REQUEST_EVENT, (raw) => {
|
|
173
188
|
const request = raw as SubagentRunRequest;
|
|
@@ -179,7 +194,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
179
194
|
request.respond({ id: request.id, ok: false, error: `Unknown agent: ${request.agent}` });
|
|
180
195
|
return;
|
|
181
196
|
}
|
|
182
|
-
const thread = threadStore.createThread({ agentName: agent.name, task: request.task, mode: "single" });
|
|
197
|
+
const thread = threadStore.createThread({ agentName: agent.name, task: request.task, mode: "single", color: agent.color ? AGENT_TO_THEME_COLOR[agent.color as AgentColor] : undefined });
|
|
183
198
|
void runNamedAgent({
|
|
184
199
|
agent: request.readOnly ? { ...agent, tools: ["read", "grep", "find", "ls"] } : agent,
|
|
185
200
|
task: request.task,
|
|
@@ -276,6 +291,34 @@ export default function (pi: ExtensionAPI) {
|
|
|
276
291
|
},
|
|
277
292
|
});
|
|
278
293
|
|
|
294
|
+
/** Map AgentColor (from agent frontmatter) to ThemeColor (for pi TUI). */
|
|
295
|
+
const AGENT_TO_THEME_COLOR: Record<AgentColor, ThemeColor> = {
|
|
296
|
+
red: "error",
|
|
297
|
+
blue: "accent",
|
|
298
|
+
green: "success",
|
|
299
|
+
yellow: "warning",
|
|
300
|
+
purple: "syntaxType",
|
|
301
|
+
orange: "syntaxString",
|
|
302
|
+
pink: "customMessageLabel",
|
|
303
|
+
cyan: "syntaxVariable",
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/** Resolve agent-defined color to a valid ThemeColor for thread creation. */
|
|
307
|
+
const agentToThemeColor = (agentName: string): ThemeColor | undefined => {
|
|
308
|
+
const ctx = currentCtx;
|
|
309
|
+
if (!ctx) return undefined;
|
|
310
|
+
const agent = discoverAgents(ctx.cwd, "both", bundledAgentsDir).agents.find(a => a.name === agentName);
|
|
311
|
+
return agent?.color ? AGENT_TO_THEME_COLOR[agent.color] : undefined;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/** Look up agent color by name for TUI rendering. */
|
|
315
|
+
const resolveAgentColor = (name: string): ThemeColor => {
|
|
316
|
+
const ctx = currentCtx;
|
|
317
|
+
if (!ctx) return "accent";
|
|
318
|
+
const found = discoverAgents(ctx.cwd, "both", bundledAgentsDir).agents.find(a => a.name === name);
|
|
319
|
+
return found?.color ? AGENT_TO_THEME_COLOR[found.color] : "accent";
|
|
320
|
+
};
|
|
321
|
+
|
|
279
322
|
pi.registerTool({
|
|
280
323
|
name: "subagent",
|
|
281
324
|
label: "Subagent",
|
|
@@ -425,10 +468,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
425
468
|
}
|
|
426
469
|
|
|
427
470
|
// Helper: validate and normalise tools for an agent.
|
|
428
|
-
function resolveChildTools(agentTools: string[] | undefined, readOnly?: boolean): string[] {
|
|
471
|
+
function resolveChildTools(agentTools: string[] | undefined, sandbox?: string, readOnly?: boolean): string[] {
|
|
429
472
|
const defaultTools = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
430
|
-
|
|
431
|
-
|
|
473
|
+
let rawTools = agentTools ?? defaultTools;
|
|
474
|
+
// sandbox overrides tools: silently strip mutation tools, not an error
|
|
475
|
+
if (sandbox === "read-only") {
|
|
476
|
+
rawTools = rawTools.filter(t => READ_ONLY_TOOLS.includes(t));
|
|
477
|
+
if (rawTools.length === 0) rawTools = [...READ_ONLY_TOOLS];
|
|
478
|
+
}
|
|
479
|
+
const effectiveReadOnly = readOnly || sandbox === "read-only";
|
|
480
|
+
const result = validateAgentTools({ tools: rawTools, readOnly: effectiveReadOnly });
|
|
432
481
|
if (result.errors.length > 0) {
|
|
433
482
|
throw new Error(`Tool validation errors: ${result.errors.join("; ")}`);
|
|
434
483
|
}
|
|
@@ -492,7 +541,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
492
541
|
try {
|
|
493
542
|
// Inject parent's API key so --api-key and other runtime overrides work
|
|
494
543
|
await injectApiKey(resolved.model);
|
|
495
|
-
tools = resolveChildTools(agent.tools, isReadOnly);
|
|
544
|
+
tools = resolveChildTools(agent.tools, agent.sandbox, isReadOnly);
|
|
496
545
|
effectiveTimeoutMs = resolveChildTimeout(timeoutMs, params.timeout);
|
|
497
546
|
safeCwd = resolveChildCwd(cwd);
|
|
498
547
|
} catch (err: unknown) {
|
|
@@ -541,6 +590,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
541
590
|
task: taskWithContext,
|
|
542
591
|
mode: "chain-step",
|
|
543
592
|
toolCallId: _toolCallId,
|
|
593
|
+
color: agentToThemeColor(step.agent),
|
|
544
594
|
});
|
|
545
595
|
const result = await runOne(
|
|
546
596
|
step.agent, taskWithContext, step.cwd,
|
|
@@ -632,6 +682,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
632
682
|
task: t.task,
|
|
633
683
|
mode: "parallel-task",
|
|
634
684
|
toolCallId: _toolCallId,
|
|
685
|
+
color: agentToThemeColor(t.agent),
|
|
635
686
|
}),
|
|
636
687
|
);
|
|
637
688
|
|
|
@@ -746,6 +797,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
746
797
|
task: params.task,
|
|
747
798
|
mode: "single",
|
|
748
799
|
toolCallId: _toolCallId,
|
|
800
|
+
color: agentToThemeColor(params.agent),
|
|
749
801
|
});
|
|
750
802
|
const result = await runOne(
|
|
751
803
|
params.agent, params.task, params.cwd,
|
|
@@ -816,7 +868,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
816
868
|
"\n " +
|
|
817
869
|
fg("muted", `${i + 1}.`) +
|
|
818
870
|
" " +
|
|
819
|
-
fg(
|
|
871
|
+
fg(resolveAgentColor(step.agent), step.agent) +
|
|
820
872
|
fg("dim", ` ${preview}`);
|
|
821
873
|
}
|
|
822
874
|
if (args.chain.length > 3)
|
|
@@ -832,7 +884,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
832
884
|
fg("muted", ` [${scope}]`);
|
|
833
885
|
for (const t of args.tasks.slice(0, 3)) {
|
|
834
886
|
const preview = t.task.length > 40 ? `${t.task.slice(0, 40)}...` : t.task;
|
|
835
|
-
text += `\n ${fg(
|
|
887
|
+
text += `\n ${fg(resolveAgentColor(t.agent), t.agent)}${fg("dim", ` ${preview}`)}`;
|
|
836
888
|
}
|
|
837
889
|
if (args.tasks.length > 3)
|
|
838
890
|
text += `\n ${fg("muted", `... +${args.tasks.length - 3} more`)}`;
|
|
@@ -848,7 +900,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
848
900
|
: "...";
|
|
849
901
|
let text =
|
|
850
902
|
fg("toolTitle", theme.bold("subagent ")) +
|
|
851
|
-
fg(
|
|
903
|
+
fg(resolveAgentColor(agentName), agentName) +
|
|
852
904
|
fg("muted", ` [${scope}]`);
|
|
853
905
|
text += `\n ${fg("dim", preview)}`;
|
|
854
906
|
return new Text(text, 0, 0);
|
|
@@ -866,7 +918,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
866
918
|
|
|
867
919
|
// --- Single ---
|
|
868
920
|
if (details.mode === "single" && details.results.length === 1) {
|
|
869
|
-
|
|
921
|
+
const r = details.results[0];
|
|
922
|
+
return renderSingleResult(r, expanded, theme, resolveAgentColor(r.agent));
|
|
870
923
|
}
|
|
871
924
|
|
|
872
925
|
// --- Chain ---
|
|
@@ -895,7 +948,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
895
948
|
container.addChild(
|
|
896
949
|
new Text(
|
|
897
950
|
fg("muted", `─── Step ${r.exitCode !== -1 ? "" : "?"}: `) +
|
|
898
|
-
fg(
|
|
951
|
+
fg(resolveAgentColor(r.agent), r.agent) +
|
|
899
952
|
` ${stepIcon}`,
|
|
900
953
|
0,
|
|
901
954
|
0,
|
|
@@ -930,7 +983,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
930
983
|
fg("accent", `${successCount}/${details.results.length} steps`);
|
|
931
984
|
for (const r of details.results) {
|
|
932
985
|
const stepIcon = isFailedResult(r) ? fg("error", "✗") : fg("success", "✓");
|
|
933
|
-
|
|
986
|
+
const color = resolveAgentColor(r.agent);
|
|
987
|
+
text += `\n ${stepIcon} ${fg(color, r.agent)}`;
|
|
934
988
|
}
|
|
935
989
|
const totalUsage = formatUsageStats(aggregateUsage(details.results));
|
|
936
990
|
if (totalUsage) text += `\n${fg("dim", totalUsage)}`;
|
|
@@ -973,7 +1027,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
973
1027
|
: fg("success", "✓");
|
|
974
1028
|
container.addChild(
|
|
975
1029
|
new Text(
|
|
976
|
-
fg("muted", "─── ") + fg(
|
|
1030
|
+
fg("muted", "─── ") + fg(resolveAgentColor(r.agent), r.agent) + ` ${taskIcon}`,
|
|
977
1031
|
0,
|
|
978
1032
|
0,
|
|
979
1033
|
),
|
|
@@ -1013,7 +1067,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1013
1067
|
: isFailedResult(r)
|
|
1014
1068
|
? fg("error", "✗")
|
|
1015
1069
|
: fg("success", "✓");
|
|
1016
|
-
text += `\n ${taskIcon} ${fg(
|
|
1070
|
+
text += `\n ${taskIcon} ${fg(resolveAgentColor(r.agent), r.agent)}`;
|
|
1017
1071
|
}
|
|
1018
1072
|
if (!isRunning) {
|
|
1019
1073
|
const totalUsage = formatUsageStats(aggregateUsage(details.results));
|
package/extensions/render.ts
CHANGED
|
@@ -188,6 +188,7 @@ export function renderSingleResult(
|
|
|
188
188
|
result: SubAgentResult,
|
|
189
189
|
expanded: boolean,
|
|
190
190
|
theme: { fg: (c: any, t: string) => string; bold: (t: string) => string },
|
|
191
|
+
agentColor?: string,
|
|
191
192
|
): Container | Text {
|
|
192
193
|
const isError = isFailedResult(result);
|
|
193
194
|
const icon = isError ? theme.fg("error", "✗") : theme.fg("success", "✓");
|
|
@@ -197,7 +198,7 @@ export function renderSingleResult(
|
|
|
197
198
|
if (expanded) {
|
|
198
199
|
const mdTheme = getMarkdownTheme();
|
|
199
200
|
const container = new Container();
|
|
200
|
-
let header = `${icon} ${theme.fg("toolTitle", theme.bold(result.agent))}`;
|
|
201
|
+
let header = `${icon} ${theme.fg(agentColor ?? "toolTitle", theme.bold(result.agent))}`;
|
|
201
202
|
if (isError && result.stopReason) {
|
|
202
203
|
const reasonColor = result.stopReason === "timeout" ? "warning" : "error";
|
|
203
204
|
header += ` ${theme.fg(reasonColor, `[${result.stopReason}]`)}`;
|
|
@@ -239,7 +240,7 @@ export function renderSingleResult(
|
|
|
239
240
|
}
|
|
240
241
|
|
|
241
242
|
// Collapsed
|
|
242
|
-
let text = `${icon} ${theme.fg("toolTitle", theme.bold(result.agent))}`;
|
|
243
|
+
let text = `${icon} ${theme.fg(agentColor ?? "toolTitle", theme.bold(result.agent))}`;
|
|
243
244
|
if (isError && result.stopReason) {
|
|
244
245
|
const reasonColor = result.stopReason === "timeout" ? "warning" : "error";
|
|
245
246
|
text += ` ${theme.fg(reasonColor, `[${result.stopReason}]`)}`;
|
|
@@ -123,7 +123,8 @@ export class ThreadViewer {
|
|
|
123
123
|
else if (this.thread.mode === "chain-step") modeLabel = t.fg("muted", " [chain]");
|
|
124
124
|
|
|
125
125
|
// Header
|
|
126
|
-
|
|
126
|
+
const agentColor = this.thread.color ?? "accent";
|
|
127
|
+
let header = `${icon} ${t.fg(agentColor, t.bold(this.thread.agentName))}${modeLabel}`;
|
|
127
128
|
if (status === "running") header += ` ${t.fg("warning", "(running...)")}`;
|
|
128
129
|
else if (status === "aborted") header += ` ${t.fg("error", "[aborted]")}`;
|
|
129
130
|
if (result && isErr && result.stopReason && result.stopReason !== "error" && result.stopReason !== "aborted") {
|
package/extensions/threads.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface SubagentThread {
|
|
|
23
23
|
status: ThreadStatus;
|
|
24
24
|
result?: SubAgentResult;
|
|
25
25
|
toolCallId?: string;
|
|
26
|
+
color?: string;
|
|
26
27
|
createdAt: number;
|
|
27
28
|
updatedAt: number;
|
|
28
29
|
}
|
|
@@ -53,6 +54,7 @@ export class ThreadStore {
|
|
|
53
54
|
task: string;
|
|
54
55
|
mode: ThreadMode;
|
|
55
56
|
toolCallId?: string;
|
|
57
|
+
color?: string;
|
|
56
58
|
}): SubagentThread {
|
|
57
59
|
const id = cryptoGenId();
|
|
58
60
|
const now = Date.now();
|
|
@@ -63,6 +65,7 @@ export class ThreadStore {
|
|
|
63
65
|
mode: params.mode,
|
|
64
66
|
status: "running",
|
|
65
67
|
toolCallId: params.toolCallId,
|
|
68
|
+
color: params.color,
|
|
66
69
|
createdAt: now,
|
|
67
70
|
updatedAt: now,
|
|
68
71
|
};
|
package/package.json
CHANGED