@arhen/pi-core-subagent 1.3.17 → 1.3.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arhen/pi-core-subagent",
3
- "version": "1.3.17",
3
+ "version": "1.3.18",
4
4
  "type": "module",
5
5
  "description": "pi extension: fast in-process subagents with a dependency-graph scheduler (needs edges gate tasks and carry upstream output into dependent prompts), plus background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
6
6
  "license": "MIT",
package/src/format.ts CHANGED
@@ -90,7 +90,10 @@ export function themedTaskLine(task: TaskSnapshot, theme: Theme, activity = ""):
90
90
  if (TERMINAL.includes(task.status)) {
91
91
  return theme.fg("dim", `${statusIcon(task.status)} ${task.agent} · ${tail}`);
92
92
  }
93
- return `${statusIcon(task.status)} ${task.agent} · ${gate}${activity}${colorNums(tail, theme)}`;
93
+ // Talking (mailbox/intercom tool in flight): pulse the name accent↔dim; normal otherwise.
94
+ pulsePhase += 1;
95
+ const name = isTalking(task) ? theme.fg(pulsePhase % 2 === 0 ? "accent" : "dim", `${task.agent}⇄`) : task.agent;
96
+ return `${statusIcon(task.status)} ${name} · ${gate}${activity}${colorNums(tail, theme)}`;
94
97
  }
95
98
  /**
96
99
  * Human-readable activity line: "Read src/index.ts", "Grep wrapSingleLine".
@@ -127,6 +130,14 @@ export function activitySnippet(text: string): string {
127
130
  const flat = text.replace(/\s+/g, " ").trim();
128
131
  return flat.length > 90 ? `${flat.slice(0, 90)}…` : flat;
129
132
  }
133
+
134
+ /** Mailbox/intercom tools — while one is the task's last activity, the agent is "talking". */
135
+ export const TALK_TOOLS = ["poll_agent_messages", "send_agent_message", "ask_parent", "notify_parent"];
136
+ export function isTalking(task: TaskSnapshot): boolean {
137
+ const a = task.lastActivity?.toLowerCase() ?? "";
138
+ return TALK_TOOLS.some((t) => a.startsWith(t));
139
+ }
140
+ let pulsePhase = 0; // flips per render; talking agents alternate between the two name styles
130
141
  /** Static compact lines (tool-result stream, subagent_status, /subagents). */
131
142
  export function compactLines(run: RunSnapshot): string[] {
132
143
  const lines: string[] = [];
package/src/manager.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  activitySnippet,
22
22
  describeCall,
23
23
  getFirstText,
24
+ isTalking,
24
25
  makeNotice,
25
26
  makeTaskNotice,
26
27
  SubagentsWidget,
@@ -288,6 +289,10 @@ export class SubagentManager {
288
289
  this.runControllers.clear();
289
290
  this.mailboxes = createMailbox();
290
291
  this.widgetTui = null; // force re-registration on the next session
292
+ if (this.pulseTimer) {
293
+ clearTimeout(this.pulseTimer);
294
+ this.pulseTimer = null;
295
+ }
291
296
  for (const t of this.widgetTimers.values()) clearTimeout(t);
292
297
  this.widgetTimers.clear();
293
298
  this.widgetRuns = [];
@@ -406,9 +411,23 @@ export class SubagentManager {
406
411
  this.ensureWidget(ctx);
407
412
  this.widgetTui?.requestRender();
408
413
  }
414
+ this.maybePulse(ctx);
409
415
  }, WIDGET_THROTTLE_MS),
410
416
  );
411
417
  }
418
+
419
+ /** While any live task's last activity is a talk tool, keep re-rendering so its name pulses. */
420
+ private pulseTimer: ReturnType<typeof setTimeout> | null = null;
421
+ private maybePulse(ctx?: ExtensionContext): void {
422
+ if (this.pulseTimer || !this.widgetTui) return;
423
+ const talking = this.widgetRuns.some((r) => r.tasks.some(isTalking));
424
+ if (!talking) return; // last tick stops the loop: talking→normal resumes instantly
425
+ this.pulseTimer = setTimeout(() => {
426
+ this.pulseTimer = null;
427
+ this.widgetTui?.requestRender();
428
+ this.maybePulse(ctx);
429
+ }, 700);
430
+ }
412
431
  private flushWidget(run: RunSnapshot | undefined, ctx?: ExtensionContext, onUpdate?: (partial: any) => void): void {
413
432
  if (run) {
414
433
  const t = this.widgetTimers.get(run.id);
@@ -422,6 +441,7 @@ export class SubagentManager {
422
441
  this.ensureWidget(ctx);
423
442
  this.widgetTui?.requestRender();
424
443
  }
444
+ this.maybePulse(ctx);
425
445
  // Transcript gets one status line only — the live per-task view is the widget's job.
426
446
  onUpdate?.({
427
447
  content: [