pi-subagents 0.9.2 → 0.10.0

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/utils.ts CHANGED
@@ -186,18 +186,29 @@ export function getDisplayItems(messages: Message[]): DisplayItem[] {
186
186
  * Detect errors in subagent execution from messages (only errors with no subsequent success)
187
187
  */
188
188
  export function detectSubagentError(messages: Message[]): ErrorInfo {
189
- let lastSuccessfulToolIndex = -1;
189
+ // Step 1: Find the last assistant message with text content.
190
+ // If the agent produced a text response after encountering errors,
191
+ // it had a chance to recover — only errors AFTER this point matter.
192
+ let lastAssistantTextIndex = -1;
190
193
  for (let i = messages.length - 1; i >= 0; i--) {
191
194
  const msg = messages[i];
192
- if (msg.role === "toolResult" && !(msg as any).isError) {
193
- lastSuccessfulToolIndex = i;
194
- break;
195
+ if (msg.role === "assistant") {
196
+ const hasText = Array.isArray(msg.content) && msg.content.some(
197
+ (c) => c.type === "text" && "text" in c && (c.text as string).trim().length > 0,
198
+ );
199
+ if (hasText) {
200
+ lastAssistantTextIndex = i;
201
+ break;
202
+ }
195
203
  }
196
204
  }
197
205
 
198
- for (let i = messages.length - 1; i >= 0; i--) {
199
- if (i < lastSuccessfulToolIndex) break;
206
+ // Step 2: Only scan tool results AFTER the last assistant text message.
207
+ // Errors before the agent's final response are implicitly recovered.
208
+ const scanStart = lastAssistantTextIndex >= 0 ? lastAssistantTextIndex + 1 : 0;
200
209
 
210
+ // Step 3: Check tool results in the post-response window
211
+ for (let i = messages.length - 1; i >= scanStart; i--) {
201
212
  const msg = messages[i];
202
213
  if (msg.role !== "toolResult") continue;
203
214
 
@@ -228,6 +239,9 @@ export function detectSubagentError(messages: Message[]): ErrorInfo {
228
239
  }
229
240
  }
230
241
 
242
+ // NOTE: These patterns can match legitimate output (grep results, logs,
243
+ // testing). With the assistant-message check above, most false positives
244
+ // are mitigated since the agent will have responded after routine errors.
231
245
  const fatalPatterns = [
232
246
  /command not found/i,
233
247
  /permission denied/i,
@@ -319,6 +333,8 @@ export async function mapConcurrent<T, R>(
319
333
  limit: number,
320
334
  fn: (item: T, i: number) => Promise<R>,
321
335
  ): Promise<R[]> {
336
+ // Clamp to at least 1; NaN/undefined/0/negative all become 1
337
+ const safeLimit = Math.max(1, Math.floor(limit) || 1);
322
338
  const results: R[] = new Array(items.length);
323
339
  let next = 0;
324
340
 
@@ -329,7 +345,7 @@ export async function mapConcurrent<T, R>(
329
345
  }
330
346
  }
331
347
 
332
- const workers = Array.from({ length: Math.min(limit, items.length) }, () => worker());
348
+ const workers = Array.from({ length: Math.min(safeLimit, items.length) }, () => worker());
333
349
  await Promise.all(workers);
334
350
  return results;
335
351
  }