@johnnywu/pi-subagents 2.2.2 → 2.2.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [2.2.3](https://github.com/jwu/pi-subagents/compare/v2.2.2...v2.2.3) (2026-09-18)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * keep subagent warnings in tool details ([2168ab0](https://github.com/jwu/pi-subagents/commit/2168ab010109af3119780dc28a110dbc7baa12c9))
7
+ * throttle concurrent subagent progress updates ([46807f5](https://github.com/jwu/pi-subagents/commit/46807f5f3f2d15dab7f0d79fa4710ecf3f6cf572))
8
+
1
9
  ## [2.2.2](https://github.com/jwu/pi-subagents/compare/v2.2.1...v2.2.2) (2026-09-04)
2
10
 
3
11
 
@@ -49,6 +49,8 @@ export interface AgentProgress {
49
49
  elapsedMs: number;
50
50
  model?: string;
51
51
  session?: SubagentSessionInfo;
52
+ /** 运行准备阶段的非致命提示,由工具渲染器展示,不能直接写入宿主 TUI。 */
53
+ warnings?: string[];
52
54
  }
53
55
 
54
56
  export interface AgentResult extends AgentProgress {
@@ -468,6 +470,7 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
468
470
  let stdoutBuffer = '';
469
471
  let finalAssistantStopReason: string | undefined;
470
472
  let finalAssistantErrorMessage: string | undefined;
473
+ const warnings: string[] = [];
471
474
 
472
475
  const progress = (status: AgentProgress['status']): AgentProgress => ({
473
476
  agent: options.agent.name,
@@ -479,6 +482,7 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
479
482
  elapsedMs: now() - startedAt,
480
483
  model,
481
484
  session: options.session ?? { requested: 'none', effective: 'none' },
485
+ ...(warnings.length > 0 ? { warnings: [...warnings] } : {}),
482
486
  });
483
487
 
484
488
  const emit = (status: AgentProgress['status'] = 'running') =>
@@ -499,15 +503,15 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
499
503
  cwd: options.cwd,
500
504
  agentDir: options.agentDir,
501
505
  });
502
- for (const source of promptResult.skippedSkillPackages) {
503
- console.warn(`[pi-subagents] package not installed, skipping skills: ${source}`);
504
- }
505
- for (const warning of promptResult.skillWarnings) {
506
- console.warn(`[pi-subagents] ${warning}`);
507
- }
508
- for (const name of promptResult.missingSkills) {
509
- console.warn(`[pi-subagents] skill not found: ${name}`);
510
- }
506
+ // 子代理可能在 fullscreen 工具视图中运行。直接 console.warn 会绕过工具渲染,
507
+ // 污染宿主的聊天输入区域;把非致命提示附到工具进度中交给渲染器显示。
508
+ warnings.push(
509
+ ...promptResult.skippedSkillPackages.map(
510
+ (source) => `package not installed, skipping skills: ${source}`,
511
+ ),
512
+ ...promptResult.skillWarnings,
513
+ ...promptResult.missingSkills.map((name) => `skill not found: ${name}`),
514
+ );
511
515
  await fileSystem.writeFile(promptFilePath, promptResult.prompt);
512
516
 
513
517
  let taskFilePath: string | undefined;
@@ -208,6 +208,11 @@ export function formatSubagentResultLines(
208
208
  ...(progress.session?.warning
209
209
  ? [{ text: `session: ${progress.session.warning}`, kind: 'hint' as const, singleLine: true }]
210
210
  : []),
211
+ ...(progress.warnings ?? []).map((warning) => ({
212
+ text: `warning: ${warning}`,
213
+ kind: 'hint' as const,
214
+ singleLine: true,
215
+ })),
211
216
  ...toolLines,
212
217
  ];
213
218
 
@@ -88,6 +88,60 @@ function toProgressResult(progress: AgentProgress) {
88
88
  };
89
89
  }
90
90
 
91
+ const PROGRESS_UPDATE_INTERVAL_MS = 100;
92
+
93
+ type PendingProgressUpdate = {
94
+ progress: AgentProgress;
95
+ onUpdate: AgentToolUpdateCallback<AgentProgress>;
96
+ };
97
+
98
+ /**
99
+ * 多个独立 subagent 工具调用共享同一批进度更新窗口,避免每个子进程事件都触发 TUI 重绘。
100
+ * 每个工具调用只保留最新快照;终态更新不等待窗口,确保完成状态即时可见。
101
+ */
102
+ class ProgressUpdateCoordinator {
103
+ private pending = new Map<string, PendingProgressUpdate>();
104
+ private timer: ReturnType<typeof setTimeout> | undefined;
105
+
106
+ queue(
107
+ toolCallId: string,
108
+ progress: AgentProgress,
109
+ onUpdate?: AgentToolUpdateCallback<AgentProgress>,
110
+ ) {
111
+ if (!onUpdate) return;
112
+ this.pending.set(toolCallId, { progress, onUpdate });
113
+ if (!this.timer) this.timer = setTimeout(() => this.flush(), PROGRESS_UPDATE_INTERVAL_MS);
114
+ }
115
+
116
+ publishTerminal(
117
+ toolCallId: string,
118
+ progress: AgentProgress,
119
+ onUpdate?: AgentToolUpdateCallback<AgentProgress>,
120
+ ) {
121
+ this.pending.delete(toolCallId);
122
+ this.clearTimerWhenIdle();
123
+ onUpdate?.(toProgressResult(progress));
124
+ }
125
+
126
+ forget(toolCallId: string) {
127
+ this.pending.delete(toolCallId);
128
+ this.clearTimerWhenIdle();
129
+ }
130
+
131
+ private flush() {
132
+ this.timer = undefined;
133
+ const updates = [...this.pending.values()];
134
+ this.pending.clear();
135
+ for (const { progress, onUpdate } of updates) onUpdate(toProgressResult(progress));
136
+ }
137
+
138
+ private clearTimerWhenIdle() {
139
+ if (this.pending.size > 0 || !this.timer) return;
140
+ clearTimeout(this.timer);
141
+ this.timer = undefined;
142
+ }
143
+ }
144
+
91
145
  function freshSessionInfo(requested: SubagentSessionMode, warning?: string): SubagentSessionInfo {
92
146
  return {
93
147
  requested,
@@ -308,6 +362,7 @@ export function registerSubagentTool(
308
362
  ? options.agents.filter((candidate) => allowed.has(candidate.name))
309
363
  : options.agents;
310
364
  const runner = options.run ?? runSubagent;
365
+ const progressCoordinator = new ProgressUpdateCoordinator();
311
366
 
312
367
  const availableSubagents = agents.map((agent) => agent.name);
313
368
  const agentNames = [...availableSubagents].sort().join(', ');
@@ -340,19 +395,30 @@ export function registerSubagentTool(
340
395
 
341
396
  const childCwd = params.cwd ?? ctx.cwd;
342
397
  const session = resolveSubagentSession(params.session, childCwd, ctx, options.agentDir);
343
- const result = await runner({
344
- agent,
345
- task: params.task,
346
- cwd: childCwd,
347
- signal,
348
- depth: Number(env.PI_SUBAGENT_DEPTH ?? '0') + 1,
349
- availableAgents: availableSubagentsForAgent(agent, availableSubagents),
350
- agentDir: options.agentDir,
351
- session,
352
- onProgress: (progress) => onUpdate?.(toProgressResult(progress)),
353
- });
354
-
355
- return toToolResult(result);
398
+ try {
399
+ const result = await runner({
400
+ agent,
401
+ task: params.task,
402
+ cwd: childCwd,
403
+ signal,
404
+ depth: Number(env.PI_SUBAGENT_DEPTH ?? '0') + 1,
405
+ availableAgents: availableSubagentsForAgent(agent, availableSubagents),
406
+ agentDir: options.agentDir,
407
+ session,
408
+ onProgress: (progress) => {
409
+ if (progress.status === 'running') {
410
+ progressCoordinator.queue(_toolCallId, progress, onUpdate);
411
+ } else {
412
+ progressCoordinator.publishTerminal(_toolCallId, progress, onUpdate);
413
+ }
414
+ },
415
+ });
416
+
417
+ return toToolResult(result);
418
+ } finally {
419
+ // Pi 会忽略工具 promise 结算后的 onUpdate;丢弃滞留快照以避免无效的延迟回调。
420
+ progressCoordinator.forget(_toolCallId);
421
+ }
356
422
  },
357
423
 
358
424
  renderCall(args, theme, context) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnnywu/pi-subagents",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Sub-agents extension for pi coding agent.",
5
5
  "homepage": "https://github.com/jwu/pi-subagents#readme",
6
6
  "repository": {