@botbotgo/agent-harness 0.0.248 → 0.0.250
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/dist/api.d.ts +2 -0
- package/dist/api.js +3 -0
- package/dist/contracts/runtime.d.ts +22 -0
- package/dist/flow/build-flow-graph.js +65 -5
- package/dist/flow/export-sequence-mermaid.js +11 -2
- package/dist/flow/types.d.ts +1 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/persistence/file-store.js +3 -0
- package/dist/persistence/sqlite-store.js +23 -9
- package/dist/persistence/types.d.ts +1 -0
- package/dist/runtime/harness/events/streaming.js +2 -0
- package/dist/runtime/harness/run/inspection.d.ts +17 -1
- package/dist/runtime/harness/run/inspection.js +146 -0
- package/dist/runtime/harness/run/stream-run.js +24 -2
- package/dist/runtime/harness/run/thread-records.js +23 -1
- package/dist/runtime/harness.js +4 -0
- package/dist/utils/agent-display.d.ts +1 -0
- package/dist/utils/agent-display.js +18 -0
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export type RequestUpstreamEventItem = {
|
|
|
32
32
|
sessionId: string;
|
|
33
33
|
requestId: string;
|
|
34
34
|
agentId: string;
|
|
35
|
+
agentName?: string;
|
|
36
|
+
surfaceItems?: import("./contracts/types.js").RuntimeSurfaceItem[];
|
|
35
37
|
event: unknown;
|
|
36
38
|
};
|
|
37
39
|
export type PublicRunListeners = {
|
package/dist/api.js
CHANGED
|
@@ -65,6 +65,8 @@ function toPublicRunListeners(listeners) {
|
|
|
65
65
|
sessionId: item.threadId,
|
|
66
66
|
requestId: item.runId,
|
|
67
67
|
agentId: item.agentId,
|
|
68
|
+
agentName: item.agentName,
|
|
69
|
+
surfaceItems: item.surfaceItems,
|
|
68
70
|
event: item.event,
|
|
69
71
|
})
|
|
70
72
|
: undefined,
|
|
@@ -142,6 +144,7 @@ function toPublicRequestSummary(run) {
|
|
|
142
144
|
requestId: run.runId,
|
|
143
145
|
sessionId: run.threadId,
|
|
144
146
|
agentId: run.agentId,
|
|
147
|
+
parentRunId: run.parentRunId,
|
|
145
148
|
executionMode: run.executionMode,
|
|
146
149
|
adapterKind: run.adapterKind,
|
|
147
150
|
createdAt: run.createdAt,
|
|
@@ -98,6 +98,21 @@ export type RuntimeHistoryItem = {
|
|
|
98
98
|
isError?: boolean;
|
|
99
99
|
key: string;
|
|
100
100
|
};
|
|
101
|
+
export type RuntimeSurfaceKind = "agent" | "llm" | "memory" | "skill" | "tool";
|
|
102
|
+
export type RuntimeSurfaceItem = {
|
|
103
|
+
kind: RuntimeSurfaceKind;
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
label: string;
|
|
107
|
+
status: "started" | "completed" | "failed";
|
|
108
|
+
ownerAgentId?: string;
|
|
109
|
+
ownerAgentName?: string;
|
|
110
|
+
sourceEventId?: string;
|
|
111
|
+
};
|
|
112
|
+
export type AgentReference = {
|
|
113
|
+
id: string;
|
|
114
|
+
name: string;
|
|
115
|
+
};
|
|
101
116
|
export type RuntimeQueueDiagnostics = {
|
|
102
117
|
status: HealthStatus;
|
|
103
118
|
activeRunSlots: number;
|
|
@@ -414,6 +429,8 @@ export type UpstreamRuntimeEventItem = {
|
|
|
414
429
|
threadId: string;
|
|
415
430
|
runId: string;
|
|
416
431
|
agentId: string;
|
|
432
|
+
agentName?: string;
|
|
433
|
+
surfaceItems?: RuntimeSurfaceItem[];
|
|
417
434
|
event: UpstreamRuntimeEvent;
|
|
418
435
|
};
|
|
419
436
|
export type RuntimeListeners = {
|
|
@@ -461,6 +478,8 @@ export type HarnessStreamItem = {
|
|
|
461
478
|
threadId: string;
|
|
462
479
|
runId: string;
|
|
463
480
|
agentId: string;
|
|
481
|
+
agentName?: string;
|
|
482
|
+
surfaceItems?: RuntimeSurfaceItem[];
|
|
464
483
|
event: UpstreamRuntimeEvent;
|
|
465
484
|
} | {
|
|
466
485
|
type: "result";
|
|
@@ -476,6 +495,7 @@ export type ThreadRunRecord = {
|
|
|
476
495
|
runId: string;
|
|
477
496
|
threadId: string;
|
|
478
497
|
agentId: string;
|
|
498
|
+
parentRunId?: string;
|
|
479
499
|
executionMode: string;
|
|
480
500
|
adapterKind?: string;
|
|
481
501
|
createdAt: string;
|
|
@@ -500,6 +520,7 @@ export type RequestSummary = Omit<ThreadRunRecord, "threadId" | "runId"> & {
|
|
|
500
520
|
};
|
|
501
521
|
export type RequestRecord = RequestSummary & {
|
|
502
522
|
history?: RuntimeHistoryItem[];
|
|
523
|
+
runtimeSurface?: RuntimeSurfaceItem[];
|
|
503
524
|
upstreamEvents?: unknown[];
|
|
504
525
|
runtimeTimeline?: RuntimeTimelineItem[];
|
|
505
526
|
};
|
|
@@ -512,6 +533,7 @@ export type RunSummary = Omit<RequestSummary, "sessionId" | "requestId"> & {
|
|
|
512
533
|
};
|
|
513
534
|
export type RunRecord = RunSummary & {
|
|
514
535
|
history?: RuntimeHistoryItem[];
|
|
536
|
+
runtimeSurface?: RuntimeSurfaceItem[];
|
|
515
537
|
upstreamEvents?: unknown[];
|
|
516
538
|
runtimeTimeline?: RuntimeTimelineItem[];
|
|
517
539
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { projectRuntimeTimeline } from "../runtime/harness/events/timeline.js";
|
|
2
2
|
import { createUpstreamTimelineReducer } from "../upstream-events.js";
|
|
3
|
+
import { formatAgentName } from "../utils/agent-display.js";
|
|
3
4
|
function asObject(value) {
|
|
4
5
|
return typeof value === "object" && value !== null ? value : null;
|
|
5
6
|
}
|
|
@@ -17,7 +18,8 @@ function extractUpstreamEventEnvelope(value) {
|
|
|
17
18
|
? nestedEvent
|
|
18
19
|
: value;
|
|
19
20
|
const agentId = readString(typed?.agentId);
|
|
20
|
-
|
|
21
|
+
const agentName = readString(typed?.agentName);
|
|
22
|
+
return { event, agentId: agentId ?? undefined, agentName: agentName ?? undefined };
|
|
21
23
|
}
|
|
22
24
|
function collectNestedAgentNames(value, names = new Set()) {
|
|
23
25
|
if (Array.isArray(value)) {
|
|
@@ -48,6 +50,9 @@ function slugify(value) {
|
|
|
48
50
|
.replace(/^-+|-+$/g, "")
|
|
49
51
|
.slice(0, 80) || "node";
|
|
50
52
|
}
|
|
53
|
+
function buildSurfaceId(kind, value) {
|
|
54
|
+
return `${kind}:${slugify(value)}`;
|
|
55
|
+
}
|
|
51
56
|
function titleCase(value) {
|
|
52
57
|
return value
|
|
53
58
|
.replace(/[_-]+/g, " ")
|
|
@@ -352,6 +357,9 @@ function deriveInitialAgentId(input, runtimeTimeline) {
|
|
|
352
357
|
}
|
|
353
358
|
return "agent";
|
|
354
359
|
}
|
|
360
|
+
function deriveAgentName(agentId, explicitName) {
|
|
361
|
+
return explicitName && explicitName.trim().length > 0 ? explicitName.trim() : formatAgentName(agentId);
|
|
362
|
+
}
|
|
355
363
|
function extractDelegatedAgentId(event) {
|
|
356
364
|
const typed = asObject(event);
|
|
357
365
|
if (!typed) {
|
|
@@ -389,15 +397,21 @@ function convertUpstreamEventsWithAgents(upstreamEvents, initialAgentId) {
|
|
|
389
397
|
const projections = [];
|
|
390
398
|
const delegationNodes = [];
|
|
391
399
|
let currentAgentId = initialAgentId;
|
|
400
|
+
let currentAgentName = deriveAgentName(initialAgentId);
|
|
392
401
|
let ordinal = 0;
|
|
393
402
|
upstreamEvents.forEach((event, index) => {
|
|
394
403
|
const sourceEventId = `upstream:${index + 1}`;
|
|
395
404
|
const envelope = extractUpstreamEventEnvelope(event);
|
|
396
405
|
if (envelope.agentId && envelope.agentId !== currentAgentId) {
|
|
397
406
|
currentAgentId = envelope.agentId;
|
|
407
|
+
currentAgentName = deriveAgentName(currentAgentId, envelope.agentName);
|
|
408
|
+
}
|
|
409
|
+
else if (envelope.agentName) {
|
|
410
|
+
currentAgentName = deriveAgentName(currentAgentId, envelope.agentName);
|
|
398
411
|
}
|
|
399
412
|
const nestedAgentId = extractAgentFromNestedMessages(envelope.event, currentAgentId);
|
|
400
413
|
if (nestedAgentId && nestedAgentId !== currentAgentId) {
|
|
414
|
+
const nestedAgentName = deriveAgentName(nestedAgentId);
|
|
401
415
|
ordinal += 1;
|
|
402
416
|
delegationNodes.push({
|
|
403
417
|
id: `delegate:${slugify(currentAgentId)}:${slugify(nestedAgentId)}:${ordinal}`,
|
|
@@ -410,19 +424,24 @@ function convertUpstreamEventsWithAgents(upstreamEvents, initialAgentId) {
|
|
|
410
424
|
threadId: "",
|
|
411
425
|
runId: "",
|
|
412
426
|
agentId: nestedAgentId,
|
|
427
|
+
agentName: nestedAgentName,
|
|
413
428
|
sourceEventIds: [sourceEventId],
|
|
414
429
|
detail: {
|
|
415
430
|
fromAgentId: currentAgentId,
|
|
431
|
+
fromAgentName: currentAgentName,
|
|
416
432
|
toAgentId: nestedAgentId,
|
|
433
|
+
toAgentName: nestedAgentName,
|
|
417
434
|
},
|
|
418
435
|
});
|
|
419
436
|
currentAgentId = nestedAgentId;
|
|
437
|
+
currentAgentName = nestedAgentName;
|
|
420
438
|
}
|
|
421
439
|
const emitted = reducer.consume(envelope.event);
|
|
422
440
|
for (const projection of emitted) {
|
|
423
441
|
projections.push({
|
|
424
442
|
projection,
|
|
425
443
|
agentId: currentAgentId,
|
|
444
|
+
agentName: currentAgentName,
|
|
426
445
|
sourceEventId,
|
|
427
446
|
});
|
|
428
447
|
}
|
|
@@ -430,6 +449,7 @@ function convertUpstreamEventsWithAgents(upstreamEvents, initialAgentId) {
|
|
|
430
449
|
if (!delegatedAgentId || delegatedAgentId === currentAgentId) {
|
|
431
450
|
return;
|
|
432
451
|
}
|
|
452
|
+
const delegatedAgentName = deriveAgentName(delegatedAgentId);
|
|
433
453
|
ordinal += 1;
|
|
434
454
|
delegationNodes.push({
|
|
435
455
|
id: `delegate:${slugify(initialAgentId)}:${slugify(delegatedAgentId)}:${ordinal}`,
|
|
@@ -442,13 +462,17 @@ function convertUpstreamEventsWithAgents(upstreamEvents, initialAgentId) {
|
|
|
442
462
|
threadId: "",
|
|
443
463
|
runId: "",
|
|
444
464
|
agentId: delegatedAgentId,
|
|
465
|
+
agentName: delegatedAgentName,
|
|
445
466
|
sourceEventIds: [sourceEventId],
|
|
446
467
|
detail: {
|
|
447
468
|
fromAgentId: currentAgentId,
|
|
469
|
+
fromAgentName: currentAgentName,
|
|
448
470
|
toAgentId: delegatedAgentId,
|
|
471
|
+
toAgentName: delegatedAgentName,
|
|
449
472
|
},
|
|
450
473
|
});
|
|
451
474
|
currentAgentId = delegatedAgentId;
|
|
475
|
+
currentAgentName = delegatedAgentName;
|
|
452
476
|
});
|
|
453
477
|
return { projections, delegationNodes };
|
|
454
478
|
}
|
|
@@ -479,7 +503,7 @@ function buildAttempts(projectionRecords, delegationNodes, runtimeGroups, thread
|
|
|
479
503
|
currentGroup = segmentGroups[nextSegmentIndex] ?? currentGroup;
|
|
480
504
|
}
|
|
481
505
|
}
|
|
482
|
-
function createAttempt(kind, label, projection, agentId) {
|
|
506
|
+
function createAttempt(kind, label, projection, agentId, agentName) {
|
|
483
507
|
ordinal += 1;
|
|
484
508
|
const attempt = {
|
|
485
509
|
id: `attempt:${runId}:${slugify(kind)}:${slugify(label)}:${ordinal}`,
|
|
@@ -487,6 +511,7 @@ function buildAttempts(projectionRecords, delegationNodes, runtimeGroups, thread
|
|
|
487
511
|
label,
|
|
488
512
|
status: deriveStatusFromProjection(projection),
|
|
489
513
|
agentId,
|
|
514
|
+
agentName,
|
|
490
515
|
groupId: currentGroup?.id,
|
|
491
516
|
sourceEventIds: [],
|
|
492
517
|
attemptKey: buildAttemptKey(kind, label, currentGroup?.id),
|
|
@@ -589,7 +614,7 @@ function buildAttempts(projectionRecords, delegationNodes, runtimeGroups, thread
|
|
|
589
614
|
const attemptKey = buildAttemptKey(kind, label, currentGroup?.id);
|
|
590
615
|
let attempt;
|
|
591
616
|
if (projection.type === "step" && projection.status === "started") {
|
|
592
|
-
attempt = createAttempt(kind, label, projection, record.agentId);
|
|
617
|
+
attempt = createAttempt(kind, label, projection, record.agentId, record.agentName);
|
|
593
618
|
activeAttemptsByKey.set(attemptKey, attempt);
|
|
594
619
|
}
|
|
595
620
|
else if (projection.type === "tool-result") {
|
|
@@ -597,16 +622,17 @@ function buildAttempts(projectionRecords, delegationNodes, runtimeGroups, thread
|
|
|
597
622
|
const existing = toolAttemptsByName.get(toolName) ?? [];
|
|
598
623
|
attempt = [...existing].reverse().find((candidate) => candidate.groupId === currentGroup?.id) ?? existing[existing.length - 1];
|
|
599
624
|
if (!attempt) {
|
|
600
|
-
attempt = createAttempt("tool", `Calling tool ${titleCase(toolName)}`, projection, record.agentId);
|
|
625
|
+
attempt = createAttempt("tool", `Calling tool ${titleCase(toolName)}`, projection, record.agentId, record.agentName);
|
|
601
626
|
}
|
|
602
627
|
}
|
|
603
628
|
else {
|
|
604
629
|
attempt = activeAttemptsByKey.get(attemptKey);
|
|
605
630
|
if (!attempt) {
|
|
606
|
-
attempt = createAttempt(kind, label, projection, record.agentId);
|
|
631
|
+
attempt = createAttempt(kind, label, projection, record.agentId, record.agentName);
|
|
607
632
|
}
|
|
608
633
|
}
|
|
609
634
|
attempt.agentId = attempt.agentId ?? record.agentId;
|
|
635
|
+
attempt.agentName = attempt.agentName ?? record.agentName;
|
|
610
636
|
attempt.projectionKeys.push(projection.key);
|
|
611
637
|
attempt.sourceEventIds.push(record.sourceEventId);
|
|
612
638
|
if (projection.type === "step") {
|
|
@@ -684,6 +710,7 @@ function buildAttempts(projectionRecords, delegationNodes, runtimeGroups, thread
|
|
|
684
710
|
threadId,
|
|
685
711
|
runId,
|
|
686
712
|
agentId: attempt.agentId,
|
|
713
|
+
agentName: attempt.agentName,
|
|
687
714
|
groupId: attempt.groupId,
|
|
688
715
|
sequenceStart: attempt.sequenceStart,
|
|
689
716
|
sequenceEnd: attempt.sequenceEnd,
|
|
@@ -730,15 +757,47 @@ export function buildFlowGraph(input) {
|
|
|
730
757
|
const { nodes: runtimeNodes, edges: runtimeEdges, groups: runtimeGroups } = buildRuntimeNodes(runtimeTimeline);
|
|
731
758
|
for (const node of runtimeNodes) {
|
|
732
759
|
node.agentId = typeof node.detail.agentId === "string" ? node.detail.agentId : initialAgentId;
|
|
760
|
+
node.agentName = deriveAgentName(node.agentId);
|
|
733
761
|
}
|
|
734
762
|
const projectionRecords = input.upstreamProjections
|
|
735
763
|
? input.upstreamProjections.map((projection, index) => ({
|
|
736
764
|
projection,
|
|
737
765
|
agentId: initialAgentId,
|
|
766
|
+
agentName: deriveAgentName(initialAgentId),
|
|
738
767
|
sourceEventId: "key" in projection ? projection.key : `projection:${index + 1}`,
|
|
739
768
|
}))
|
|
740
769
|
: upstreamContext.projections;
|
|
741
770
|
const { nodes: attemptNodes, edges: attemptEdges, groups: attemptGroups } = buildAttempts(projectionRecords, upstreamContext.delegationNodes, runtimeGroups, sessionId, requestId);
|
|
771
|
+
const runtimeSurface = [...upstreamContext.delegationNodes, ...attemptNodes]
|
|
772
|
+
.filter((node) => node.layer !== "detail"
|
|
773
|
+
&& (node.kind === "agent" || node.kind === "llm" || node.kind === "memory" || node.kind === "skill" || node.kind === "tool"))
|
|
774
|
+
.map((node) => ({
|
|
775
|
+
kind: node.kind,
|
|
776
|
+
id: buildSurfaceId(node.kind, node.kind === "agent" ? (node.agentId ?? node.agentName ?? "agent") : node.label),
|
|
777
|
+
name: node.kind === "agent"
|
|
778
|
+
? (node.agentName ?? node.agentId ?? "Agent")
|
|
779
|
+
: normalizeLabel(node.label)
|
|
780
|
+
.replace(/^Delegate to\s+/i, "")
|
|
781
|
+
.replace(/^Calling LLM\s+/i, "")
|
|
782
|
+
.replace(/^Completed LLM\s+/i, "")
|
|
783
|
+
.replace(/^Calling tool\s+/i, "")
|
|
784
|
+
.replace(/^Completed tool\s+/i, "")
|
|
785
|
+
.replace(/^Tool\s+/i, "")
|
|
786
|
+
.replace(/\s+failed$/i, "")
|
|
787
|
+
.replace(/^Calling skill\s+/i, "")
|
|
788
|
+
.replace(/^Completed skill\s+/i, "")
|
|
789
|
+
.replace(/^Accessing memory\s+/i, "")
|
|
790
|
+
.replace(/^Completed memory\s+/i, ""),
|
|
791
|
+
label: node.label,
|
|
792
|
+
status: node.status === "resolved" ? "completed" : node.status,
|
|
793
|
+
ownerAgentId: node.kind === "agent"
|
|
794
|
+
? (typeof node.detail.fromAgentId === "string" ? node.detail.fromAgentId : node.agentId)
|
|
795
|
+
: node.agentId,
|
|
796
|
+
ownerAgentName: node.kind === "agent"
|
|
797
|
+
? (typeof node.detail.fromAgentName === "string" ? node.detail.fromAgentName : node.agentName)
|
|
798
|
+
: node.agentName,
|
|
799
|
+
sourceEventId: node.sourceEventIds[0],
|
|
800
|
+
}));
|
|
742
801
|
return {
|
|
743
802
|
graphId: `${sessionId}/${requestId}`,
|
|
744
803
|
scope: input.scope ?? "run",
|
|
@@ -753,6 +812,7 @@ export function buildFlowGraph(input) {
|
|
|
753
812
|
runtimeTimelineCount: runtimeTimeline.length,
|
|
754
813
|
upstreamProjectionCount: upstreamProjections.length,
|
|
755
814
|
delegationCount: upstreamContext.delegationNodes.length,
|
|
815
|
+
runtimeSurface,
|
|
756
816
|
...(input.metadata ?? {}),
|
|
757
817
|
},
|
|
758
818
|
};
|
|
@@ -5,6 +5,13 @@ function sanitizeAlias(value) {
|
|
|
5
5
|
function escapeLabel(value) {
|
|
6
6
|
return value.replace(/"/g, '\\"');
|
|
7
7
|
}
|
|
8
|
+
function formatAgentParticipantLabel(node) {
|
|
9
|
+
const agentId = node.agentId ?? "agent";
|
|
10
|
+
if (node.agentName && node.agentName !== agentId) {
|
|
11
|
+
return `Agent:${agentId} (${node.agentName})`;
|
|
12
|
+
}
|
|
13
|
+
return `Agent:${agentId}`;
|
|
14
|
+
}
|
|
8
15
|
function selectNodes(graph, options) {
|
|
9
16
|
const includedKinds = options.includeKinds
|
|
10
17
|
? new Set(options.includeKinds)
|
|
@@ -33,7 +40,7 @@ function getParticipantsForNode(node) {
|
|
|
33
40
|
const agentParticipant = {
|
|
34
41
|
id: `agent:${node.agentId ?? "agent"}`,
|
|
35
42
|
alias: sanitizeAlias(`Agent_${node.agentId ?? "agent"}`),
|
|
36
|
-
label:
|
|
43
|
+
label: formatAgentParticipantLabel(node),
|
|
37
44
|
};
|
|
38
45
|
if (node.kind === "run") {
|
|
39
46
|
return [
|
|
@@ -48,7 +55,9 @@ function getParticipantsForNode(node) {
|
|
|
48
55
|
{
|
|
49
56
|
id: `agent:${fromAgentId}`,
|
|
50
57
|
alias: sanitizeAlias(`Agent_${fromAgentId}`),
|
|
51
|
-
label:
|
|
58
|
+
label: typeof node.detail.fromAgentName === "string" && node.detail.fromAgentName !== fromAgentId
|
|
59
|
+
? `Agent:${fromAgentId} (${node.detail.fromAgentName})`
|
|
60
|
+
: `Agent:${fromAgentId}`,
|
|
52
61
|
},
|
|
53
62
|
agentParticipant,
|
|
54
63
|
];
|
package/dist/flow/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.249";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.249";
|
|
@@ -67,10 +67,12 @@ export class FilePersistence {
|
|
|
67
67
|
const runDir = this.runDir(input.threadId, input.runId);
|
|
68
68
|
await ensureDir(path.join(runDir, "events"));
|
|
69
69
|
await ensureDir(path.join(runDir, "approvals"));
|
|
70
|
+
const threadMeta = await this.getThreadMeta(input.threadId);
|
|
70
71
|
const meta = {
|
|
71
72
|
runId: input.runId,
|
|
72
73
|
threadId: input.threadId,
|
|
73
74
|
agentId: input.agentId,
|
|
75
|
+
parentRunId: threadMeta?.latestRunId ?? null,
|
|
74
76
|
executionMode: input.executionMode,
|
|
75
77
|
adapterKind: input.adapterKind ?? input.executionMode,
|
|
76
78
|
createdAt: input.createdAt,
|
|
@@ -266,6 +268,7 @@ export class FilePersistence {
|
|
|
266
268
|
runId: meta.runId,
|
|
267
269
|
threadId: meta.threadId,
|
|
268
270
|
agentId: meta.agentId,
|
|
271
|
+
...(meta.parentRunId ? { parentRunId: meta.parentRunId } : {}),
|
|
269
272
|
executionMode: meta.executionMode,
|
|
270
273
|
adapterKind: meta.adapterKind ?? meta.executionMode,
|
|
271
274
|
createdAt: meta.createdAt,
|
|
@@ -4,7 +4,7 @@ import { createClient } from "@libsql/client";
|
|
|
4
4
|
import { fileExists, readJson, writeJson } from "../utils/fs.js";
|
|
5
5
|
import { SqliteRunContextStore } from "./sqlite-run-context-store.js";
|
|
6
6
|
import { SqliteRunQueueStore } from "./sqlite-run-queue-store.js";
|
|
7
|
-
const RUNTIME_SQLITE_SCHEMA_VERSION =
|
|
7
|
+
const RUNTIME_SQLITE_SCHEMA_VERSION = 5;
|
|
8
8
|
const RUNTIME_SQLITE_SCHEMA_FAMILY = "agent-harness-runtime";
|
|
9
9
|
function asRow(value) {
|
|
10
10
|
return value;
|
|
@@ -168,6 +168,7 @@ export class SqlitePersistence {
|
|
|
168
168
|
run_id TEXT PRIMARY KEY,
|
|
169
169
|
thread_id TEXT NOT NULL,
|
|
170
170
|
agent_id TEXT NOT NULL,
|
|
171
|
+
parent_run_id TEXT,
|
|
171
172
|
execution_mode TEXT NOT NULL,
|
|
172
173
|
adapter_kind TEXT,
|
|
173
174
|
created_at TEXT NOT NULL,
|
|
@@ -403,6 +404,7 @@ export class SqlitePersistence {
|
|
|
403
404
|
runId: asString(row.run_id),
|
|
404
405
|
threadId: asString(row.thread_id),
|
|
405
406
|
agentId: asString(row.agent_id),
|
|
407
|
+
...(asNullableString(row.parent_run_id) ? { parentRunId: asNullableString(row.parent_run_id) } : {}),
|
|
406
408
|
executionMode: asString(row.execution_mode),
|
|
407
409
|
adapterKind: asNullableString(row.adapter_kind) ?? undefined,
|
|
408
410
|
createdAt: asString(row.created_at),
|
|
@@ -473,12 +475,19 @@ export class SqlitePersistence {
|
|
|
473
475
|
SELECT run_id, thread_id, created_at, NULL, updated_at, agent_id, json_array(agent_id), NULL, '[]'
|
|
474
476
|
FROM runs
|
|
475
477
|
`);
|
|
478
|
+
await this.rawExecute("ALTER TABLE runs ADD COLUMN parent_run_id TEXT");
|
|
476
479
|
await this.rawExecute("INSERT OR REPLACE INTO runtime_metadata (key, value) VALUES (?, ?)", ["schema_version", String(RUNTIME_SQLITE_SCHEMA_VERSION)]);
|
|
477
480
|
return;
|
|
478
481
|
}
|
|
479
482
|
if (version === "3") {
|
|
480
483
|
await this.rawExecute("ALTER TABLE run_inspection ADD COLUMN upstream_events_json TEXT NOT NULL DEFAULT '[]'");
|
|
481
484
|
await this.rawExecute("UPDATE run_inspection SET upstream_events_json = '[]' WHERE upstream_events_json IS NULL");
|
|
485
|
+
await this.rawExecute("ALTER TABLE runs ADD COLUMN parent_run_id TEXT");
|
|
486
|
+
await this.rawExecute("INSERT OR REPLACE INTO runtime_metadata (key, value) VALUES (?, ?)", ["schema_version", String(RUNTIME_SQLITE_SCHEMA_VERSION)]);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (version === "4") {
|
|
490
|
+
await this.rawExecute("ALTER TABLE runs ADD COLUMN parent_run_id TEXT");
|
|
482
491
|
await this.rawExecute("INSERT OR REPLACE INTO runtime_metadata (key, value) VALUES (?, ?)", ["schema_version", String(RUNTIME_SQLITE_SCHEMA_VERSION)]);
|
|
483
492
|
return;
|
|
484
493
|
}
|
|
@@ -493,6 +502,7 @@ export class SqlitePersistence {
|
|
|
493
502
|
VALUES (?, ?, ?, ?, ?, ?, ?)`, [input.threadId, "default", input.agentId, input.status, input.runId, input.createdAt, input.createdAt]);
|
|
494
503
|
}
|
|
495
504
|
async bootstrapRun(input) {
|
|
505
|
+
const parentRunId = input.createThread ? null : (await this.getThreadMeta(input.threadId))?.latestRunId ?? null;
|
|
496
506
|
await mkdir(this.threadDir(input.threadId), { recursive: true });
|
|
497
507
|
await mkdir(path.join(this.runDir(input.threadId, input.runId), "events"), { recursive: true });
|
|
498
508
|
const steps = [];
|
|
@@ -506,12 +516,13 @@ export class SqlitePersistence {
|
|
|
506
516
|
}
|
|
507
517
|
steps.push({
|
|
508
518
|
sql: `INSERT OR REPLACE INTO runs
|
|
509
|
-
(run_id, thread_id, agent_id, execution_mode, adapter_kind, created_at, updated_at, state, previous_state, state_entered_at, last_transition_at, resumable, checkpoint_ref)
|
|
510
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
519
|
+
(run_id, thread_id, agent_id, parent_run_id, execution_mode, adapter_kind, created_at, updated_at, state, previous_state, state_entered_at, last_transition_at, resumable, checkpoint_ref)
|
|
520
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
511
521
|
args: [
|
|
512
522
|
input.runId,
|
|
513
523
|
input.threadId,
|
|
514
524
|
input.agentId,
|
|
525
|
+
parentRunId,
|
|
515
526
|
input.executionMode,
|
|
516
527
|
input.adapterKind ?? input.executionMode ?? null,
|
|
517
528
|
input.createdAt,
|
|
@@ -562,13 +573,15 @@ export class SqlitePersistence {
|
|
|
562
573
|
await this.executeTransaction(steps);
|
|
563
574
|
}
|
|
564
575
|
async createRun(input) {
|
|
576
|
+
const parentRunId = (await this.getThreadMeta(input.threadId))?.latestRunId ?? null;
|
|
565
577
|
await mkdir(path.join(this.runDir(input.threadId, input.runId), "events"), { recursive: true });
|
|
566
578
|
await this.execute(`INSERT OR REPLACE INTO runs
|
|
567
|
-
(run_id, thread_id, agent_id, execution_mode, adapter_kind, created_at, updated_at, state, previous_state, state_entered_at, last_transition_at, resumable, checkpoint_ref)
|
|
568
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
579
|
+
(run_id, thread_id, agent_id, parent_run_id, execution_mode, adapter_kind, created_at, updated_at, state, previous_state, state_entered_at, last_transition_at, resumable, checkpoint_ref)
|
|
580
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
569
581
|
input.runId,
|
|
570
582
|
input.threadId,
|
|
571
583
|
input.agentId,
|
|
584
|
+
parentRunId,
|
|
572
585
|
input.executionMode,
|
|
573
586
|
input.adapterKind ?? input.executionMode ?? null,
|
|
574
587
|
input.createdAt,
|
|
@@ -698,7 +711,7 @@ export class SqlitePersistence {
|
|
|
698
711
|
["runs.thread_id = ?", filter.threadId],
|
|
699
712
|
["runs.state = ?", filter.state],
|
|
700
713
|
]);
|
|
701
|
-
const rows = await this.selectAll(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
714
|
+
const rows = await this.selectAll(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.parent_run_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
702
715
|
run_inspection.started_at, run_inspection.ended_at, run_inspection.last_activity_at, run_inspection.current_agent_id, run_inspection.delegation_chain_json, run_inspection.runtime_snapshot_json
|
|
703
716
|
FROM runs
|
|
704
717
|
LEFT JOIN run_inspection ON run_inspection.run_id = runs.run_id
|
|
@@ -707,7 +720,7 @@ export class SqlitePersistence {
|
|
|
707
720
|
return rows.map((row) => this.mapRunSummary(row));
|
|
708
721
|
}
|
|
709
722
|
async getRun(runId) {
|
|
710
|
-
const row = await this.selectOne(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
723
|
+
const row = await this.selectOne(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.parent_run_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
711
724
|
run_inspection.started_at, run_inspection.ended_at, run_inspection.last_activity_at, run_inspection.current_agent_id, run_inspection.delegation_chain_json, run_inspection.runtime_snapshot_json
|
|
712
725
|
FROM runs
|
|
713
726
|
LEFT JOIN run_inspection ON run_inspection.run_id = runs.run_id
|
|
@@ -737,7 +750,7 @@ export class SqlitePersistence {
|
|
|
737
750
|
};
|
|
738
751
|
}
|
|
739
752
|
async listThreadRuns(threadId) {
|
|
740
|
-
const rows = await this.selectAll(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
753
|
+
const rows = await this.selectAll(`SELECT runs.run_id, runs.thread_id, runs.agent_id, runs.parent_run_id, runs.execution_mode, runs.adapter_kind, runs.created_at, runs.updated_at, runs.state, runs.checkpoint_ref, runs.resumable,
|
|
741
754
|
run_inspection.started_at, run_inspection.ended_at, run_inspection.last_activity_at, run_inspection.current_agent_id, run_inspection.delegation_chain_json, run_inspection.runtime_snapshot_json
|
|
742
755
|
FROM runs
|
|
743
756
|
LEFT JOIN run_inspection ON run_inspection.run_id = runs.run_id
|
|
@@ -770,7 +783,7 @@ export class SqlitePersistence {
|
|
|
770
783
|
return rows.map((row) => this.mapApproval(row));
|
|
771
784
|
}
|
|
772
785
|
async getRunMeta(threadId, runId) {
|
|
773
|
-
const row = await this.selectOne(`SELECT run_id, thread_id, agent_id, execution_mode, adapter_kind, created_at, updated_at
|
|
786
|
+
const row = await this.selectOne(`SELECT run_id, thread_id, agent_id, parent_run_id, execution_mode, adapter_kind, created_at, updated_at
|
|
774
787
|
FROM runs
|
|
775
788
|
WHERE thread_id = ? AND run_id = ?`, [threadId, runId]);
|
|
776
789
|
if (!row) {
|
|
@@ -780,6 +793,7 @@ export class SqlitePersistence {
|
|
|
780
793
|
runId: asString(row.run_id),
|
|
781
794
|
threadId: asString(row.thread_id),
|
|
782
795
|
agentId: asString(row.agent_id),
|
|
796
|
+
parentRunId: asNullableString(row.parent_run_id),
|
|
783
797
|
executionMode: asString(row.execution_mode),
|
|
784
798
|
adapterKind: asNullableString(row.adapter_kind) ?? undefined,
|
|
785
799
|
createdAt: asString(row.created_at),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompiledAgentBinding, RuntimeSnapshot } from "../../../contracts/types.js";
|
|
1
|
+
import type { CompiledAgentBinding, RuntimeSnapshot, RuntimeSurfaceItem } from "../../../contracts/types.js";
|
|
2
2
|
export declare function buildRunRuntimeSnapshot(binding: CompiledAgentBinding, options?: {
|
|
3
3
|
runId?: string;
|
|
4
4
|
}): RuntimeSnapshot;
|
|
@@ -11,3 +11,19 @@ export declare function consumeRunInspectionUpstreamEvent(input: {
|
|
|
11
11
|
currentAgentId: string;
|
|
12
12
|
delegationChain: string[];
|
|
13
13
|
};
|
|
14
|
+
export declare function projectRuntimeSurfaceFromUpstreamEvents(input: {
|
|
15
|
+
upstreamEvents: unknown[];
|
|
16
|
+
binding?: CompiledAgentBinding;
|
|
17
|
+
initialAgentId: string;
|
|
18
|
+
}): RuntimeSurfaceItem[];
|
|
19
|
+
export declare function projectRuntimeSurfaceFromSingleUpstreamEvent(input: {
|
|
20
|
+
event: unknown;
|
|
21
|
+
binding?: CompiledAgentBinding;
|
|
22
|
+
currentAgentId: string;
|
|
23
|
+
currentAgentName?: string;
|
|
24
|
+
sourceEventId: string;
|
|
25
|
+
}): {
|
|
26
|
+
currentAgentId: string;
|
|
27
|
+
currentAgentName: string;
|
|
28
|
+
items: RuntimeSurfaceItem[];
|
|
29
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { readSkillMetadata } from "../../support/skill-metadata.js";
|
|
2
|
+
import { createUpstreamTimelineReducer } from "../../../upstream-events.js";
|
|
3
|
+
import { formatAgentName } from "../../../utils/agent-display.js";
|
|
2
4
|
import { getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, } from "../../support/compiled-binding.js";
|
|
3
5
|
import { buildRuntimeGovernanceBundles } from "./governance.js";
|
|
4
6
|
function asObject(value) {
|
|
@@ -7,6 +9,32 @@ function asObject(value) {
|
|
|
7
9
|
function readStringArray(value) {
|
|
8
10
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
9
11
|
}
|
|
12
|
+
function normalizeLabel(value) {
|
|
13
|
+
return value.replace(/\s+/g, " ").trim();
|
|
14
|
+
}
|
|
15
|
+
function slugify(value) {
|
|
16
|
+
return value
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
19
|
+
.replace(/^-+|-+$/g, "")
|
|
20
|
+
.slice(0, 80) || "item";
|
|
21
|
+
}
|
|
22
|
+
function stripStepPrefix(label) {
|
|
23
|
+
return normalizeLabel(label)
|
|
24
|
+
.replace(/^Calling LLM\s+/i, "")
|
|
25
|
+
.replace(/^Completed LLM\s+/i, "")
|
|
26
|
+
.replace(/^Calling tool\s+/i, "")
|
|
27
|
+
.replace(/^Completed tool\s+/i, "")
|
|
28
|
+
.replace(/^Tool\s+/i, "")
|
|
29
|
+
.replace(/\s+failed$/i, "")
|
|
30
|
+
.replace(/^Calling skill\s+/i, "")
|
|
31
|
+
.replace(/^Completed skill\s+/i, "")
|
|
32
|
+
.replace(/^Accessing memory\s+/i, "")
|
|
33
|
+
.replace(/^Completed memory\s+/i, "");
|
|
34
|
+
}
|
|
35
|
+
function buildSurfaceId(kind, value) {
|
|
36
|
+
return `${kind}:${slugify(value)}`;
|
|
37
|
+
}
|
|
10
38
|
function readTracingConfig(binding) {
|
|
11
39
|
const deepAgentTracing = asObject(binding.harnessRuntime?.deepagent?.passthrough)?.tracing;
|
|
12
40
|
const langchainTracing = asObject(binding.harnessRuntime?.langchain?.passthrough)?.tracing;
|
|
@@ -158,3 +186,121 @@ export function consumeRunInspectionUpstreamEvent(input) {
|
|
|
158
186
|
delegationChain: maybeAppendAgent(input.delegationChain, delegatedAgentId),
|
|
159
187
|
};
|
|
160
188
|
}
|
|
189
|
+
function unwrapUpstreamEvent(event) {
|
|
190
|
+
const typed = asObject(event);
|
|
191
|
+
if (!typed) {
|
|
192
|
+
return { event };
|
|
193
|
+
}
|
|
194
|
+
const nestedEvent = asObject(typed.event);
|
|
195
|
+
if (nestedEvent) {
|
|
196
|
+
return {
|
|
197
|
+
event: nestedEvent,
|
|
198
|
+
...(typeof typed.agentId === "string" ? { agentId: typed.agentId } : {}),
|
|
199
|
+
...(typeof typed.agentName === "string" ? { agentName: typed.agentName } : {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return { event };
|
|
203
|
+
}
|
|
204
|
+
function toSurfaceKind(category) {
|
|
205
|
+
switch (category) {
|
|
206
|
+
case "llm":
|
|
207
|
+
case "memory":
|
|
208
|
+
case "skill":
|
|
209
|
+
case "tool":
|
|
210
|
+
return category;
|
|
211
|
+
default:
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export function projectRuntimeSurfaceFromUpstreamEvents(input) {
|
|
216
|
+
const items = [];
|
|
217
|
+
let currentAgentId = input.initialAgentId;
|
|
218
|
+
let currentAgentName = formatAgentName(currentAgentId);
|
|
219
|
+
for (const [index, rawEvent] of input.upstreamEvents.entries()) {
|
|
220
|
+
const projected = projectRuntimeSurfaceFromSingleUpstreamEvent({
|
|
221
|
+
event: rawEvent,
|
|
222
|
+
binding: input.binding,
|
|
223
|
+
currentAgentId,
|
|
224
|
+
currentAgentName,
|
|
225
|
+
sourceEventId: `upstream:${index + 1}`,
|
|
226
|
+
});
|
|
227
|
+
currentAgentId = projected.currentAgentId;
|
|
228
|
+
currentAgentName = projected.currentAgentName;
|
|
229
|
+
items.push(...projected.items);
|
|
230
|
+
}
|
|
231
|
+
return items;
|
|
232
|
+
}
|
|
233
|
+
export function projectRuntimeSurfaceFromSingleUpstreamEvent(input) {
|
|
234
|
+
const reducer = createUpstreamTimelineReducer();
|
|
235
|
+
const items = [];
|
|
236
|
+
let currentAgentId = input.currentAgentId;
|
|
237
|
+
let currentAgentName = input.currentAgentName ?? formatAgentName(currentAgentId);
|
|
238
|
+
const unwrapped = unwrapUpstreamEvent(input.event);
|
|
239
|
+
if (unwrapped.agentId) {
|
|
240
|
+
if (unwrapped.agentId !== currentAgentId) {
|
|
241
|
+
const nextAgentName = unwrapped.agentName ?? formatAgentName(unwrapped.agentId);
|
|
242
|
+
items.push({
|
|
243
|
+
kind: "agent",
|
|
244
|
+
id: buildSurfaceId("agent", unwrapped.agentId),
|
|
245
|
+
name: nextAgentName,
|
|
246
|
+
label: `Delegate to ${nextAgentName}`,
|
|
247
|
+
status: "completed",
|
|
248
|
+
ownerAgentId: currentAgentId,
|
|
249
|
+
ownerAgentName: currentAgentName,
|
|
250
|
+
sourceEventId: input.sourceEventId,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
currentAgentId = unwrapped.agentId;
|
|
254
|
+
currentAgentName = unwrapped.agentName ?? formatAgentName(currentAgentId);
|
|
255
|
+
}
|
|
256
|
+
else if (unwrapped.agentName) {
|
|
257
|
+
currentAgentName = unwrapped.agentName;
|
|
258
|
+
}
|
|
259
|
+
const typed = asObject(unwrapped.event);
|
|
260
|
+
if (typed && input.binding) {
|
|
261
|
+
const next = consumeRunInspectionUpstreamEvent({
|
|
262
|
+
event: typed,
|
|
263
|
+
currentAgentId,
|
|
264
|
+
delegationChain: [currentAgentId],
|
|
265
|
+
binding: input.binding,
|
|
266
|
+
});
|
|
267
|
+
if (next.currentAgentId !== currentAgentId) {
|
|
268
|
+
currentAgentId = next.currentAgentId;
|
|
269
|
+
currentAgentName = formatAgentName(currentAgentId);
|
|
270
|
+
items.push({
|
|
271
|
+
kind: "agent",
|
|
272
|
+
id: buildSurfaceId("agent", currentAgentId),
|
|
273
|
+
name: currentAgentName,
|
|
274
|
+
label: `Delegate to ${currentAgentName}`,
|
|
275
|
+
status: "completed",
|
|
276
|
+
ownerAgentId: next.delegationChain.at(-2),
|
|
277
|
+
ownerAgentName: next.delegationChain.at(-2) ? formatAgentName(next.delegationChain.at(-2)) : undefined,
|
|
278
|
+
sourceEventId: input.sourceEventId,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
for (const projection of reducer.consume(unwrapped.event)) {
|
|
283
|
+
if (projection.type !== "step") {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const kind = toSurfaceKind(projection.category);
|
|
287
|
+
if (!kind) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
items.push({
|
|
291
|
+
kind,
|
|
292
|
+
id: buildSurfaceId(kind, stripStepPrefix(projection.step) || projection.step),
|
|
293
|
+
name: stripStepPrefix(projection.step) || formatAgentName(kind),
|
|
294
|
+
label: projection.step,
|
|
295
|
+
status: projection.status,
|
|
296
|
+
ownerAgentId: currentAgentId,
|
|
297
|
+
ownerAgentName: currentAgentName,
|
|
298
|
+
sourceEventId: input.sourceEventId,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
currentAgentId,
|
|
303
|
+
currentAgentName,
|
|
304
|
+
items,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
@@ -2,7 +2,8 @@ import { AGENT_INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError } from ".
|
|
|
2
2
|
import { renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
|
|
3
3
|
import { getBindingPrimaryModel } from "../../support/compiled-binding.js";
|
|
4
4
|
import { createContentBlocksItem, createToolResultKey, } from "../events/streaming.js";
|
|
5
|
-
import { consumeRunInspectionUpstreamEvent } from "./inspection.js";
|
|
5
|
+
import { consumeRunInspectionUpstreamEvent, projectRuntimeSurfaceFromSingleUpstreamEvent } from "./inspection.js";
|
|
6
|
+
import { formatAgentName } from "../../../utils/agent-display.js";
|
|
6
7
|
function normalizeStreamChunk(chunk) {
|
|
7
8
|
if (typeof chunk === "string") {
|
|
8
9
|
if (chunk.startsWith(AGENT_INTERRUPT_SENTINEL_PREFIX)) {
|
|
@@ -45,7 +46,9 @@ export async function* streamHarnessRun(options) {
|
|
|
45
46
|
let streamActivityObserved = false;
|
|
46
47
|
let nonUpstreamStreamActivityObserved = false;
|
|
47
48
|
let currentAgentId = options.selectedAgentId;
|
|
49
|
+
let currentAgentName = formatAgentName(options.selectedAgentId);
|
|
48
50
|
let delegationChain = [options.selectedAgentId];
|
|
51
|
+
let upstreamEventOrdinal = 0;
|
|
49
52
|
let syntheticFallback;
|
|
50
53
|
try {
|
|
51
54
|
const [priorHistory, acquiredReleaseRunSlot] = await Promise.all([
|
|
@@ -69,6 +72,18 @@ export async function* streamHarnessRun(options) {
|
|
|
69
72
|
streamActivityObserved = true;
|
|
70
73
|
const normalizedChunk = normalizeStreamChunk(rawChunk);
|
|
71
74
|
if (normalizedChunk.kind === "upstream-event") {
|
|
75
|
+
upstreamEventOrdinal += 1;
|
|
76
|
+
const surfaceProjection = projectRuntimeSurfaceFromSingleUpstreamEvent({
|
|
77
|
+
event: {
|
|
78
|
+
agentId: currentAgentId,
|
|
79
|
+
agentName: currentAgentName,
|
|
80
|
+
event: normalizedChunk.event,
|
|
81
|
+
},
|
|
82
|
+
binding: options.binding,
|
|
83
|
+
currentAgentId,
|
|
84
|
+
currentAgentName,
|
|
85
|
+
sourceEventId: `upstream:${upstreamEventOrdinal}`,
|
|
86
|
+
});
|
|
72
87
|
const inspection = consumeRunInspectionUpstreamEvent({
|
|
73
88
|
event: normalizedChunk.event,
|
|
74
89
|
currentAgentId,
|
|
@@ -76,17 +91,24 @@ export async function* streamHarnessRun(options) {
|
|
|
76
91
|
binding: options.binding,
|
|
77
92
|
});
|
|
78
93
|
currentAgentId = inspection.currentAgentId;
|
|
94
|
+
currentAgentName = formatAgentName(currentAgentId);
|
|
79
95
|
delegationChain = inspection.delegationChain;
|
|
80
96
|
await options.updateRunInspection(options.threadId, options.runId, {
|
|
81
97
|
currentAgentId,
|
|
82
98
|
delegationChain,
|
|
83
|
-
appendUpstreamEvent:
|
|
99
|
+
appendUpstreamEvent: {
|
|
100
|
+
agentId: currentAgentId,
|
|
101
|
+
agentName: currentAgentName,
|
|
102
|
+
event: normalizedChunk.event,
|
|
103
|
+
},
|
|
84
104
|
});
|
|
85
105
|
yield {
|
|
86
106
|
type: "upstream-event",
|
|
87
107
|
threadId: options.threadId,
|
|
88
108
|
runId: options.runId,
|
|
89
109
|
agentId: currentAgentId,
|
|
110
|
+
agentName: currentAgentName,
|
|
111
|
+
surfaceItems: surfaceProjection.items,
|
|
90
112
|
event: normalizedChunk.event,
|
|
91
113
|
};
|
|
92
114
|
continue;
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { isTerminalRunState, toInspectableApprovalRecord } from "./helpers.js";
|
|
2
2
|
import { projectRuntimeTimeline } from "../events/timeline.js";
|
|
3
3
|
import { createUpstreamTimelineReducer } from "../../../upstream-events.js";
|
|
4
|
+
import { projectRuntimeSurfaceFromUpstreamEvents } from "./inspection.js";
|
|
5
|
+
function unwrapPersistedUpstreamEvent(event) {
|
|
6
|
+
if (typeof event !== "object" || event === null || Array.isArray(event)) {
|
|
7
|
+
return event;
|
|
8
|
+
}
|
|
9
|
+
const typed = event;
|
|
10
|
+
if (Object.prototype.hasOwnProperty.call(typed, "event")
|
|
11
|
+
&& typeof typed.event === "object"
|
|
12
|
+
&& typed.event !== null
|
|
13
|
+
&& !Array.isArray(typed.event)) {
|
|
14
|
+
return typed.event;
|
|
15
|
+
}
|
|
16
|
+
return event;
|
|
17
|
+
}
|
|
4
18
|
function selectLatestPendingApproval(approvals) {
|
|
5
19
|
return approvals
|
|
6
20
|
.filter((approval) => approval.status === "pending")
|
|
@@ -8,7 +22,7 @@ function selectLatestPendingApproval(approvals) {
|
|
|
8
22
|
}
|
|
9
23
|
function buildRunInspectionProjection(upstreamEvents) {
|
|
10
24
|
const reducer = createUpstreamTimelineReducer();
|
|
11
|
-
const history = upstreamEvents.flatMap((event) => reducer.consume(event));
|
|
25
|
+
const history = upstreamEvents.flatMap((event) => reducer.consume(unwrapPersistedUpstreamEvent(event)));
|
|
12
26
|
return {
|
|
13
27
|
upstreamEvents,
|
|
14
28
|
history,
|
|
@@ -18,10 +32,15 @@ export async function buildRequestInspectionRecord(persistence, request) {
|
|
|
18
32
|
const inspection = await persistence.getRunInspection(request.threadId, request.runId);
|
|
19
33
|
const runtimeEvents = await persistence.listRunEvents(request.threadId, request.runId);
|
|
20
34
|
const { upstreamEvents, history } = buildRunInspectionProjection(inspection.upstreamEvents);
|
|
35
|
+
const runtimeSurface = projectRuntimeSurfaceFromUpstreamEvents({
|
|
36
|
+
upstreamEvents,
|
|
37
|
+
initialAgentId: request.agentId ?? inspection.currentAgentId ?? "agent",
|
|
38
|
+
});
|
|
21
39
|
return {
|
|
22
40
|
requestId: request.runId,
|
|
23
41
|
sessionId: request.threadId,
|
|
24
42
|
agentId: request.agentId,
|
|
43
|
+
parentRunId: request.parentRunId,
|
|
25
44
|
executionMode: request.executionMode,
|
|
26
45
|
adapterKind: request.adapterKind,
|
|
27
46
|
createdAt: request.createdAt,
|
|
@@ -37,6 +56,7 @@ export async function buildRequestInspectionRecord(persistence, request) {
|
|
|
37
56
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
38
57
|
upstreamEvents,
|
|
39
58
|
history,
|
|
59
|
+
runtimeSurface,
|
|
40
60
|
runtimeTimeline: projectRuntimeTimeline(runtimeEvents, {
|
|
41
61
|
threadId: request.threadId,
|
|
42
62
|
runId: request.runId,
|
|
@@ -48,6 +68,7 @@ function toRunRecord(request) {
|
|
|
48
68
|
runId: request.requestId,
|
|
49
69
|
threadId: request.sessionId,
|
|
50
70
|
agentId: request.agentId,
|
|
71
|
+
parentRunId: request.parentRunId,
|
|
51
72
|
executionMode: request.executionMode,
|
|
52
73
|
adapterKind: request.adapterKind,
|
|
53
74
|
createdAt: request.createdAt,
|
|
@@ -63,6 +84,7 @@ function toRunRecord(request) {
|
|
|
63
84
|
runtimeSnapshot: request.runtimeSnapshot,
|
|
64
85
|
upstreamEvents: request.upstreamEvents,
|
|
65
86
|
history: request.history,
|
|
87
|
+
runtimeSurface: request.runtimeSurface,
|
|
66
88
|
runtimeTimeline: request.runtimeTimeline,
|
|
67
89
|
};
|
|
68
90
|
}
|
package/dist/runtime/harness.js
CHANGED
|
@@ -385,6 +385,7 @@ export class AgentHarnessRuntime {
|
|
|
385
385
|
requestId: request.runId,
|
|
386
386
|
sessionId: request.threadId,
|
|
387
387
|
agentId: request.agentId,
|
|
388
|
+
parentRunId: request.parentRunId,
|
|
388
389
|
executionMode: request.executionMode,
|
|
389
390
|
adapterKind: request.adapterKind,
|
|
390
391
|
createdAt: request.createdAt,
|
|
@@ -410,6 +411,7 @@ export class AgentHarnessRuntime {
|
|
|
410
411
|
runId: request.requestId,
|
|
411
412
|
threadId: request.sessionId,
|
|
412
413
|
agentId: request.agentId,
|
|
414
|
+
parentRunId: request.parentRunId,
|
|
413
415
|
executionMode: request.executionMode,
|
|
414
416
|
adapterKind: request.adapterKind,
|
|
415
417
|
createdAt: request.createdAt,
|
|
@@ -1800,6 +1802,7 @@ function toRequestSummary(summary) {
|
|
|
1800
1802
|
requestId: summary.runId,
|
|
1801
1803
|
sessionId: summary.threadId,
|
|
1802
1804
|
agentId: summary.agentId,
|
|
1805
|
+
parentRunId: summary.parentRunId,
|
|
1803
1806
|
executionMode: summary.executionMode,
|
|
1804
1807
|
adapterKind: summary.adapterKind,
|
|
1805
1808
|
createdAt: summary.createdAt,
|
|
@@ -1834,6 +1837,7 @@ function toRequestRecord(record) {
|
|
|
1834
1837
|
...toRequestSummary(record),
|
|
1835
1838
|
upstreamEvents: record.upstreamEvents,
|
|
1836
1839
|
history: record.history,
|
|
1840
|
+
runtimeSurface: record.runtimeSurface,
|
|
1837
1841
|
runtimeTimeline: record.runtimeTimeline,
|
|
1838
1842
|
};
|
|
1839
1843
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatAgentName(agentId: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function normalizeSegment(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
4
|
+
.replace(/[_-]+/g, " ")
|
|
5
|
+
.replace(/\s+/g, " ")
|
|
6
|
+
.trim();
|
|
7
|
+
}
|
|
8
|
+
export function formatAgentName(agentId) {
|
|
9
|
+
const normalized = normalizeSegment(agentId);
|
|
10
|
+
if (!normalized) {
|
|
11
|
+
return "Agent";
|
|
12
|
+
}
|
|
13
|
+
return normalized
|
|
14
|
+
.split(" ")
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
17
|
+
.join(" ");
|
|
18
|
+
}
|