@johnnywu/pi-subagents 2.2.1 → 2.2.2

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,10 @@
1
+ ## [2.2.2](https://github.com/jwu/pi-subagents/compare/v2.2.1...v2.2.2) (2026-09-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * surface subagent assistant-turn failures ([b9a3013](https://github.com/jwu/pi-subagents/commit/b9a3013ef9577fea1f634d9fda7b82c4ad09074d))
7
+
1
8
  ## [2.2.1](https://github.com/jwu/pi-subagents/compare/v2.2.0...v2.2.1) (2026-08-24)
2
9
 
3
10
 
@@ -223,14 +223,27 @@ function textFromMessage(message: unknown): string | undefined {
223
223
  const content = (message as { content?: unknown }).content;
224
224
  if (!Array.isArray(content)) return undefined;
225
225
 
226
+ const texts: string[] = [];
226
227
  for (const part of content) {
227
228
  if (part && typeof part === 'object' && (part as { type?: unknown }).type === 'text') {
228
229
  const text = (part as { text?: unknown }).text;
229
- if (typeof text === 'string') return text;
230
+ if (typeof text === 'string') texts.push(text);
230
231
  }
231
232
  }
232
233
 
233
- return undefined;
234
+ return texts.length > 0 ? texts.join('\n') : undefined;
235
+ }
236
+
237
+ function isAssistantMessage(message: unknown): boolean {
238
+ return (
239
+ !!message && typeof message === 'object' && (message as { role?: unknown }).role === 'assistant'
240
+ );
241
+ }
242
+
243
+ function stringMessageProperty(message: unknown, property: 'stopReason' | 'errorMessage') {
244
+ if (!message || typeof message !== 'object') return undefined;
245
+ const value = (message as Record<string, unknown>)[property];
246
+ return typeof value === 'string' ? value : undefined;
234
247
  }
235
248
 
236
249
  type ContextWindowLookup = {
@@ -322,18 +335,18 @@ function usageFromMessages(
322
335
  return sawUsage ? aggregate : undefined;
323
336
  }
324
337
 
325
- function lastAssistantModel(messages: unknown): string | undefined {
338
+ function lastAssistantMessage(messages: unknown): unknown | undefined {
326
339
  if (!Array.isArray(messages)) return undefined;
327
340
  for (let index = messages.length - 1; index >= 0; index--) {
328
- const message = messages[index];
329
- if (!message || typeof message !== 'object') continue;
330
- if ((message as { role?: unknown }).role !== 'assistant') continue;
331
- const model = modelFromMessage(message);
332
- if (model) return model;
341
+ if (isAssistantMessage(messages[index])) return messages[index];
333
342
  }
334
343
  return undefined;
335
344
  }
336
345
 
346
+ function lastAssistantModel(messages: unknown): string | undefined {
347
+ return modelFromMessage(lastAssistantMessage(messages));
348
+ }
349
+
337
350
  function buildTaskArgument(task: string, taskFilePath: string | undefined): string {
338
351
  return taskFilePath ? `Task: @${taskFilePath}` : `Task: ${task}`;
339
352
  }
@@ -453,6 +466,8 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
453
466
  let stderr = '';
454
467
  let model = options.agent.model;
455
468
  let stdoutBuffer = '';
469
+ let finalAssistantStopReason: string | undefined;
470
+ let finalAssistantErrorMessage: string | undefined;
456
471
 
457
472
  const progress = (status: AgentProgress['status']): AgentProgress => ({
458
473
  agent: options.agent.name,
@@ -469,6 +484,13 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
469
484
  const emit = (status: AgentProgress['status'] = 'running') =>
470
485
  options.onProgress?.(progress(status));
471
486
 
487
+ const setFinalAssistantOutput = (message: unknown) => {
488
+ // 最后一条 assistant 消息才是权威的最终输出,不能让工具结果占位符或旧文本伪装为结果。
489
+ output = textFromMessage(message) ?? '';
490
+ finalAssistantStopReason = stringMessageProperty(message, 'stopReason');
491
+ finalAssistantErrorMessage = stringMessageProperty(message, 'errorMessage');
492
+ };
493
+
472
494
  try {
473
495
  const promptFilePath = path.join(tempDir, 'system-prompt.md');
474
496
 
@@ -578,8 +600,9 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
578
600
  }
579
601
 
580
602
  if (event.type === 'message_end' && event.message) {
581
- const text = textFromMessage(event.message);
582
- if (text !== undefined) output = text;
603
+ if (!isAssistantMessage(event.message)) return;
604
+
605
+ setFinalAssistantOutput(event.message);
583
606
  updateUsage(usage, usageFromMessage(event.message, modelRegistry));
584
607
  model = modelFromMessage(event.message) ?? model;
585
608
  emit();
@@ -589,6 +612,8 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
589
612
  if (event.type === 'agent_end') {
590
613
  const aggregate = usageFromMessages(event.messages, modelRegistry);
591
614
  if (aggregate) replaceUsage(usage, aggregate);
615
+ const finalAssistantMessage = lastAssistantMessage(event.messages);
616
+ if (finalAssistantMessage) setFinalAssistantOutput(finalAssistantMessage);
592
617
  model = lastAssistantModel(event.messages) ?? model;
593
618
  emit();
594
619
  }
@@ -616,8 +641,22 @@ export async function runSubagent(options: RunSubagentOptions): Promise<AgentRes
616
641
  );
617
642
 
618
643
  if (stdoutBuffer.trim()) processLine(stdoutBuffer);
619
- const isError = exit.exitCode !== 0;
620
- if (isError && !output) output = stderr || `Subagent exited with code ${exit.exitCode}`;
644
+ const failedStopReason =
645
+ finalAssistantStopReason === 'error' || finalAssistantStopReason === 'aborted';
646
+ const hasFinalOutput = output.trim().length > 0;
647
+ const isError = exit.exitCode !== 0 || failedStopReason || !hasFinalOutput;
648
+
649
+ if (failedStopReason && finalAssistantErrorMessage) {
650
+ output = finalAssistantErrorMessage;
651
+ } else if (isError && !hasFinalOutput) {
652
+ output =
653
+ stderr ||
654
+ (failedStopReason
655
+ ? `Subagent stopped with reason: ${finalAssistantStopReason}`
656
+ : exit.exitCode !== 0
657
+ ? `Subagent exited with code ${exit.exitCode}`
658
+ : 'Subagent produced no final text output.');
659
+ }
621
660
 
622
661
  const truncated = truncateHeadContent(output, OUTPUT_MAX_BYTES, OUTPUT_MAX_LINES);
623
662
  if (truncated !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnnywu/pi-subagents",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "Sub-agents extension for pi coding agent.",
5
5
  "homepage": "https://github.com/jwu/pi-subagents#readme",
6
6
  "repository": {