pi-cc-extensions 1.0.9 → 1.0.10

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.
@@ -7,7 +7,7 @@ import {
7
7
  type Component,
8
8
  } from "@earendil-works/pi-tui";
9
9
  import { config } from "../../config/config.ts";
10
- import { aggregateToolCardBgSlot, toolCardStatus } from "./tool-state.ts";
10
+ import { TOOL_CARD_BG_SLOT, toolCardStatus } from "./tool-state.ts";
11
11
  import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "../../utils/tool-loading-icon.ts";
12
12
  import {
13
13
  ASK_USER_QUESTION_TOOL_NAME,
@@ -36,6 +36,11 @@ import {
36
36
  } from "./names.ts";
37
37
  import { formatToolDuration, headTruncateToWidth, toolViewportWidth } from "./result.ts";
38
38
  import { isLiveToolCall } from "./tool-state.ts";
39
+ import {
40
+ AGENT_TOOL_NAME,
41
+ isSubagentNotificationComponent,
42
+ subagentNotificationItems,
43
+ } from "./subagent-notification.ts";
39
44
  import {
40
45
  patchRegistry,
41
46
  TOOL_GROUPING_GENERATION_KEY as GENERATION_KEY,
@@ -76,10 +81,15 @@ function toolTitle(tool: any): string {
76
81
  );
77
82
  }
78
83
 
79
- function isGroupable(value: unknown): boolean {
84
+ function isGroupableTool(value: unknown): boolean {
80
85
  return value instanceof ToolExecutionComponent && !NON_GROUPABLE.has(toolName(value));
81
86
  }
82
87
 
88
+ /** 可分组项:工具,或子代理完成通知(只是展示项,不是工具执行)。 */
89
+ function isGroupable(value: unknown): boolean {
90
+ return isGroupableTool(value) || isSubagentNotificationComponent(value);
91
+ }
92
+
83
93
  function isIgnorable(value: unknown): boolean {
84
94
  if (value instanceof Spacer) return true;
85
95
  if (!(value instanceof AssistantMessageComponent)) return false;
@@ -103,7 +113,36 @@ function previousSibling(
103
113
  return undefined;
104
114
  }
105
115
 
106
- type ToolStatus = "pending" | "success" | "error";
116
+ export type ToolStatus = "pending" | "success" | "error";
117
+
118
+ type Fg = (color: string, text: string) => string;
119
+
120
+ function themeFg(theme: any): Fg {
121
+ return (color, text) => theme?.fg?.(color, text) ?? text;
122
+ }
123
+
124
+ /**
125
+ * 分组里的一行显示项:工具一项;子代理完成通知按 agent 各一项。
126
+ * 分组只管计数、树形缩进与展开状态转发,每项自己负责摘要与展开内容。
127
+ */
128
+ export type GroupDisplayItem = {
129
+ /** 组头判定「是否同一种」用的身份:工具原名,或通知类型。 */
130
+ name: string;
131
+ /** 组头工具列表与子行用的显示名。 */
132
+ label: string;
133
+ status: ToolStatus;
134
+ /** 折叠行正文(接在树枝与状态图标之后),width 为剩余可用宽度。 */
135
+ summary(width: number, theme: any): string;
136
+ /**
137
+ * 展开行:首行接在树枝与状态图标之后,其余作为续行。width 为面板内宽(含树枝前缀)。
138
+ * 输入不变时返回同一数组:分组展开缓存按引用比对。
139
+ */
140
+ expandedRows(width: number, theme: any): string[];
141
+ /** 展开态里这些行对应的内部卡片;没有时行归分组本身(点击收起整组)。 */
142
+ hitTarget?: unknown;
143
+ /** 丢掉该项自己缓存的行(换主题后颜色要重算);分组 invalidate 时转发。 */
144
+ invalidate?(): void;
145
+ };
107
146
 
108
147
  function status(tool: any): ToolStatus {
109
148
  return toolCardStatus(tool);
@@ -275,29 +314,86 @@ function shellResultTail(
275
314
  return duration ? ` ${fg("dim", `· ${duration}`)}` : "";
276
315
  }
277
316
 
278
- /** 分组折叠态的一行子工具:`├/└` + 状态图标 + 与组内同一套摘要。 */
279
- function collapsedGroupToolLine(
280
- tool: any,
317
+ /** 子工具折叠行正文:与组内同一套摘要。 */
318
+ function toolSummaryText(tool: any, toolStatus: ToolStatus, width: number, fg: Fg): string {
319
+ const summary = askUserResultSummary(tool) ?? codemodeResultSummary(tool) ?? toolSummary(tool);
320
+ const detail = fg("dim", summary.detail);
321
+ const result = agentResultTail(tool, fg) + shellResultTail(tool, fg, toolStatus);
322
+ const mainWidth = Math.max(0, width - visibleWidth(detail) - visibleWidth(result));
323
+ return `${renderToolSummary(summary, mainWidth, fg)}${detail}${result}`;
324
+ }
325
+
326
+ /**
327
+ * Agent 的折叠行尾巴:`-> Running in background · ID: xxx`。参考 ask_user_question 的
328
+ * 问题 -> 回答写法:跑后台的结果状态放在 payload 后,比单独的 ↳ 行省一行。
329
+ */
330
+ function agentResultTail(tool: any, fg: Fg): string {
331
+ if (toolName(tool).toLowerCase() !== AGENT_TOOL_NAME.toLowerCase() || !tool?.result) return "";
332
+ const details = tool.result.details;
333
+ if (details?.status !== "background") return "";
334
+ const agentId = details.agentId ? ` ID: ${details.agentId}` : "";
335
+ return ` ${fg("dim", "\u2192")} ${fg("dim", `Running in background${agentId}`)}`;
336
+ }
337
+
338
+ /** 原始 paint → 去掉状态图标与外层缩进后的展开行;paint 引用不变就复用。 */
339
+ const expandedToolRowsCache = new WeakMap<string[], string[]>();
340
+
341
+ function expandedToolRows(tool: any, width: number): string[] {
342
+ const paint = tool.render?.(width);
343
+ if (!Array.isArray(paint)) return [toolSummary(tool).main];
344
+ const cached = expandedToolRowsCache.get(paint);
345
+ if (cached) return cached;
346
+ const rendered = visibleLines(paint);
347
+ if (rendered.length) {
348
+ rendered[0] = stripLeadingStatusIcon(rendered[0])
349
+ .replace(/^ +/, "")
350
+ .replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*) +/, "$1");
351
+ }
352
+ const rows = rendered.length
353
+ ? // 续行只剥外层 Box 的 1 格 left pad,保留 Input/Output 相对缩进
354
+ rendered.map((line, index) => (index === 0 ? line : stripLeadingSpaces(line, 1)))
355
+ : [toolSummary(tool).main];
356
+ expandedToolRowsCache.set(paint, rows);
357
+ return rows;
358
+ }
359
+
360
+ function toolItem(tool: any): GroupDisplayItem {
361
+ const toolStatus = status(tool);
362
+ return {
363
+ name: toolName(tool),
364
+ label: toolTitle(tool),
365
+ status: toolStatus,
366
+ summary: (width, theme) => toolSummaryText(tool, toolStatus, width, themeFg(theme)),
367
+ expandedRows: (width) => expandedToolRows(tool, width),
368
+ hitTarget: tool,
369
+ };
370
+ }
371
+
372
+ function groupItems(children: readonly unknown[]): GroupDisplayItem[] {
373
+ return children.flatMap((child) => subagentNotificationItems(child) ?? [toolItem(child)]);
374
+ }
375
+
376
+ /** 分组折叠态的一行:`├/└` + 状态图标 + 该项摘要。 */
377
+ function collapsedGroupLine(
378
+ item: GroupDisplayItem,
281
379
  last: boolean,
282
- fg: (color: string, text: string) => string,
380
+ theme: any,
283
381
  width: number,
284
382
  ): string {
285
- const toolStatus = status(tool);
286
- const color = toolStatus === "pending" ? "dim" : toolStatus;
287
- const summary = askUserResultSummary(tool) ?? codemodeResultSummary(tool) ?? toolSummary(tool);
288
- const prefix = ` ${fg("dim", last ? "└" : "├")} ${fg(color, statusIcon(toolStatus))} `;
289
- const detail = fg("dim", summary.detail);
290
- const result = shellResultTail(tool, fg, toolStatus);
383
+ const fg = themeFg(theme);
384
+ const color = item.status === "pending" ? "dim" : item.status;
385
+ const prefix = ` ${fg("dim", last ? "└" : "├")} ${fg(color, statusIcon(item.status))} `;
291
386
  const rowWidth = toolViewportWidth(width);
292
- const mainWidth = Math.max(
293
- 0,
294
- rowWidth - visibleWidth(prefix) - visibleWidth(detail) - visibleWidth(result),
295
- );
296
- return truncateToWidth(
297
- `${prefix}${renderToolSummary(summary, mainWidth, fg)}${detail}${result}`,
298
- rowWidth,
299
- "",
300
- );
387
+ const room = Math.max(0, rowWidth - visibleWidth(prefix));
388
+ return truncateToWidth(`${prefix}${item.summary(room, theme)}`, rowWidth, "");
389
+ }
390
+
391
+ /** 展开面板里一项占的行:最后一项不留尾部空行(面板自带底部留白)。 */
392
+ function panelRows(rows: string[], last: boolean): string[] {
393
+ if (!last) return rows;
394
+ let end = rows.length;
395
+ while (end > 1 && !stripAnsi(rows[end - 1]).trim()) end--;
396
+ return end === rows.length ? rows : rows.slice(0, end);
301
397
  }
302
398
 
303
399
  type GroupHeaderCounts = { pending: number; success: number; error: number };
@@ -326,10 +422,9 @@ function groupOverallStatus(counts: GroupHeaderCounts): ToolStatus {
326
422
  * 组头的工具列表:名称用与单组标题、子行同一套人性化写法,次数一律带上(各跑一次也报 ×1)。
327
423
  * 同名(同显示标题)的合并成一项,避免 `MCP call×1, MCP call×1` 这种重复。
328
424
  */
329
- function toolNameList(tools: any[]): string {
425
+ function toolNameList(items: readonly GroupDisplayItem[]): string {
330
426
  const counts = new Map<string, number>();
331
- for (const tool of tools) {
332
- const label = toolTitle(tool);
427
+ for (const { label } of items) {
333
428
  counts.set(label, (counts.get(label) ?? 0) + 1);
334
429
  }
335
430
  return [...counts].map(([label, count]) => `${label}×${count}`).join(", ");
@@ -425,17 +520,18 @@ export class ToolGroupComponent extends Container {
425
520
  childAtRow(localRow: number, width: number): { component: any; row: number } | null {
426
521
  if (!this._expanded || localRow < 2) return null;
427
522
  let offset = 2;
428
- for (const tool of this.children) {
429
- let lines: string[] = [];
523
+ const items = groupItems(this.children);
524
+ for (let index = 0; index < items.length; index++) {
525
+ const item = items[index];
526
+ let lineCount = 1;
430
527
  try {
431
- const rendered = tool.render?.(Math.max(1, width - 2));
432
- if (Array.isArray(rendered)) lines = visibleLines(rendered.map((line) => String(line)));
528
+ const rows = item.expandedRows(Math.max(1, width - 2), this.patch.theme);
529
+ lineCount = Math.max(1, panelRows(rows, index === items.length - 1).length);
433
530
  } catch {
434
- lines = [];
531
+ lineCount = 1;
435
532
  }
436
- const lineCount = Math.max(1, lines.length);
437
533
  if (localRow < offset + lineCount) {
438
- return { component: tool, row: localRow - offset };
534
+ return item.hitTarget ? { component: item.hitTarget, row: localRow - offset } : null;
439
535
  }
440
536
  offset += lineCount;
441
537
  }
@@ -445,6 +541,7 @@ export class ToolGroupComponent extends Container {
445
541
  invalidate(): void {
446
542
  this.clearPaintCache();
447
543
  for (const tool of this.children) tool.invalidate?.();
544
+ for (const item of groupItems(this.children)) item.invalidate?.();
448
545
  }
449
546
 
450
547
  private settledCacheHit(width: number): string[] | undefined {
@@ -466,7 +563,7 @@ export class ToolGroupComponent extends Container {
466
563
  cache.children[i] !== tool ||
467
564
  cache.args[i] !== tool?.args ||
468
565
  cache.results[i] !== tool?.result ||
469
- status(tool) === "pending"
566
+ (!isSubagentNotificationComponent(tool) && status(tool) === "pending")
470
567
  ) {
471
568
  return;
472
569
  }
@@ -491,12 +588,15 @@ export class ToolGroupComponent extends Container {
491
588
  const cached = this.settledCacheHit(width);
492
589
  if (cached) return cached;
493
590
  const theme = this.patch.theme;
494
- const fg = (color: string, text: string) => theme?.fg?.(color, text) ?? text;
591
+ const fg = themeFg(theme);
592
+ const items = groupItems(this.children);
495
593
  const counts = { pending: 0, success: 0, error: 0 };
496
- for (const tool of this.children) counts[status(tool)]++;
594
+ // 组头按行计数:通知的每个 agent 算一行(与工具各一行同一口径,避免
595
+ // 「5 done 其实是 4 工具 + 1 通知行」这类误导)。
596
+ for (const item of items) counts[item.status]++;
497
597
  const countText = groupCountText(counts, fg);
498
- const names = new Set(this.children.map(toolName));
499
- const label = names.size === 1 ? toolTitle(this.children[0]) : "Multiple Tools";
598
+ const names = new Set(items.map((item) => item.name));
599
+ const label = names.size === 1 ? items[0].label : "Multiple Tools";
500
600
  const overall = groupOverallStatus(counts);
501
601
  if ((this.children as any[]).some(isAnimatingTool)) scheduleGroupAnimation(this.patch);
502
602
  const overallColor = overall === "pending" ? "dim" : overall;
@@ -512,76 +612,58 @@ export class ToolGroupComponent extends Container {
512
612
  // 连一个工具名都放不下就不放列表:退化成 `· …` 只会把行撑爆。
513
613
  const nameList =
514
614
  names.size > 1 && listRoom >= 4
515
- ? ` ${fg("dim", `· ${headTruncateToWidth(toolNameList(this.children), listRoom)}`)}`
615
+ ? ` ${fg("dim", `· ${headTruncateToWidth(toolNameList(items), listRoom)}`)}`
516
616
  : "";
517
617
  const lines = ["", truncateToWidth(`${head}${nameList}${tail}`, rowWidth, "")];
518
- const total = this.children.length;
519
- const childPaints = this._expanded
520
- ? (this.children as any[]).map((tool) => tool.render?.(Math.max(1, width - 2)))
521
- : undefined;
522
- if (this._expanded && childPaints) {
523
- const expandedHit = this.expandedPaintCache;
524
- if (
525
- expandedHit &&
526
- expandedHit.width === width &&
527
- expandedHit.hover === this.hintHovered &&
528
- expandedHit.theme === this.patch.theme &&
529
- expandedHit.fullscreen === isToolTuiFullscreen() &&
530
- expandedHit.bgSlot === aggregateToolCardBgSlot(this.children) &&
531
- expandedHit.paints.length === childPaints.length &&
532
- expandedHit.paints.every((paint, index) => paint === childPaints[index])
533
- ) {
534
- return expandedHit.lines;
618
+ const total = items.length;
619
+ if (!this._expanded) {
620
+ for (let index = 0; index < total; index++) {
621
+ lines.push(collapsedGroupLine(items[index], index === total - 1, theme, width));
535
622
  }
623
+ if (counts.pending === 0) this.storeSettledCache(width, lines);
624
+ return lines;
625
+ }
626
+ // 有一个失败整卡用失败底色;没有失败但还有进行中用进行中底色。
627
+ const backgroundSlot = TOOL_CARD_BG_SLOT[overall];
628
+ const itemPaints = items.map((item) => item.expandedRows(Math.max(1, width - 2), theme));
629
+ const expandedHit = this.expandedPaintCache;
630
+ if (
631
+ expandedHit &&
632
+ expandedHit.width === width &&
633
+ expandedHit.hover === this.hintHovered &&
634
+ expandedHit.theme === this.patch.theme &&
635
+ expandedHit.fullscreen === isToolTuiFullscreen() &&
636
+ expandedHit.bgSlot === backgroundSlot &&
637
+ expandedHit.paints.length === itemPaints.length &&
638
+ expandedHit.paints.every((paint, index) => paint === itemPaints[index])
639
+ ) {
640
+ return expandedHit.lines;
536
641
  }
537
- const expandedLines: string[] = [];
538
642
  for (let index = 0; index < total; index++) {
539
- const tool = this.children[index];
540
- const toolStatus = status(tool);
541
- const color = toolStatus === "pending" ? "dim" : toolStatus;
542
- const branch = index === total - 1 ? "└" : "├";
543
- const continuation = index === total - 1 ? " " : "│ ";
544
- if (!this._expanded) {
545
- lines.push(collapsedGroupToolLine(tool, index === total - 1, fg, width));
546
- continue;
547
- }
548
- const rendered = visibleLines(Array.isArray(childPaints?.[index]) ? childPaints[index] : []);
549
- if (rendered.length) {
550
- rendered[0] = stripLeadingStatusIcon(rendered[0])
551
- .replace(/^ +/, "")
552
- .replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*) +/, "$1");
553
- }
554
- const childLines = rendered.length ? rendered : [toolSummary(tool).main];
555
- for (let lineIndex = 0; lineIndex < childLines.length; lineIndex++) {
556
- const content =
557
- // 续行只剥外层 Box 的 1 格 left pad,保留 Input/Output 相对缩进
558
- lineIndex === 0 ? childLines[lineIndex] : stripLeadingSpaces(childLines[lineIndex], 1);
643
+ const item = items[index];
644
+ const color = item.status === "pending" ? "dim" : item.status;
645
+ const last = index === total - 1;
646
+ const branch = last ? "└" : "├";
647
+ const continuation = last ? " " : "│ ";
648
+ const rows = panelRows(itemPaints[index], last);
649
+ for (let lineIndex = 0; lineIndex < rows.length; lineIndex++) {
559
650
  const prefix =
560
651
  lineIndex === 0
561
- ? `${fg("dim", branch)} ${fg(color, statusIcon(toolStatus))} `
652
+ ? `${fg("dim", branch)} ${fg(color, statusIcon(item.status))} `
562
653
  : fg("dim", continuation);
563
- expandedLines.push(prefix + content);
654
+ lines.push(paddedBackgroundRow(theme, backgroundSlot, prefix + rows[lineIndex], width));
564
655
  }
565
656
  }
566
- if (this._expanded) {
567
- // 有一个失败整卡用失败底色;没有失败但还有进行中用进行中底色。
568
- const backgroundSlot = aggregateToolCardBgSlot(this.children);
569
- for (const line of expandedLines) {
570
- lines.push(paddedBackgroundRow(theme, backgroundSlot, line, width));
571
- }
572
- lines.push(paddedBackgroundRow(theme, backgroundSlot, "", width));
573
- this.expandedPaintCache = {
574
- width,
575
- hover: this.hintHovered,
576
- theme: this.patch.theme,
577
- fullscreen: isToolTuiFullscreen(),
578
- bgSlot: backgroundSlot,
579
- paints: childPaints ?? [],
580
- lines,
581
- };
582
- } else if (counts.pending === 0) {
583
- this.storeSettledCache(width, lines);
584
- }
657
+ lines.push(paddedBackgroundRow(theme, backgroundSlot, "", width));
658
+ this.expandedPaintCache = {
659
+ width,
660
+ hover: this.hintHovered,
661
+ theme: this.patch.theme,
662
+ fullscreen: isToolTuiFullscreen(),
663
+ bgSlot: backgroundSlot,
664
+ paints: itemPaints,
665
+ lines,
666
+ };
585
667
  return lines;
586
668
  }
587
669
  }
@@ -605,8 +687,9 @@ function ungroup(patch: Patch): void {
605
687
  }
606
688
  }
607
689
 
690
+ /** 至少两项展示内容就保留分组:一个通知组件也可能包含多个 agent。 */
608
691
  function normalizeGroup(patch: Patch, group: ToolGroupComponent): void {
609
- if (group.children.length > 1) return;
692
+ if (groupItems(group.children).length > 1) return;
610
693
  const parent = (group as any)[PARENT_KEY];
611
694
  const index = parent?.children?.indexOf(group) ?? -1;
612
695
  const tools = group.releaseTools();
@@ -615,12 +698,8 @@ function normalizeGroup(patch: Patch, group: ToolGroupComponent): void {
615
698
  for (const tool of tools) delete tool[PARENT_KEY];
616
699
  return;
617
700
  }
618
- if (tools.length === 1) {
619
- tools[0][PARENT_KEY] = parent;
620
- parent.children.splice(index, 1, tools[0]);
621
- } else {
622
- parent.children.splice(index, 1);
623
- }
701
+ for (const tool of tools) tool[PARENT_KEY] = parent;
702
+ parent.children.splice(index, 1, ...tools);
624
703
  }
625
704
 
626
705
  function maybeGroup(patch: Patch, parent: any, component: any): void {
@@ -636,19 +715,25 @@ function maybeGroup(patch: Patch, parent: any, component: any): void {
636
715
  if (!Array.isArray(children)) return;
637
716
  const index = children.indexOf(component);
638
717
  const prior = previousSibling(children, index - 1);
639
- if (!prior) return;
640
- if (prior.child instanceof ToolGroupComponent && (prior.child as any).patch === patch) {
718
+ if (prior?.child instanceof ToolGroupComponent && (prior.child as any).patch === patch) {
641
719
  children.splice(index, 1);
642
720
  prior.child.addTool(component);
643
721
  return;
644
722
  }
645
- if (!isGroupable(prior.child) || prior.child[GENERATION_KEY] !== patch.generation) return;
723
+ const members = [component];
724
+ let firstIndex = index;
725
+ if (prior && isGroupable(prior.child) && prior.child[GENERATION_KEY] === patch.generation) {
726
+ members.unshift(prior.child);
727
+ firstIndex = prior.index;
728
+ }
729
+ // 按展示项而非消息数决定:单条批量通知也成组,原消息仍完整地留在组内。
730
+ if (groupItems(members).length < 2) return;
646
731
  const group = new ToolGroupComponent(patch);
647
- group.addTool(prior.child);
648
- group.addTool(component);
732
+ for (const member of members) group.addTool(member);
649
733
  (group as any)[PARENT_KEY] = parent;
650
- children[prior.index] = group;
651
- children.splice(index, 1);
734
+ // 两个相邻组件合并时移走后者;夹在中间的空行留在分组之后。
735
+ if (firstIndex !== index) children.splice(index, 1);
736
+ children[firstIndex] = group;
652
737
  }
653
738
 
654
739
  /** /reload 不会重新 addChild;扫描当前 mounted roots,把已有工具重新送入同一分组规则。 */
@@ -23,6 +23,9 @@ const TOOL_LABEL_OVERRIDES: Readonly<Record<string, string>> = {
23
23
  // MCP 入口两个没有具体工具名,跟普通工具一样人性化;缩写固定大写
24
24
  mcp: "MCP",
25
25
  mcpscript: "MCP Script",
26
+ // pi-subagents 的 Agent 工具标题写 Subagent,与结束通知的 Subagent Finished 叫法一致。
27
+ agent: "Subagent",
28
+ agents: "Subagents",
26
29
  };
27
30
 
28
31
  /**
@@ -264,19 +267,23 @@ export function toolCallSummary(
264
267
  if (variant === "default" && AGENT_FAMILY_TOOL_NAMES.has(toolName) && args.agent_id) {
265
268
  return { main: title, detail: "", payload: clip(args.agent_id) };
266
269
  }
267
- if (variant === "grouping" && (name === "agent" || name === "agents")) {
270
+ if (name === "agent" || name === "agents") {
268
271
  const displayName = args.subagent_type ?? args.agent_type ?? args.agent;
272
+ const description =
273
+ typeof args.description === "string" ? oneLine(args.description, clipLimit()) : "";
269
274
  if (typeof displayName === "string" && displayName) {
270
- return { main: title, detail: "", payload: displayName };
275
+ // 类型进标题,`·` 分隔后描述作灰色载荷:单卡与组内行同一写法。
276
+ return {
277
+ main: `${title} ${displayName}`,
278
+ detail: "",
279
+ payload: description ? `\u00b7 ${description}` : undefined,
280
+ };
271
281
  }
272
282
  return value(name === "agent" ? "launch agent" : "launch agents", "description", "prompt");
273
283
  }
274
284
  if (variant === "grouping" && (name === "get_subagent_result" || name === "steer_subagent")) {
275
285
  return value(name === "get_subagent_result" ? "agent result" : "steer agent", "agent_id");
276
286
  }
277
- if (variant === "default" && name === "agents") {
278
- return value("launch agents", "description", "prompt");
279
- }
280
287
  if (name === "skill") return value("run skill", "name");
281
288
  if (name === "enterplanmode" || name === "enter_plan_mode") {
282
289
  return { main: title, detail: "", payload: "enable read-only planning" };