@arhen/pi-core-subagent 1.3.17 → 1.3.19

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 CHANGED
@@ -259,6 +259,8 @@ Background (default) + intercom — the run returns a runId immediately; you sta
259
259
  | `send_agent_message` | message to a sibling subagent's mailbox (`to` = its task id, or `"leader"`) |
260
260
  | `poll_agent_messages` | drain this subagent's mailbox |
261
261
 
262
+ > **Intercom anti-deadlock:** children are told to never block indefinitely on intercom replies — `ask_parent` keeps the stall watchdog fed while awaiting a parent reply (background runs), and sibling polls are capped (~5 tries) with a proceed-with-best-judgment fallback. Gated siblings (later waves) may not be running yet — waiting on them is the top stall cause, so children are instructed not to.
263
+
262
264
  ## Commands
263
265
 
264
266
  - `/subagents` — list runs; `/subagents peek` (or `ctrl+shift+a`) — browsable pane
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.19",
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: [
@@ -673,7 +693,7 @@ export class SubagentManager {
673
693
  const key = `${run.id}:${task.id}`;
674
694
  try {
675
695
  const subagentInstruction = run.allowIntercom
676
- ? `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer. You MAY use ask_parent only when truly blocked on information only the parent has; notify_parent for one-way updates; send_agent_message/poll_agent_messages to coordinate with siblings. Your mailbox address and siblings: ${task.roster ?? "(none)"}. Use the exact task ids (e.g. task_2) as send_agent_message targets.`
696
+ ? `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer. You MAY use ask_parent only when truly blocked on information only the parent has; notify_parent for one-way updates; send_agent_message/poll_agent_messages to coordinate with siblings. Your mailbox address and siblings: ${task.roster ?? "(none)"}. Use the exact task ids (e.g. task_2) as send_agent_message targets. Siblings run independently and may start late or finish early — never block indefinitely on their replies: poll at most 5 times, then proceed with your best judgment. A gated sibling (marked ↳ waits in the graph) may not be running yet; do not wait for it. Stalled waits get the whole run killed.`
677
697
  : `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer for the parent agent.`;
678
698
 
679
699
  const loader = new DefaultResourceLoader({