@threadplane/langgraph 0.0.60 → 0.0.61

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.
@@ -361,14 +361,28 @@ class SubagentTracker {
361
361
  return establish(toolCallId);
362
362
  }
363
363
  }
364
- // Last-resort fallback — tool children only. A subgraph child is keyed by
365
- // its own namespace and must never absorb an unrelated child's events.
366
- for (const [toolCallId, subagent] of this.subagents) {
367
- if (subagent.kind !== 'tool')
368
- continue;
369
- if (!mapped.has(toolCallId) && (subagent.status === 'pending' || subagent.status === 'running')) {
370
- return establish(toolCallId);
371
- }
364
+ // Last-resort fallback — tool children only, and only when there is
365
+ // nothing to guess between.
366
+ //
367
+ // LangGraph's `tools:<uuid>` namespace is a checkpoint id assigned
368
+ // independently of the parent's `call_*` tool-call id; the two are not
369
+ // linked anywhere on the wire (verified against a live run). So when a
370
+ // delegation tool carries no matchable description, position is the only
371
+ // signal left.
372
+ //
373
+ // That is sound with exactly one outstanding child — the shape every graph
374
+ // in this repo produces, since each dispatches one tool call per assistant
375
+ // turn. With several outstanding at once (parallel fan-out) arrival order
376
+ // is NOT dispatch order, and claiming the first unmapped call cross-wires
377
+ // the children: one card renders another's output. Leaving the stream
378
+ // unattributed keeps its messages buffered instead, so an empty card is
379
+ // the worst case rather than a confidently wrong one. It can still resolve
380
+ // later: as siblings complete, the candidate set shrinks back to one.
381
+ const candidates = [...this.subagents].filter(([toolCallId, subagent]) => subagent.kind === 'tool' &&
382
+ !mapped.has(toolCallId) &&
383
+ (subagent.status === 'pending' || subagent.status === 'running'));
384
+ if (candidates.length === 1) {
385
+ return establish(candidates[0][0]);
372
386
  }
373
387
  if (description) {
374
388
  this.pendingMatches.set(namespaceId, description);