@threadplane/langgraph 0.0.58 → 0.0.60
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.
|
@@ -243,6 +243,18 @@ class SubagentTracker {
|
|
|
243
243
|
onSubagentChange;
|
|
244
244
|
subagents = new Map();
|
|
245
245
|
namespaceToToolCallId = new Map();
|
|
246
|
+
/**
|
|
247
|
+
* Child messages received under a namespace that is not yet attributed to a
|
|
248
|
+
* registered subagent. LangGraph's `tools:<id>` namespace carries an internal
|
|
249
|
+
* run UUID, not the parent's tool-call id, and the two are only reconciled
|
|
250
|
+
* once a `values` event arrives carrying the child's first human message. Any
|
|
251
|
+
* chunk streamed before that point would otherwise be dropped — which is what
|
|
252
|
+
* made subagent cards render "0 message(s)" despite a full child transcript.
|
|
253
|
+
*
|
|
254
|
+
* Merged by id like a real transcript, so this stays bounded by the child's
|
|
255
|
+
* distinct message count rather than by chunk volume.
|
|
256
|
+
*/
|
|
257
|
+
unattributedMessages = new Map();
|
|
246
258
|
pendingMatches = new Map();
|
|
247
259
|
constructor(options = {}) {
|
|
248
260
|
this.subagentToolNames = new Set(options.subagentToolNames ?? DEFAULT_SUBAGENT_TOOL_NAMES);
|
|
@@ -252,6 +264,7 @@ class SubagentTracker {
|
|
|
252
264
|
this.subagents.clear();
|
|
253
265
|
this.namespaceToToolCallId.clear();
|
|
254
266
|
this.pendingMatches.clear();
|
|
267
|
+
this.unattributedMessages.clear();
|
|
255
268
|
this.onSubagentChange?.();
|
|
256
269
|
}
|
|
257
270
|
getSubagents() {
|
|
@@ -278,6 +291,7 @@ class SubagentTracker {
|
|
|
278
291
|
this.subagents.set(id, {
|
|
279
292
|
id,
|
|
280
293
|
generation: existing?.generation ?? createSubagentGeneration(),
|
|
294
|
+
kind: 'tool',
|
|
281
295
|
status: existing?.status ?? 'pending',
|
|
282
296
|
toolCall: {
|
|
283
297
|
id,
|
|
@@ -319,23 +333,26 @@ class SubagentTracker {
|
|
|
319
333
|
this.namespaceToToolCallId.set(namespaceId, toolCallId);
|
|
320
334
|
const subagent = this.subagents.get(toolCallId);
|
|
321
335
|
if (subagent) {
|
|
336
|
+
const buffered = this.unattributedMessages.get(namespaceId);
|
|
322
337
|
this.subagents.set(toolCallId, {
|
|
323
338
|
...subagent,
|
|
324
339
|
status: subagent.status === 'complete' || subagent.status === 'error' ? subagent.status : 'running',
|
|
340
|
+
messages: buffered ? mergeMessages$1(subagent.messages, buffered) : subagent.messages,
|
|
325
341
|
});
|
|
342
|
+
this.unattributedMessages.delete(namespaceId);
|
|
326
343
|
}
|
|
327
344
|
this.onSubagentChange?.();
|
|
328
345
|
return toolCallId;
|
|
329
346
|
};
|
|
330
347
|
for (const [toolCallId, subagent] of this.subagents) {
|
|
331
|
-
if (mapped.has(toolCallId))
|
|
348
|
+
if (subagent.kind !== 'tool' || mapped.has(toolCallId))
|
|
332
349
|
continue;
|
|
333
350
|
if (subagent.toolCall.args['description'] === description) {
|
|
334
351
|
return establish(toolCallId);
|
|
335
352
|
}
|
|
336
353
|
}
|
|
337
354
|
for (const [toolCallId, subagent] of this.subagents) {
|
|
338
|
-
if (mapped.has(toolCallId))
|
|
355
|
+
if (subagent.kind !== 'tool' || mapped.has(toolCallId))
|
|
339
356
|
continue;
|
|
340
357
|
const subagentDescription = subagent.toolCall.args['description'];
|
|
341
358
|
if (typeof subagentDescription !== 'string' || !subagentDescription)
|
|
@@ -344,7 +361,11 @@ class SubagentTracker {
|
|
|
344
361
|
return establish(toolCallId);
|
|
345
362
|
}
|
|
346
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.
|
|
347
366
|
for (const [toolCallId, subagent] of this.subagents) {
|
|
367
|
+
if (subagent.kind !== 'tool')
|
|
368
|
+
continue;
|
|
348
369
|
if (!mapped.has(toolCallId) && (subagent.status === 'pending' || subagent.status === 'running')) {
|
|
349
370
|
return establish(toolCallId);
|
|
350
371
|
}
|
|
@@ -372,6 +393,67 @@ class SubagentTracker {
|
|
|
372
393
|
});
|
|
373
394
|
this.onSubagentChange?.();
|
|
374
395
|
}
|
|
396
|
+
/**
|
|
397
|
+
* Attribute a `tools:` child stream to its parent tool call as soon as the
|
|
398
|
+
* child is seen, without requiring a description to match on.
|
|
399
|
+
*
|
|
400
|
+
* The description ladder needs two things this repo's own graphs don't
|
|
401
|
+
* reliably provide: a delegation tool that names its argument `description`,
|
|
402
|
+
* and a child whose first message is the human task. `cockpit/chat/subagents`
|
|
403
|
+
* has neither — it uses `task_description`, and its child's messages begin
|
|
404
|
+
* with the AI reply. Attribution therefore never ran, so the child's
|
|
405
|
+
* transcript was never claimed and every card rendered "0 message(s)".
|
|
406
|
+
*
|
|
407
|
+
* Calling the ladder with no description skips both description rungs and
|
|
408
|
+
* lands on the positional fallback (first unmapped pending/running tool
|
|
409
|
+
* child), which is correct for sequential dispatch and is the same heuristic
|
|
410
|
+
* the ladder already relied on in practice.
|
|
411
|
+
*/
|
|
412
|
+
ensureToolStreamAttribution(namespaceId) {
|
|
413
|
+
if (this.namespaceToToolCallId.has(namespaceId))
|
|
414
|
+
return;
|
|
415
|
+
this.matchSubgraphToSubagent(namespaceId, '');
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Register a plain-subgraph child stream on its first namespaced event.
|
|
419
|
+
*
|
|
420
|
+
* Unlike tool children — announced ahead of time by the parent's tool call —
|
|
421
|
+
* a compiled child added as a plain node has no announcement: its existence
|
|
422
|
+
* is learned from the first event carrying its namespace. It starts
|
|
423
|
+
* 'running' because by the time we see an event, it is.
|
|
424
|
+
*/
|
|
425
|
+
ensureSubgraphStream(key, name) {
|
|
426
|
+
if (this.subagents.has(key))
|
|
427
|
+
return;
|
|
428
|
+
this.subagents.set(key, {
|
|
429
|
+
id: key,
|
|
430
|
+
generation: createSubagentGeneration(),
|
|
431
|
+
kind: 'subgraph',
|
|
432
|
+
status: 'running',
|
|
433
|
+
toolCall: { id: key, name, args: {} },
|
|
434
|
+
values: {},
|
|
435
|
+
messages: [],
|
|
436
|
+
});
|
|
437
|
+
this.onSubagentChange?.();
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Settle still-running subgraph children when the run reaches a terminal
|
|
441
|
+
* outcome. Tool children settle through their tool result
|
|
442
|
+
* (`processToolMessage`); subgraph children have no result message, so the
|
|
443
|
+
* run's own settle is their completion signal. Paused/interrupted runs must
|
|
444
|
+
* NOT call this — a child can resume with the thread.
|
|
445
|
+
*/
|
|
446
|
+
settleRunningSubgraphs(outcome) {
|
|
447
|
+
let changed = false;
|
|
448
|
+
for (const [key, subagent] of this.subagents) {
|
|
449
|
+
if (subagent.kind !== 'subgraph' || subagent.status !== 'running')
|
|
450
|
+
continue;
|
|
451
|
+
this.subagents.set(key, { ...subagent, status: outcome });
|
|
452
|
+
changed = true;
|
|
453
|
+
}
|
|
454
|
+
if (changed)
|
|
455
|
+
this.onSubagentChange?.();
|
|
456
|
+
}
|
|
375
457
|
updateSubagentValues(namespaceId, values) {
|
|
376
458
|
const toolCallId = this.resolveToolCallId(namespaceId);
|
|
377
459
|
const subagent = this.subagents.get(toolCallId);
|
|
@@ -387,8 +469,12 @@ class SubagentTracker {
|
|
|
387
469
|
addMessageToSubagent(namespaceId, message) {
|
|
388
470
|
const toolCallId = this.resolveToolCallId(namespaceId);
|
|
389
471
|
const subagent = this.subagents.get(toolCallId);
|
|
390
|
-
if (!subagent)
|
|
472
|
+
if (!subagent) {
|
|
473
|
+
// Not attributed yet — hold it rather than drop it. `establish()` will
|
|
474
|
+
// replay the buffer the moment this namespace is matched to a tool call.
|
|
475
|
+
this.unattributedMessages.set(namespaceId, mergeMessages$1(this.unattributedMessages.get(namespaceId) ?? [], [message]));
|
|
391
476
|
return;
|
|
477
|
+
}
|
|
392
478
|
this.subagents.set(toolCallId, {
|
|
393
479
|
...subagent,
|
|
394
480
|
status: subagent.status === 'complete' || subagent.status === 'error' ? subagent.status : 'running',
|
|
@@ -437,12 +523,40 @@ class SubagentTracker {
|
|
|
437
523
|
return this.namespaceToToolCallId.get(namespaceId) ?? namespaceId;
|
|
438
524
|
}
|
|
439
525
|
}
|
|
440
|
-
|
|
526
|
+
/**
|
|
527
|
+
* True when a stream event belongs to a child graph rather than the parent —
|
|
528
|
+
* i.e. it carries any namespace at all. This is the single classification
|
|
529
|
+
* question; which child owns the event is a separate (attribution) question.
|
|
530
|
+
*
|
|
531
|
+
* Kept consistent with the terminal-evidence guard, which has always refused
|
|
532
|
+
* ANY namespaced event as proof the parent run finished.
|
|
533
|
+
*/
|
|
534
|
+
function isChildNamespace(namespace) {
|
|
441
535
|
if (!namespace)
|
|
442
536
|
return false;
|
|
443
537
|
if (typeof namespace === 'string')
|
|
444
|
-
return namespace.
|
|
445
|
-
return namespace.
|
|
538
|
+
return namespace.length > 0;
|
|
539
|
+
return namespace.length > 0;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Derive a child stream's identity from an event namespace.
|
|
543
|
+
*
|
|
544
|
+
* `tools:<id>` segments identify a tool-dispatched child by its tool-call id.
|
|
545
|
+
* Any other segment (e.g. `research:<uuid>` from a compiled graph added with
|
|
546
|
+
* `add_node`) identifies a plain subgraph child: the full segment is the key
|
|
547
|
+
* (unique per invocation) and the part before the first ':' is the node name.
|
|
548
|
+
*/
|
|
549
|
+
function childStreamRefFromNamespace(namespace) {
|
|
550
|
+
for (const segment of namespace) {
|
|
551
|
+
if (segment.startsWith('tools:')) {
|
|
552
|
+
return { key: segment.slice(6), name: '', kind: 'tool' };
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const first = namespace[0];
|
|
556
|
+
if (!first)
|
|
557
|
+
return undefined;
|
|
558
|
+
const colon = first.indexOf(':');
|
|
559
|
+
return { key: first, name: colon > 0 ? first.slice(0, colon) : first, kind: 'subgraph' };
|
|
446
560
|
}
|
|
447
561
|
function extractToolCallIdFromNamespace(namespace) {
|
|
448
562
|
if (!namespace)
|
|
@@ -485,7 +599,7 @@ function mergeMessages$1(existing, incoming) {
|
|
|
485
599
|
const id = getMessageId(msg);
|
|
486
600
|
const idx = id ? merged.findIndex(m => getMessageId(m) === id) : -1;
|
|
487
601
|
if (idx >= 0) {
|
|
488
|
-
merged[idx] = msg;
|
|
602
|
+
merged[idx] = accumulateChunk(merged[idx], msg);
|
|
489
603
|
}
|
|
490
604
|
else {
|
|
491
605
|
merged.push(msg);
|
|
@@ -493,6 +607,57 @@ function mergeMessages$1(existing, incoming) {
|
|
|
493
607
|
}
|
|
494
608
|
return merged;
|
|
495
609
|
}
|
|
610
|
+
/**
|
|
611
|
+
* Fold a streamed chunk into the message it belongs to.
|
|
612
|
+
*
|
|
613
|
+
* A child graph streams `AIMessageChunk`s that are *deltas* — a handful of
|
|
614
|
+
* characters each, hundreds per message. Replacing by id (the previous
|
|
615
|
+
* behavior) therefore kept only the final delta, so a fully attributed
|
|
616
|
+
* subagent still rendered a near-empty message. Snapshots, which carry the
|
|
617
|
+
* message-so-far, still replace.
|
|
618
|
+
*
|
|
619
|
+
* This mirrors the parent transcript's delta handling: append unconditionally
|
|
620
|
+
* rather than comparing text, because a prefix-style "dedupe" silently eats
|
|
621
|
+
* legitimate tokens that happen to repeat the accumulated prefix.
|
|
622
|
+
*/
|
|
623
|
+
function accumulateChunk(existing, incoming) {
|
|
624
|
+
if (!isChunkMessage(incoming))
|
|
625
|
+
return incoming;
|
|
626
|
+
const previousText = extractText$1(existing['content']);
|
|
627
|
+
const incomingText = extractText$1(incoming['content']);
|
|
628
|
+
if (!incomingText)
|
|
629
|
+
return existing;
|
|
630
|
+
if (!previousText)
|
|
631
|
+
return incoming;
|
|
632
|
+
return { ...incoming, content: previousText + incomingText };
|
|
633
|
+
}
|
|
634
|
+
function isChunkMessage(message) {
|
|
635
|
+
const type = message['type'];
|
|
636
|
+
return typeof type === 'string' && type.endsWith('Chunk');
|
|
637
|
+
}
|
|
638
|
+
function extractText$1(content) {
|
|
639
|
+
if (typeof content === 'string')
|
|
640
|
+
return content;
|
|
641
|
+
if (!Array.isArray(content))
|
|
642
|
+
return '';
|
|
643
|
+
let out = '';
|
|
644
|
+
for (const block of content) {
|
|
645
|
+
if (typeof block === 'string') {
|
|
646
|
+
out += block;
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (block == null || typeof block !== 'object')
|
|
650
|
+
continue;
|
|
651
|
+
const record = block;
|
|
652
|
+
const blockType = record['type'];
|
|
653
|
+
if (blockType === 'text' || blockType === 'output_text' || blockType === undefined) {
|
|
654
|
+
const text = record['text'];
|
|
655
|
+
if (typeof text === 'string')
|
|
656
|
+
out += text;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return out;
|
|
660
|
+
}
|
|
496
661
|
function getMessageId(message) {
|
|
497
662
|
return message.id;
|
|
498
663
|
}
|
|
@@ -649,6 +814,13 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
649
814
|
continue;
|
|
650
815
|
finalizeMessage(attempt, id, outcome);
|
|
651
816
|
}
|
|
817
|
+
// Subgraph children have no tool result to settle them; the run's own
|
|
818
|
+
// terminal outcome is their completion signal. Paused/interrupted runs
|
|
819
|
+
// are excluded — a child can resume with the thread.
|
|
820
|
+
if (outcome === 'success' || outcome === 'error' || outcome === 'aborted') {
|
|
821
|
+
subagentManager.settleRunningSubgraphs(outcome === 'success' ? 'complete' : 'error');
|
|
822
|
+
publishSubagents();
|
|
823
|
+
}
|
|
652
824
|
}
|
|
653
825
|
function finishOutcome(attempt) {
|
|
654
826
|
return attempt.terminalOutcome
|
|
@@ -1118,17 +1290,27 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1118
1290
|
const normalized = options.toMessage
|
|
1119
1291
|
? msgs.map(options.toMessage)
|
|
1120
1292
|
: msgs;
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1293
|
+
// Any namespaced message event is child content. It feeds the child's
|
|
1294
|
+
// stream and never merges into the parent transcript — the parent
|
|
1295
|
+
// transcript is what the parent graph says. Shared-state children still
|
|
1296
|
+
// surface at settle through the authoritative top-level `values` sync.
|
|
1297
|
+
if (isChildNamespace(namespace)) {
|
|
1298
|
+
const child = namespace ? childStreamRefFromNamespace(namespace) : undefined;
|
|
1299
|
+
if (child) {
|
|
1300
|
+
if (child.kind === 'subgraph') {
|
|
1301
|
+
subagentManager.ensureSubgraphStream(child.key, child.name);
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
// Claim the stream for its tool call before its tokens arrive.
|
|
1305
|
+
subagentManager.ensureToolStreamAttribution(child.key);
|
|
1306
|
+
}
|
|
1124
1307
|
for (const msg of normalized) {
|
|
1125
|
-
subagentManager.addMessageToSubagent(
|
|
1308
|
+
subagentManager.addMessageToSubagent(child.key, msg);
|
|
1126
1309
|
}
|
|
1127
1310
|
publishSubagents();
|
|
1128
1311
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
}
|
|
1312
|
+
storeMessageMetadata(normalized, event);
|
|
1313
|
+
return;
|
|
1132
1314
|
}
|
|
1133
1315
|
// Partial and message-tuple events are incremental. Merge them by id
|
|
1134
1316
|
// so optimistic human messages and earlier tool messages are preserved.
|
|
@@ -1143,7 +1325,7 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1143
1325
|
const merged = mergeMessages(subjects.messages$.value, normalized, reasoningTimingMap, mode, canonicalMessageIds, affectedMessageIds, activeAttempt?.currentAssistantMessageId !== undefined
|
|
1144
1326
|
&& activeAttempt.currentStepHasTerminalEvidence !== true);
|
|
1145
1327
|
subjects.messages$.next(merged);
|
|
1146
|
-
|
|
1328
|
+
{
|
|
1147
1329
|
trackAssistantMessages(merged.filter(message => {
|
|
1148
1330
|
const id = message['id'];
|
|
1149
1331
|
return typeof id === 'string' && affectedMessageIds.has(id);
|
|
@@ -1166,7 +1348,7 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1166
1348
|
const affectedMessageIds = new Set();
|
|
1167
1349
|
const preserved = preserveIds(subjects.messages$.value, normalized, affectedMessageIds);
|
|
1168
1350
|
subjects.messages$.next(preserved);
|
|
1169
|
-
|
|
1351
|
+
{
|
|
1170
1352
|
trackAssistantMessages(preserved.filter(message => {
|
|
1171
1353
|
const id = message['id'];
|
|
1172
1354
|
return typeof id === 'string' && affectedMessageIds.has(id);
|
|
@@ -1185,8 +1367,11 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1185
1367
|
switch (baseType) {
|
|
1186
1368
|
case 'values': {
|
|
1187
1369
|
const vals = extractEventData(event);
|
|
1188
|
-
if (
|
|
1189
|
-
|
|
1370
|
+
if (isChildNamespace(namespace)) {
|
|
1371
|
+
// A child's state must not clobber the parent's `values$` — route it
|
|
1372
|
+
// to the child stream and stop.
|
|
1373
|
+
if (isRecord$1(vals))
|
|
1374
|
+
updateSubagentValues(namespace, vals);
|
|
1190
1375
|
break;
|
|
1191
1376
|
}
|
|
1192
1377
|
if ((namespace?.length ?? 0) === 0) {
|
|
@@ -1252,7 +1437,9 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1252
1437
|
}
|
|
1253
1438
|
case 'updates': {
|
|
1254
1439
|
const upd = extractEventData(event);
|
|
1255
|
-
if (
|
|
1440
|
+
if (isChildNamespace(namespace)) {
|
|
1441
|
+
// A child's updates must not spread-merge into the parent's
|
|
1442
|
+
// `values$` — they only mark the child stream running.
|
|
1256
1443
|
markSubagentRunning(namespace);
|
|
1257
1444
|
break;
|
|
1258
1445
|
}
|
|
@@ -1315,24 +1502,36 @@ function createStreamManagerBridge({ options, subjects, threadId$, destroy$ }) {
|
|
|
1315
1502
|
publishSubagents();
|
|
1316
1503
|
}
|
|
1317
1504
|
function updateSubagentValues(namespace, values) {
|
|
1318
|
-
const
|
|
1319
|
-
if (!
|
|
1505
|
+
const child = namespace ? childStreamRefFromNamespace(namespace) : undefined;
|
|
1506
|
+
if (!child)
|
|
1320
1507
|
return;
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1508
|
+
if (child.kind === 'tool') {
|
|
1509
|
+
// Prefer the precise description match when the child's first message is
|
|
1510
|
+
// the human task; otherwise claim the stream positionally so a graph that
|
|
1511
|
+
// doesn't fit that shape still gets attributed.
|
|
1512
|
+
const messages = values['messages'];
|
|
1513
|
+
const first = Array.isArray(messages) && messages.length > 0 ? messages[0] : undefined;
|
|
1324
1514
|
if (isRecord$1(first) && (first['type'] === 'human' || first['type'] === 'user') && typeof first['content'] === 'string') {
|
|
1325
|
-
subagentManager.matchSubgraphToSubagent(
|
|
1515
|
+
subagentManager.matchSubgraphToSubagent(child.key, first['content']);
|
|
1516
|
+
}
|
|
1517
|
+
else {
|
|
1518
|
+
subagentManager.ensureToolStreamAttribution(child.key);
|
|
1326
1519
|
}
|
|
1327
1520
|
}
|
|
1328
|
-
|
|
1521
|
+
else {
|
|
1522
|
+
subagentManager.ensureSubgraphStream(child.key, child.name);
|
|
1523
|
+
}
|
|
1524
|
+
subagentManager.updateSubagentValues(child.key, values);
|
|
1329
1525
|
publishSubagents();
|
|
1330
1526
|
}
|
|
1331
1527
|
function markSubagentRunning(namespace) {
|
|
1332
|
-
const
|
|
1333
|
-
if (!
|
|
1528
|
+
const child = namespace ? childStreamRefFromNamespace(namespace) : undefined;
|
|
1529
|
+
if (!child)
|
|
1334
1530
|
return;
|
|
1335
|
-
|
|
1531
|
+
if (child.kind === 'subgraph') {
|
|
1532
|
+
subagentManager.ensureSubgraphStream(child.key, child.name);
|
|
1533
|
+
}
|
|
1534
|
+
subagentManager.markRunningFromNamespace(child.key, namespace);
|
|
1336
1535
|
publishSubagents();
|
|
1337
1536
|
}
|
|
1338
1537
|
function publishSubagents() {
|
|
@@ -2130,9 +2329,13 @@ function toSubagentRefs(subagents) {
|
|
|
2130
2329
|
subagents.forEach((subagent, key) => {
|
|
2131
2330
|
refs.set(key, {
|
|
2132
2331
|
toolCallId: subagent.id,
|
|
2332
|
+
// Tool children are named by their `subagent_type` arg; subgraph
|
|
2333
|
+
// children by their node name (stored as the synthetic toolCall name).
|
|
2133
2334
|
name: typeof subagent.toolCall.args['subagent_type'] === 'string'
|
|
2134
2335
|
? subagent.toolCall.args['subagent_type']
|
|
2135
|
-
:
|
|
2336
|
+
: subagent.kind === 'subgraph'
|
|
2337
|
+
? subagent.toolCall.name
|
|
2338
|
+
: undefined,
|
|
2136
2339
|
status: signal(subagent.status),
|
|
2137
2340
|
values: signal(subagent.values),
|
|
2138
2341
|
messages: signal(subagent.messages),
|
|
@@ -3424,7 +3627,6 @@ function agentFactory() {
|
|
|
3424
3627
|
...(config.transport !== undefined ? { transport: config.transport } : {}),
|
|
3425
3628
|
...(config.clientOptions !== undefined ? { clientOptions: config.clientOptions } : {}),
|
|
3426
3629
|
...(config.telemetry !== undefined ? { telemetry: config.telemetry } : {}),
|
|
3427
|
-
...(config.filterSubagentMessages !== undefined ? { filterSubagentMessages: config.filterSubagentMessages } : {}),
|
|
3428
3630
|
...(config.subagentToolNames !== undefined ? { subagentToolNames: config.subagentToolNames } : {}),
|
|
3429
3631
|
...(config.transcriptNodeNames !== undefined ? { transcriptNodeNames: config.transcriptNodeNames } : {}),
|
|
3430
3632
|
});
|