@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
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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) {
|