@botbotgo/agent-harness 0.0.120 → 0.0.121
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 +1 -0
- package/dist/api.js +1 -0
- package/dist/contracts/runtime.d.ts +3 -49
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/runtime/adapter/stream-event-projection.d.ts +0 -1
- package/dist/runtime/adapter/stream-event-projection.js +2 -110
- package/dist/runtime/harness/events/listener-runtime.d.ts +3 -2
- package/dist/runtime/harness/events/streaming.d.ts +24 -3
- package/dist/runtime/harness/events/streaming.js +0 -20
- package/dist/runtime/harness/run/stream-run.d.ts +4 -6
- package/dist/runtime/harness/run/stream-run.js +1 -31
- package/dist/runtime/harness.js +10 -11
- package/dist/runtime/parsing/index.d.ts +0 -1
- package/dist/runtime/parsing/stream-event-parsing.d.ts +1 -7
- package/dist/runtime/parsing/stream-event-parsing.js +0 -222
- package/dist/upstream-events.d.ts +20 -0
- package/dist/upstream-events.js +172 -0
- package/package.json +1 -1
- package/dist/runtime/parsing/stream-event-types.d.ts +0 -62
- package/dist/runtime/parsing/stream-event-types.js +0 -1
package/dist/api.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { InventoryAgentRecord, InventorySkillRecord } from "./runtime/harne
|
|
|
4
4
|
import type { RequirementAssessmentOptions } from "./runtime/harness/system/skill-requirements.js";
|
|
5
5
|
import type { ToolMcpServerOptions } from "./mcp.js";
|
|
6
6
|
export { AgentHarnessRuntime } from "./runtime/harness.js";
|
|
7
|
+
export { createUpstreamTimelineReducer } from "./upstream-events.js";
|
|
7
8
|
type CreateAgentHarnessOptions = {
|
|
8
9
|
/**
|
|
9
10
|
* Workspace loading behavior.
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AgentHarnessRuntime } from "./runtime/harness.js";
|
|
2
2
|
import { loadWorkspace } from "./workspace/compile.js";
|
|
3
3
|
export { AgentHarnessRuntime } from "./runtime/harness.js";
|
|
4
|
+
export { createUpstreamTimelineReducer } from "./upstream-events.js";
|
|
4
5
|
export async function createAgentHarness(workspaceRoot = process.cwd(), options = {}) {
|
|
5
6
|
const workspace = await loadWorkspace(workspaceRoot, options.load ?? {});
|
|
6
7
|
const harness = new AgentHarnessRuntime(workspace, options.adapter ?? {});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RunState } from "./core.js";
|
|
2
2
|
import type { CompiledAgentBinding, CompiledModel, CompiledTool, ParsedAgentObject, ParsedToolObject, WorkspaceBundle } from "./workspace.js";
|
|
3
|
-
import type { CompatibleStreamPart as InternalCompatibleStreamPart, NormalizedUpstreamEvent as InternalNormalizedUpstreamEvent, UpstreamRuntimeEvent as InternalUpstreamRuntimeEvent } from "../runtime/parsing/stream-event-types.js";
|
|
4
3
|
export type ThreadSummary = {
|
|
5
4
|
agentId: string;
|
|
6
5
|
threadId: string;
|
|
@@ -10,7 +9,7 @@ export type ThreadSummary = {
|
|
|
10
9
|
status: RunState;
|
|
11
10
|
};
|
|
12
11
|
export type SessionRecord = ThreadSummary;
|
|
13
|
-
export type KnownHarnessEventType = "run.created" | "run.queued" | "run.dequeued" | "run.state.changed" | "run.resumed" | "approval.requested" | "approval.resolved" | "artifact.created" | "output.delta" | "
|
|
12
|
+
export type KnownHarnessEventType = "run.created" | "run.queued" | "run.dequeued" | "run.state.changed" | "run.resumed" | "approval.requested" | "approval.resolved" | "artifact.created" | "output.delta" | "runtime.health.changed" | "runtime.synthetic_fallback";
|
|
14
13
|
export type HarnessEventType = KnownHarnessEventType | (string & {});
|
|
15
14
|
export type HarnessEvent = {
|
|
16
15
|
eventId: string;
|
|
@@ -86,25 +85,12 @@ export type RunResult = {
|
|
|
86
85
|
artifacts?: ArtifactRecord[];
|
|
87
86
|
metadata?: Record<string, unknown>;
|
|
88
87
|
};
|
|
89
|
-
export type
|
|
90
|
-
export type CompatibleStreamPart = InternalCompatibleStreamPart;
|
|
91
|
-
export type UpstreamRuntimeEvent = InternalUpstreamRuntimeEvent;
|
|
88
|
+
export type UpstreamRuntimeEvent = unknown;
|
|
92
89
|
export type RuntimeListeners = {
|
|
93
90
|
onEvent?: (event: HarnessEvent) => void | Promise<void>;
|
|
94
|
-
};
|
|
95
|
-
export type FrontendStreamListeners = {
|
|
96
|
-
onChunk?: (chunk: string) => void | Promise<void>;
|
|
97
|
-
onContentBlocks?: (blocks: unknown[]) => void | Promise<void>;
|
|
98
91
|
onUpstreamEvent?: (event: UpstreamRuntimeEvent) => void | Promise<void>;
|
|
99
|
-
onReasoning?: (chunk: string) => void | Promise<void>;
|
|
100
|
-
onStep?: (step: string) => void | Promise<void>;
|
|
101
|
-
onToolResult?: (item: {
|
|
102
|
-
toolName: string;
|
|
103
|
-
output: unknown;
|
|
104
|
-
isError?: boolean;
|
|
105
|
-
}) => void | Promise<void>;
|
|
106
92
|
};
|
|
107
|
-
export type RunListeners = RuntimeListeners
|
|
93
|
+
export type RunListeners = RuntimeListeners;
|
|
108
94
|
export type MessageContentPart = {
|
|
109
95
|
type: "text";
|
|
110
96
|
text: string;
|
|
@@ -148,38 +134,6 @@ export type HarnessStreamItem = {
|
|
|
148
134
|
} | {
|
|
149
135
|
type: "result";
|
|
150
136
|
result: RunResult;
|
|
151
|
-
} | {
|
|
152
|
-
type: "content";
|
|
153
|
-
threadId: string;
|
|
154
|
-
runId: string;
|
|
155
|
-
agentId: string;
|
|
156
|
-
content: string;
|
|
157
|
-
} | {
|
|
158
|
-
type: "content-blocks";
|
|
159
|
-
threadId: string;
|
|
160
|
-
runId: string;
|
|
161
|
-
agentId: string;
|
|
162
|
-
contentBlocks: unknown[];
|
|
163
|
-
} | {
|
|
164
|
-
type: "reasoning";
|
|
165
|
-
threadId: string;
|
|
166
|
-
runId: string;
|
|
167
|
-
agentId: string;
|
|
168
|
-
content: string;
|
|
169
|
-
} | {
|
|
170
|
-
type: "step";
|
|
171
|
-
threadId: string;
|
|
172
|
-
runId: string;
|
|
173
|
-
agentId: string;
|
|
174
|
-
content: string;
|
|
175
|
-
} | {
|
|
176
|
-
type: "tool-result";
|
|
177
|
-
threadId: string;
|
|
178
|
-
runId: string;
|
|
179
|
-
agentId: string;
|
|
180
|
-
toolName: string;
|
|
181
|
-
output: unknown;
|
|
182
|
-
isError?: boolean;
|
|
183
137
|
};
|
|
184
138
|
export type TranscriptMessage = {
|
|
185
139
|
role: "user" | "assistant";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
1
|
+
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
2
2
|
export type { ToolMcpServerOptions } from "./mcp.js";
|
|
3
3
|
export { tool } from "./tools.js";
|
|
4
|
+
export type { UpstreamTimelineProjection, UpstreamTimelineReducer } from "./upstream-events.js";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
1
|
+
export { AgentHarnessRuntime, cancelRun, createAgentHarness, createUpstreamTimelineReducer, createToolMcpServer, deleteThread, describeInventory, getApproval, getHealth, getRun, getThread, listAgentSkills, listApprovals, listRuns, listThreads, resolveApproval, run, serveToolsOverStdio, subscribe, stop, } from "./api.js";
|
|
2
2
|
export { tool } from "./tools.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export declare const AGENT_HARNESS_VERSION = "0.0.120";
|
package/dist/package-version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AGENT_HARNESS_VERSION = "0.0.
|
|
1
|
+
export const AGENT_HARNESS_VERSION = "0.0.120";
|
|
@@ -6,7 +6,6 @@ export type StreamEventProjectionState = {
|
|
|
6
6
|
emittedToolError: boolean;
|
|
7
7
|
emittedToolResult: boolean;
|
|
8
8
|
seenTerminalOutputs: Set<string>;
|
|
9
|
-
lastStep: string;
|
|
10
9
|
};
|
|
11
10
|
export declare function createStreamEventProjectionState(): StreamEventProjectionState;
|
|
12
11
|
export declare function projectRuntimeStreamEvent(params: {
|
|
@@ -1,122 +1,19 @@
|
|
|
1
1
|
import { sanitizeVisibleText } from "../parsing/output-parsing.js";
|
|
2
|
-
import { computeIncrementalOutput, extractInterruptPayload, extractReasoningStreamOutput, extractStateStreamOutput, extractTerminalStreamOutput, extractToolResult, extractVisibleStreamOutput, normalizeTerminalOutputKey,
|
|
2
|
+
import { computeIncrementalOutput, extractInterruptPayload, extractReasoningStreamOutput, extractStateStreamOutput, extractTerminalStreamOutput, extractToolResult, extractVisibleStreamOutput, normalizeTerminalOutputKey, } from "../parsing/stream-event-parsing.js";
|
|
3
3
|
import { resolveModelFacingToolName } from "./tool/tool-name-mapping.js";
|
|
4
|
-
function formatStepValue(value, maxLength = 120) {
|
|
5
|
-
if (value == null)
|
|
6
|
-
return "";
|
|
7
|
-
const normalized = typeof value === "string"
|
|
8
|
-
? parseMaybeJson(value)
|
|
9
|
-
: value;
|
|
10
|
-
const summarized = summarizeStepValue(normalized);
|
|
11
|
-
const serialized = typeof summarized === "string"
|
|
12
|
-
? summarized
|
|
13
|
-
: (() => {
|
|
14
|
-
try {
|
|
15
|
-
return JSON.stringify(summarized);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return String(summarized);
|
|
19
|
-
}
|
|
20
|
-
})();
|
|
21
|
-
return serialized.length > maxLength ? serialized.slice(0, maxLength) + "…" : serialized;
|
|
22
|
-
}
|
|
23
|
-
function parseMaybeJson(value) {
|
|
24
|
-
const trimmed = value.trim();
|
|
25
|
-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
return JSON.parse(trimmed);
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return value;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function summarizeStepValue(value) {
|
|
36
|
-
if (typeof value !== "object" || !value) {
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
const record = value;
|
|
40
|
-
if (typeof record.url === "string" && typeof record.status === "number") {
|
|
41
|
-
const warnings = Array.isArray(record.warnings) ? record.warnings.filter((item) => typeof item === "string") : [];
|
|
42
|
-
return {
|
|
43
|
-
status: record.status,
|
|
44
|
-
ok: record.ok,
|
|
45
|
-
quality: record.quality,
|
|
46
|
-
source: record.source,
|
|
47
|
-
warnings,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return value;
|
|
51
|
-
}
|
|
52
|
-
function humanizeEventName(name) {
|
|
53
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
|
|
54
|
-
}
|
|
55
|
-
function deriveAgentStep(event) {
|
|
56
|
-
if (typeof event !== "object" || !event)
|
|
57
|
-
return null;
|
|
58
|
-
const typed = event;
|
|
59
|
-
const name = typeof typed.name === "string" ? typed.name : "";
|
|
60
|
-
if (typed.event === "on_tool_start" || (typed.event === "on_chain_start" && typed.run_type === "tool")) {
|
|
61
|
-
const inputSummary = formatStepValue(typed.data?.input);
|
|
62
|
-
return inputSummary ? `tool ${name || "tool"} ${inputSummary}` : `tool ${name || "tool"}`;
|
|
63
|
-
}
|
|
64
|
-
if (typed.event === "on_tool_end" || (typed.event === "on_chain_end" && typed.run_type === "tool")) {
|
|
65
|
-
const outputSummary = formatStepValue(typed.data?.output, 80);
|
|
66
|
-
return outputSummary ? `tool ${name || "tool"} done ${outputSummary}` : `tool ${name || "tool"} done`;
|
|
67
|
-
}
|
|
68
|
-
if (typed.event === "on_tool_error" || (typed.event === "on_chain_error" && typed.run_type === "tool")) {
|
|
69
|
-
const errorSummary = formatStepValue(typed.data?.error ?? typed.data?.output, 80);
|
|
70
|
-
return errorSummary ? `tool ${name || "tool"} error ${errorSummary}` : `tool ${name || "tool"} error`;
|
|
71
|
-
}
|
|
72
|
-
if (typed.event === "on_chat_model_start") {
|
|
73
|
-
const inputTools = typed.data?.input?.tools;
|
|
74
|
-
const tools = typed.invocation_params?.tools ?? inputTools ?? [];
|
|
75
|
-
const toolNames = tools.map((tool) => tool.name ?? tool.function?.name).filter((toolName) => typeof toolName === "string").join(", ");
|
|
76
|
-
return toolNames ? `calling model tools: [${toolNames}]` : "calling model…";
|
|
77
|
-
}
|
|
78
|
-
if (typed.event === "on_chain_start") {
|
|
79
|
-
if (/SkillsMiddleware/i.test(name))
|
|
80
|
-
return "loading skills…";
|
|
81
|
-
if (/MemoryMiddleware/i.test(name))
|
|
82
|
-
return "loading memory…";
|
|
83
|
-
if (/patchToolCallsMiddleware/i.test(name))
|
|
84
|
-
return "normalizing tool calls…";
|
|
85
|
-
if (/HumanInTheLoopMiddleware/i.test(name))
|
|
86
|
-
return "checking approvals…";
|
|
87
|
-
if (/todoListMiddleware/i.test(name))
|
|
88
|
-
return "updating plan…";
|
|
89
|
-
if (/model_request/i.test(name))
|
|
90
|
-
return "preparing model request…";
|
|
91
|
-
if (/RunnableLambda/i.test(name) || /^__start__$/.test(name))
|
|
92
|
-
return null;
|
|
93
|
-
if (/(subagent|specialist)/i.test(name))
|
|
94
|
-
return `starting ${humanizeEventName(name)}…`;
|
|
95
|
-
if (/agent$/i.test(name))
|
|
96
|
-
return `running ${humanizeEventName(name)}…`;
|
|
97
|
-
}
|
|
98
|
-
if (typed.event === "on_chain_end") {
|
|
99
|
-
if (/(subagent|specialist)/i.test(name))
|
|
100
|
-
return `${humanizeEventName(name)} done`;
|
|
101
|
-
if (/agent$/i.test(name) && !/^__start__$/.test(name))
|
|
102
|
-
return `${humanizeEventName(name)} done`;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
4
|
export function createStreamEventProjectionState() {
|
|
107
5
|
return {
|
|
108
6
|
emittedOutput: "",
|
|
109
7
|
emittedToolError: false,
|
|
110
8
|
emittedToolResult: false,
|
|
111
9
|
seenTerminalOutputs: new Set(),
|
|
112
|
-
lastStep: "",
|
|
113
10
|
};
|
|
114
11
|
}
|
|
115
12
|
export function projectRuntimeStreamEvent(params) {
|
|
116
13
|
const { event, allowVisibleStreamDeltas, includeStateStreamOutput, toolNameMapping, primaryTools, state, } = params;
|
|
117
14
|
const chunks = [{
|
|
118
15
|
kind: "upstream-event",
|
|
119
|
-
event
|
|
16
|
+
event,
|
|
120
17
|
}];
|
|
121
18
|
const interruptPayload = extractInterruptPayload(event);
|
|
122
19
|
if (interruptPayload) {
|
|
@@ -147,11 +44,6 @@ export function projectRuntimeStreamEvent(params) {
|
|
|
147
44
|
}
|
|
148
45
|
}
|
|
149
46
|
}
|
|
150
|
-
const agentStep = deriveAgentStep(event);
|
|
151
|
-
if (agentStep && agentStep !== state.lastStep) {
|
|
152
|
-
state.lastStep = agentStep;
|
|
153
|
-
chunks.push({ kind: "step", content: agentStep });
|
|
154
|
-
}
|
|
155
47
|
const toolResult = extractToolResult(event);
|
|
156
48
|
if (toolResult) {
|
|
157
49
|
state.emittedToolResult = true;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunListeners, RunResult } from "../../../contracts/types.js";
|
|
2
|
+
import { type InternalHarnessStreamItem } from "./streaming.js";
|
|
2
3
|
export declare function createListenerDispatchRuntime(input: {
|
|
3
4
|
notifyListener: <T>(listener: ((value: T) => void | Promise<void>) | undefined, value: T) => Promise<void>;
|
|
4
5
|
getThread: (threadId: string) => Promise<{
|
|
@@ -14,5 +15,5 @@ export declare function createListenerDispatchRuntime(input: {
|
|
|
14
15
|
};
|
|
15
16
|
} | null>;
|
|
16
17
|
}): {
|
|
17
|
-
dispatchRunListeners: (stream: AsyncGenerator<
|
|
18
|
+
dispatchRunListeners: (stream: AsyncGenerator<InternalHarnessStreamItem>, listeners: RunListeners) => Promise<RunResult>;
|
|
18
19
|
};
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import type { HarnessEvent, HarnessStreamItem, RunListeners, RunResult } from "../../../contracts/types.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
2
|
+
export type InternalHarnessStreamItem = HarnessStreamItem | {
|
|
3
|
+
type: "content";
|
|
4
|
+
threadId: string;
|
|
5
|
+
runId: string;
|
|
6
|
+
agentId: string;
|
|
7
|
+
content: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: "content-blocks";
|
|
10
|
+
threadId: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
agentId: string;
|
|
13
|
+
contentBlocks: unknown[];
|
|
14
|
+
} | {
|
|
15
|
+
type: "tool-result";
|
|
16
|
+
threadId: string;
|
|
17
|
+
runId: string;
|
|
18
|
+
agentId: string;
|
|
19
|
+
toolName: string;
|
|
20
|
+
output: unknown;
|
|
21
|
+
isError?: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function emitOutputDeltaAndCreateItem(emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<HarnessEvent>, threadId: string, runId: string, agentId: string, content: string): Promise<InternalHarnessStreamItem>;
|
|
24
|
+
export declare function createContentBlocksItem(threadId: string, runId: string, agentId: string, contentBlocks: unknown[]): InternalHarnessStreamItem;
|
|
4
25
|
export declare function createToolResultKey(toolName: string, output: unknown, isError?: boolean): string;
|
|
5
|
-
export declare function dispatchRunListeners(stream: AsyncGenerator<
|
|
26
|
+
export declare function dispatchRunListeners(stream: AsyncGenerator<InternalHarnessStreamItem>, listeners: RunListeners, options: {
|
|
6
27
|
notifyListener: <T>(listener: ((value: T) => void | Promise<void>) | undefined, value: T) => Promise<void>;
|
|
7
28
|
getThread: (threadId: string) => Promise<{
|
|
8
29
|
currentState: RunResult["state"];
|
|
@@ -50,28 +50,8 @@ export async function dispatchRunListeners(stream, listeners, options) {
|
|
|
50
50
|
}
|
|
51
51
|
if (item.type === "content") {
|
|
52
52
|
output += item.content;
|
|
53
|
-
await options.notifyListener(listeners.onChunk, item.content);
|
|
54
53
|
continue;
|
|
55
54
|
}
|
|
56
|
-
if (item.type === "content-blocks") {
|
|
57
|
-
await options.notifyListener(listeners.onContentBlocks, item.contentBlocks);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (item.type === "reasoning") {
|
|
61
|
-
await options.notifyListener(listeners.onReasoning, item.content);
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
if (item.type === "step") {
|
|
65
|
-
await options.notifyListener(listeners.onStep, item.content);
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
if (item.type === "tool-result") {
|
|
69
|
-
await options.notifyListener(listeners.onToolResult, {
|
|
70
|
-
toolName: item.toolName,
|
|
71
|
-
output: item.output,
|
|
72
|
-
isError: item.isError,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
55
|
}
|
|
76
56
|
if (!latestEvent) {
|
|
77
57
|
throw new Error("run did not emit any events");
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import type { CompiledAgentBinding, HarnessEvent,
|
|
1
|
+
import type { CompiledAgentBinding, HarnessEvent, MessageContent, RunResult, TranscriptMessage } from "../../../contracts/types.js";
|
|
2
|
+
import { type InternalHarnessStreamItem } from "../events/streaming.js";
|
|
2
3
|
type RuntimeStreamChunk = string | {
|
|
3
4
|
kind: "content" | "interrupt" | "reasoning" | "step" | "tool-result" | "upstream-event";
|
|
4
5
|
content?: string;
|
|
5
6
|
toolName?: string;
|
|
6
7
|
output?: unknown;
|
|
7
8
|
isError?: boolean;
|
|
8
|
-
event?:
|
|
9
|
-
format?: string;
|
|
10
|
-
raw?: unknown;
|
|
11
|
-
} | Record<string, unknown>;
|
|
9
|
+
event?: unknown;
|
|
12
10
|
};
|
|
13
11
|
type StreamRunOptions = {
|
|
14
12
|
binding: CompiledAgentBinding;
|
|
@@ -49,5 +47,5 @@ type StreamRunOptions = {
|
|
|
49
47
|
clearRunRequest: (threadId: string, runId: string) => Promise<void>;
|
|
50
48
|
emitSyntheticFallback: (threadId: string, runId: string, selectedAgentId: string, error: unknown) => Promise<void>;
|
|
51
49
|
};
|
|
52
|
-
export declare function streamHarnessRun(options: StreamRunOptions): AsyncGenerator<
|
|
50
|
+
export declare function streamHarnessRun(options: StreamRunOptions): AsyncGenerator<InternalHarnessStreamItem>;
|
|
53
51
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AGENT_INTERRUPT_SENTINEL_PREFIX, RuntimeOperationTimeoutError } from "../../agent-runtime-adapter.js";
|
|
2
|
-
import { normalizeUpstreamRuntimeEvent } from "../../parsing/stream-event-parsing.js";
|
|
3
2
|
import { renderRuntimeFailure, renderToolFailure } from "../../support/harness-support.js";
|
|
4
3
|
import { createContentBlocksItem, createToolResultKey, emitOutputDeltaAndCreateItem, } from "../events/streaming.js";
|
|
5
4
|
function normalizeStreamChunk(chunk) {
|
|
@@ -18,9 +17,6 @@ function normalizeStreamChunk(chunk) {
|
|
|
18
17
|
if (chunk.kind === "reasoning") {
|
|
19
18
|
return { kind: "reasoning", content: chunk.content ?? "" };
|
|
20
19
|
}
|
|
21
|
-
if (chunk.kind === "step") {
|
|
22
|
-
return { kind: "step", content: chunk.content ?? "" };
|
|
23
|
-
}
|
|
24
20
|
if (chunk.kind === "tool-result") {
|
|
25
21
|
return {
|
|
26
22
|
kind: "tool-result",
|
|
@@ -31,9 +27,6 @@ function normalizeStreamChunk(chunk) {
|
|
|
31
27
|
}
|
|
32
28
|
return { kind: "content", content: chunk.content ?? "" };
|
|
33
29
|
}
|
|
34
|
-
function isUpstreamRuntimeEvent(event) {
|
|
35
|
-
return event.format === "langgraph-v2" && "normalized" in event && "streamPart" in event;
|
|
36
|
-
}
|
|
37
30
|
export async function* streamHarnessRun(options) {
|
|
38
31
|
const priorHistoryPromise = Promise.resolve(options.isNewThread ? [] : undefined).then((historyHint) => historyHint ?? options.loadPriorHistory(options.threadId, options.runId));
|
|
39
32
|
yield { type: "event", event: await options.runCreatedEventPromise };
|
|
@@ -62,15 +55,12 @@ export async function* streamHarnessRun(options) {
|
|
|
62
55
|
streamActivityObserved = true;
|
|
63
56
|
const normalizedChunk = normalizeStreamChunk(rawChunk);
|
|
64
57
|
if (normalizedChunk.kind === "upstream-event") {
|
|
65
|
-
const rawEvent = normalizedChunk.event;
|
|
66
58
|
yield {
|
|
67
59
|
type: "upstream-event",
|
|
68
60
|
threadId: options.threadId,
|
|
69
61
|
runId: options.runId,
|
|
70
62
|
agentId: options.selectedAgentId,
|
|
71
|
-
event:
|
|
72
|
-
? rawEvent
|
|
73
|
-
: normalizeUpstreamRuntimeEvent(rawEvent.raw ?? rawEvent),
|
|
63
|
+
event: normalizedChunk.event,
|
|
74
64
|
};
|
|
75
65
|
continue;
|
|
76
66
|
}
|
|
@@ -100,26 +90,6 @@ export async function* streamHarnessRun(options) {
|
|
|
100
90
|
return;
|
|
101
91
|
}
|
|
102
92
|
if (normalizedChunk.kind === "reasoning") {
|
|
103
|
-
await options.emit(options.threadId, options.runId, 3, "reasoning.delta", {
|
|
104
|
-
content: normalizedChunk.content,
|
|
105
|
-
});
|
|
106
|
-
yield {
|
|
107
|
-
type: "reasoning",
|
|
108
|
-
threadId: options.threadId,
|
|
109
|
-
runId: options.runId,
|
|
110
|
-
agentId: options.selectedAgentId,
|
|
111
|
-
content: normalizedChunk.content,
|
|
112
|
-
};
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
if (normalizedChunk.kind === "step") {
|
|
116
|
-
yield {
|
|
117
|
-
type: "step",
|
|
118
|
-
threadId: options.threadId,
|
|
119
|
-
runId: options.runId,
|
|
120
|
-
agentId: options.selectedAgentId,
|
|
121
|
-
content: normalizedChunk.content,
|
|
122
|
-
};
|
|
123
93
|
continue;
|
|
124
94
|
}
|
|
125
95
|
if (normalizedChunk.kind === "tool-result") {
|
package/dist/runtime/harness.js
CHANGED
|
@@ -38,7 +38,6 @@ export class AgentHarnessRuntime {
|
|
|
38
38
|
"run.queued",
|
|
39
39
|
"run.dequeued",
|
|
40
40
|
"output.delta",
|
|
41
|
-
"reasoning.delta",
|
|
42
41
|
]);
|
|
43
42
|
eventBus = new EventBus();
|
|
44
43
|
persistence;
|
|
@@ -464,15 +463,10 @@ export class AgentHarnessRuntime {
|
|
|
464
463
|
const binding = getWorkspaceBinding(this.workspace, selectedAgentId);
|
|
465
464
|
if (!binding) {
|
|
466
465
|
const result = await this.run(options);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
runId: result.runId,
|
|
472
|
-
agentId: result.agentId ?? selectedAgentId,
|
|
473
|
-
content: `${line}\n`,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
466
|
+
yield {
|
|
467
|
+
type: "result",
|
|
468
|
+
result,
|
|
469
|
+
};
|
|
476
470
|
return;
|
|
477
471
|
}
|
|
478
472
|
const { threadId, runId, isNewThread, runCreatedEventPromise, releaseRunSlotPromise, } = await prepareRunStart(this.createPrepareRunStartRuntime(), {
|
|
@@ -486,7 +480,7 @@ export class AgentHarnessRuntime {
|
|
|
486
480
|
state: "running",
|
|
487
481
|
}),
|
|
488
482
|
});
|
|
489
|
-
|
|
483
|
+
const stream = streamHarnessRun({
|
|
490
484
|
binding,
|
|
491
485
|
input: options.input,
|
|
492
486
|
invocation,
|
|
@@ -506,6 +500,11 @@ export class AgentHarnessRuntime {
|
|
|
506
500
|
clearRunRequest: (threadId, runId) => this.persistence.clearRunRequest(threadId, runId),
|
|
507
501
|
emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
|
|
508
502
|
});
|
|
503
|
+
for await (const item of stream) {
|
|
504
|
+
if (item.type === "event" || item.type === "upstream-event" || item.type === "result") {
|
|
505
|
+
yield item;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
509
508
|
}
|
|
510
509
|
async resume(options) {
|
|
511
510
|
return resumeRun({
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
export { extractReasoningText, extractToolFallbackContext, extractVisibleOutput, hasToolCalls, isLikelyToolArgsObject, readTextContent, sanitizeVisibleText, tryParseJson, wrapResolvedModel, } from "./output-parsing.js";
|
|
2
2
|
export { extractInterruptPayload, extractReasoningStreamOutput, extractTerminalStreamOutput, extractToolResult, normalizeTerminalOutputKey, readStreamDelta, type RuntimeStreamChunk, } from "./stream-event-parsing.js";
|
|
3
|
-
export type { CompatibleStreamPart, NormalizedUpstreamEvent, UpstreamRuntimeEvent, } from "./stream-event-types.js";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { CompatibleStreamPart, UpstreamRuntimeEvent } from "./stream-event-types.js";
|
|
2
1
|
export type RuntimeStreamChunk = {
|
|
3
2
|
kind: "upstream-event";
|
|
4
|
-
event:
|
|
3
|
+
event: unknown;
|
|
5
4
|
} | {
|
|
6
5
|
kind: "content";
|
|
7
6
|
content: string;
|
|
@@ -11,17 +10,12 @@ export type RuntimeStreamChunk = {
|
|
|
11
10
|
} | {
|
|
12
11
|
kind: "interrupt";
|
|
13
12
|
content: string;
|
|
14
|
-
} | {
|
|
15
|
-
kind: "step";
|
|
16
|
-
content: string;
|
|
17
13
|
} | {
|
|
18
14
|
kind: "tool-result";
|
|
19
15
|
toolName: string;
|
|
20
16
|
output: unknown;
|
|
21
17
|
isError?: boolean;
|
|
22
18
|
};
|
|
23
|
-
export declare function normalizeUpstreamRuntimeEvent(event: unknown): UpstreamRuntimeEvent;
|
|
24
|
-
export declare function extractCustomCompatibleStreamPart(event: unknown): CompatibleStreamPart | null;
|
|
25
19
|
export declare function extractTerminalStreamOutput(event: unknown): string;
|
|
26
20
|
export declare function extractReasoningStreamOutput(event: unknown): string;
|
|
27
21
|
export declare function extractVisibleStreamOutput(event: unknown): string;
|
|
@@ -1,226 +1,4 @@
|
|
|
1
1
|
import { extractReasoningText, extractVisibleOutput, hasToolCalls, readTextContent } from "./output-parsing.js";
|
|
2
|
-
function asObject(value) {
|
|
3
|
-
return typeof value === "object" && value !== null ? value : undefined;
|
|
4
|
-
}
|
|
5
|
-
function extractCustomStreamData(event) {
|
|
6
|
-
const typed = asObject(event);
|
|
7
|
-
if (!typed) {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
const eventName = typeof typed.event === "string" ? typed.event : "";
|
|
11
|
-
const ns = Array.isArray(typed.ns) ? typed.ns.filter((item) => typeof item === "string") : undefined;
|
|
12
|
-
const data = asObject(typed.data);
|
|
13
|
-
if (eventName === "on_custom_event") {
|
|
14
|
-
return {
|
|
15
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
16
|
-
data: typed.data,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
if (eventName === "on_chain_stream" && data && "custom" in data) {
|
|
20
|
-
return {
|
|
21
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
22
|
-
data: data.custom,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
export function normalizeUpstreamRuntimeEvent(event) {
|
|
28
|
-
const typed = asObject(event);
|
|
29
|
-
const tags = Array.isArray(typed?.tags) ? typed.tags.filter((item) => typeof item === "string") : undefined;
|
|
30
|
-
const ns = Array.isArray(typed?.ns) ? typed.ns.filter((item) => typeof item === "string") : undefined;
|
|
31
|
-
const name = typeof typed?.name === "string" ? typed.name : undefined;
|
|
32
|
-
const eventName = typeof typed?.event === "string" ? typed.event : undefined;
|
|
33
|
-
const runType = typeof typed?.run_type === "string" ? typed.run_type : undefined;
|
|
34
|
-
const data = asObject(typed?.data);
|
|
35
|
-
const metadata = asObject(typed?.metadata);
|
|
36
|
-
const normalized = normalizeUpstreamEventShape({
|
|
37
|
-
raw: event,
|
|
38
|
-
event: eventName,
|
|
39
|
-
name,
|
|
40
|
-
runType,
|
|
41
|
-
metadata,
|
|
42
|
-
tags,
|
|
43
|
-
data,
|
|
44
|
-
ns,
|
|
45
|
-
});
|
|
46
|
-
const streamPart = normalizeCompatibleStreamPart({
|
|
47
|
-
raw: event,
|
|
48
|
-
event: eventName,
|
|
49
|
-
name,
|
|
50
|
-
runType,
|
|
51
|
-
metadata,
|
|
52
|
-
tags,
|
|
53
|
-
data,
|
|
54
|
-
ns,
|
|
55
|
-
normalized,
|
|
56
|
-
});
|
|
57
|
-
return {
|
|
58
|
-
format: "langgraph-v2",
|
|
59
|
-
normalized,
|
|
60
|
-
raw: event,
|
|
61
|
-
event: eventName,
|
|
62
|
-
name,
|
|
63
|
-
runType,
|
|
64
|
-
data,
|
|
65
|
-
metadata,
|
|
66
|
-
...(tags && tags.length > 0 ? { tags } : {}),
|
|
67
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
68
|
-
streamPart,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function normalizeUpstreamEventShape(event) {
|
|
72
|
-
const nodeName = event.name;
|
|
73
|
-
const ns = event.ns;
|
|
74
|
-
const interruptPayload = extractInterruptPayload(event.raw);
|
|
75
|
-
if (interruptPayload) {
|
|
76
|
-
return {
|
|
77
|
-
kind: "interrupt",
|
|
78
|
-
interrupt: parseMaybeJson(interruptPayload),
|
|
79
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
80
|
-
...(nodeName ? { nodeName } : {}),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
const reasoning = extractReasoningStreamOutput(event.raw);
|
|
84
|
-
if (reasoning) {
|
|
85
|
-
return {
|
|
86
|
-
kind: "reasoning-delta",
|
|
87
|
-
text: reasoning,
|
|
88
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
89
|
-
...(nodeName ? { nodeName } : {}),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const visibleText = extractVisibleStreamOutput(event.raw);
|
|
93
|
-
if (visibleText) {
|
|
94
|
-
return {
|
|
95
|
-
kind: "text-delta",
|
|
96
|
-
source: "model",
|
|
97
|
-
text: visibleText,
|
|
98
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
99
|
-
...(nodeName ? { nodeName } : {}),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const stateText = extractStateStreamOutput(event.raw);
|
|
103
|
-
if (stateText) {
|
|
104
|
-
return {
|
|
105
|
-
kind: "text-delta",
|
|
106
|
-
source: "state",
|
|
107
|
-
text: stateText,
|
|
108
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
109
|
-
...(nodeName ? { nodeName } : {}),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (event.event === "on_tool_start" || (event.event === "on_chain_start" && event.runType === "tool")) {
|
|
113
|
-
return {
|
|
114
|
-
kind: "tool-start",
|
|
115
|
-
toolName: nodeName ?? "tool",
|
|
116
|
-
...(event.data && "input" in event.data ? { input: event.data.input } : {}),
|
|
117
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
118
|
-
...(nodeName ? { nodeName } : {}),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
const toolResult = extractToolResult(event.raw);
|
|
122
|
-
if (toolResult) {
|
|
123
|
-
return {
|
|
124
|
-
kind: "tool-end",
|
|
125
|
-
toolName: toolResult.toolName,
|
|
126
|
-
...(toolResult.output !== undefined ? { output: toolResult.output } : {}),
|
|
127
|
-
...(toolResult.isError !== undefined ? { isError: toolResult.isError } : {}),
|
|
128
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
129
|
-
...(nodeName ? { nodeName } : {}),
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
kind: "run-event",
|
|
134
|
-
eventName: event.event ?? "unknown",
|
|
135
|
-
...(event.data ? { data: event.data } : {}),
|
|
136
|
-
...(ns && ns.length > 0 ? { ns } : {}),
|
|
137
|
-
...(nodeName ? { nodeName } : {}),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function normalizeCompatibleStreamPart(event) {
|
|
141
|
-
const custom = extractCustomCompatibleStreamPart(event.raw);
|
|
142
|
-
if (custom) {
|
|
143
|
-
return custom;
|
|
144
|
-
}
|
|
145
|
-
const ns = event.ns ?? [];
|
|
146
|
-
const messageChunk = extractMessageCompatibleStreamPart(event);
|
|
147
|
-
if (messageChunk) {
|
|
148
|
-
return {
|
|
149
|
-
type: "messages",
|
|
150
|
-
ns,
|
|
151
|
-
data: [messageChunk, buildStreamPartMetadata(event)],
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
return {
|
|
155
|
-
type: "updates",
|
|
156
|
-
ns,
|
|
157
|
-
data: buildUpdateCompatiblePayload(event),
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
function buildStreamPartMetadata(event) {
|
|
161
|
-
return {
|
|
162
|
-
...(event.metadata ?? {}),
|
|
163
|
-
...(event.event ? { event: event.event } : {}),
|
|
164
|
-
...(event.name ? { name: event.name } : {}),
|
|
165
|
-
...(event.runType ? { run_type: event.runType } : {}),
|
|
166
|
-
...(event.tags && event.tags.length > 0 ? { tags: event.tags } : {}),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function extractMessageCompatibleStreamPart(event) {
|
|
170
|
-
const typed = asObject(event.raw);
|
|
171
|
-
const data = asObject(typed?.data);
|
|
172
|
-
if (event.event === "on_chat_model_stream" && data && "chunk" in data) {
|
|
173
|
-
return data.chunk;
|
|
174
|
-
}
|
|
175
|
-
if (event.normalized.kind === "tool-end") {
|
|
176
|
-
return {
|
|
177
|
-
type: "tool",
|
|
178
|
-
name: event.normalized.toolName,
|
|
179
|
-
content: event.normalized.output,
|
|
180
|
-
...(event.normalized.isError !== undefined ? { isError: event.normalized.isError } : {}),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
function buildUpdateCompatiblePayload(event) {
|
|
186
|
-
const key = event.name ?? event.event ?? "event";
|
|
187
|
-
return {
|
|
188
|
-
[key]: extractUpdateNodePayload(event),
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function extractUpdateNodePayload(event) {
|
|
192
|
-
const data = event.data;
|
|
193
|
-
if (event.event === "on_chain_stream" && data && "chunk" in data) {
|
|
194
|
-
return data.chunk;
|
|
195
|
-
}
|
|
196
|
-
if ((event.event === "on_tool_start" || event.event === "on_chain_start") && data && "input" in data) {
|
|
197
|
-
return { input: data.input };
|
|
198
|
-
}
|
|
199
|
-
if ((event.event === "on_tool_end" || event.event === "on_chain_end" || event.event === "on_chat_model_end") && data && "output" in data) {
|
|
200
|
-
return data.output;
|
|
201
|
-
}
|
|
202
|
-
if ((event.event === "on_tool_error" || event.event === "on_chain_error") && data) {
|
|
203
|
-
return {
|
|
204
|
-
...(data.output !== undefined ? { output: data.output } : {}),
|
|
205
|
-
...(data.error !== undefined ? { error: data.error } : {}),
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
if (event.normalized.kind === "interrupt") {
|
|
209
|
-
return { __interrupt__: event.normalized.interrupt };
|
|
210
|
-
}
|
|
211
|
-
return data ?? {};
|
|
212
|
-
}
|
|
213
|
-
export function extractCustomCompatibleStreamPart(event) {
|
|
214
|
-
const custom = extractCustomStreamData(event);
|
|
215
|
-
if (!custom) {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
return {
|
|
219
|
-
type: "custom",
|
|
220
|
-
ns: custom.ns ?? [],
|
|
221
|
-
data: custom.data,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
2
|
function parseMaybeJson(value) {
|
|
225
3
|
const trimmed = value.trim();
|
|
226
4
|
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type UpstreamTimelineProjection = {
|
|
2
|
+
type: "thinking";
|
|
3
|
+
text: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: "step";
|
|
6
|
+
step: string;
|
|
7
|
+
category: "llm" | "tool" | "skill" | "memory" | "chain" | "approval";
|
|
8
|
+
status: "started" | "completed" | "failed";
|
|
9
|
+
key: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: "tool-result";
|
|
12
|
+
toolName: string;
|
|
13
|
+
output: unknown;
|
|
14
|
+
isError?: boolean;
|
|
15
|
+
key: string;
|
|
16
|
+
};
|
|
17
|
+
export type UpstreamTimelineReducer = {
|
|
18
|
+
consume: (event: unknown) => UpstreamTimelineProjection[];
|
|
19
|
+
};
|
|
20
|
+
export declare function createUpstreamTimelineReducer(): UpstreamTimelineReducer;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { extractInterruptPayload, extractReasoningStreamOutput, extractToolResult } from "./runtime/parsing/index.js";
|
|
2
|
+
function asObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
4
|
+
}
|
|
5
|
+
function readStringArray(value) {
|
|
6
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
7
|
+
}
|
|
8
|
+
function normalizeSegment(value) {
|
|
9
|
+
return value
|
|
10
|
+
.replace(/[_-]+/g, " ")
|
|
11
|
+
.replace(/\s+/g, " ")
|
|
12
|
+
.trim();
|
|
13
|
+
}
|
|
14
|
+
function titleCase(value) {
|
|
15
|
+
return normalizeSegment(value)
|
|
16
|
+
.split(" ")
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
19
|
+
.join(" ");
|
|
20
|
+
}
|
|
21
|
+
function readEventContext(event) {
|
|
22
|
+
const typed = asObject(event);
|
|
23
|
+
return {
|
|
24
|
+
eventName: typeof typed?.event === 'string' ? typed.event : "",
|
|
25
|
+
name: typeof typed?.name === 'string' ? typed.name : "",
|
|
26
|
+
runType: typeof typed?.run_type === 'string' ? typed.run_type : "",
|
|
27
|
+
tags: readStringArray(typed?.tags),
|
|
28
|
+
ns: readStringArray(typed?.ns),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function containsSemanticHint(values, hint) {
|
|
32
|
+
return values.some((value) => hint.test(value));
|
|
33
|
+
}
|
|
34
|
+
function classifyStepCategory(context) {
|
|
35
|
+
const hints = [context.name, context.runType, ...context.tags, ...context.ns].map((value) => value.toLowerCase());
|
|
36
|
+
if (containsSemanticHint(hints, /\b(skill|skills)\b/)) {
|
|
37
|
+
return "skill";
|
|
38
|
+
}
|
|
39
|
+
if (containsSemanticHint(hints, /\b(memory|recall|store|checkpoint)\b/)) {
|
|
40
|
+
return "memory";
|
|
41
|
+
}
|
|
42
|
+
if (context.eventName.startsWith("on_tool_") || context.runType === "tool") {
|
|
43
|
+
return "tool";
|
|
44
|
+
}
|
|
45
|
+
return "chain";
|
|
46
|
+
}
|
|
47
|
+
function buildStepLabel(category, status, name) {
|
|
48
|
+
const displayName = titleCase(name || category);
|
|
49
|
+
if (category === "llm") {
|
|
50
|
+
return status === "started" ? `Calling LLM ${displayName}` : `Completed LLM ${displayName}`;
|
|
51
|
+
}
|
|
52
|
+
if (category === "tool") {
|
|
53
|
+
if (status === "failed") {
|
|
54
|
+
return `Tool ${displayName} failed`;
|
|
55
|
+
}
|
|
56
|
+
return status === "started" ? `Calling tool ${displayName}` : `Completed tool ${displayName}`;
|
|
57
|
+
}
|
|
58
|
+
if (category === "skill") {
|
|
59
|
+
return status === "started" ? `Calling skill ${displayName}` : `Completed skill ${displayName}`;
|
|
60
|
+
}
|
|
61
|
+
if (category === "memory") {
|
|
62
|
+
return status === "started" ? `Accessing memory ${displayName}` : `Completed memory ${displayName}`;
|
|
63
|
+
}
|
|
64
|
+
return status === "started" ? `Running ${displayName}` : `Completed ${displayName}`;
|
|
65
|
+
}
|
|
66
|
+
function createProjectionKey(parts) {
|
|
67
|
+
return JSON.stringify(parts);
|
|
68
|
+
}
|
|
69
|
+
export function createUpstreamTimelineReducer() {
|
|
70
|
+
const emittedStepKeys = new Set();
|
|
71
|
+
const emittedToolResultKeys = new Set();
|
|
72
|
+
return {
|
|
73
|
+
consume(event) {
|
|
74
|
+
const projections = [];
|
|
75
|
+
const context = readEventContext(event);
|
|
76
|
+
const reasoning = extractReasoningStreamOutput(event);
|
|
77
|
+
if (reasoning) {
|
|
78
|
+
projections.push({
|
|
79
|
+
type: "thinking",
|
|
80
|
+
text: reasoning,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const interruptPayload = extractInterruptPayload(event);
|
|
84
|
+
if (interruptPayload) {
|
|
85
|
+
const key = createProjectionKey(["approval", context.eventName || "interrupt", interruptPayload]);
|
|
86
|
+
if (!emittedStepKeys.has(key)) {
|
|
87
|
+
emittedStepKeys.add(key);
|
|
88
|
+
projections.push({
|
|
89
|
+
type: "step",
|
|
90
|
+
step: "Waiting for approval",
|
|
91
|
+
category: "approval",
|
|
92
|
+
status: "started",
|
|
93
|
+
key,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (context.eventName === "on_chat_model_start") {
|
|
98
|
+
const stepName = context.name || "model";
|
|
99
|
+
const key = createProjectionKey(["llm", "started", stepName]);
|
|
100
|
+
if (!emittedStepKeys.has(key)) {
|
|
101
|
+
emittedStepKeys.add(key);
|
|
102
|
+
projections.push({
|
|
103
|
+
type: "step",
|
|
104
|
+
step: buildStepLabel("llm", "started", stepName),
|
|
105
|
+
category: "llm",
|
|
106
|
+
status: "started",
|
|
107
|
+
key,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (context.eventName === "on_tool_start"
|
|
112
|
+
|| (context.eventName === "on_chain_start" && context.runType === "tool")
|
|
113
|
+
|| context.eventName === "on_chain_start") {
|
|
114
|
+
const category = classifyStepCategory(context);
|
|
115
|
+
if (category !== "llm") {
|
|
116
|
+
const stepName = context.name || context.runType || "chain";
|
|
117
|
+
const key = createProjectionKey([category, "started", stepName]);
|
|
118
|
+
if (!emittedStepKeys.has(key)) {
|
|
119
|
+
emittedStepKeys.add(key);
|
|
120
|
+
projections.push({
|
|
121
|
+
type: "step",
|
|
122
|
+
step: buildStepLabel(category, "started", stepName),
|
|
123
|
+
category,
|
|
124
|
+
status: "started",
|
|
125
|
+
key,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const toolResult = extractToolResult(event);
|
|
131
|
+
if (toolResult) {
|
|
132
|
+
const resultKey = createProjectionKey(["tool-result", toolResult.toolName, toolResult.output, toolResult.isError === true]);
|
|
133
|
+
if (!emittedToolResultKeys.has(resultKey)) {
|
|
134
|
+
emittedToolResultKeys.add(resultKey);
|
|
135
|
+
projections.push({
|
|
136
|
+
type: "tool-result",
|
|
137
|
+
toolName: toolResult.toolName,
|
|
138
|
+
output: toolResult.output,
|
|
139
|
+
...(toolResult.isError !== undefined ? { isError: toolResult.isError } : {}),
|
|
140
|
+
key: resultKey,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const stepKey = createProjectionKey(["tool", toolResult.isError ? "failed" : "completed", toolResult.toolName]);
|
|
144
|
+
if (!emittedStepKeys.has(stepKey)) {
|
|
145
|
+
emittedStepKeys.add(stepKey);
|
|
146
|
+
projections.push({
|
|
147
|
+
type: "step",
|
|
148
|
+
step: buildStepLabel("tool", toolResult.isError ? "failed" : "completed", toolResult.toolName),
|
|
149
|
+
category: "tool",
|
|
150
|
+
status: toolResult.isError ? "failed" : "completed",
|
|
151
|
+
key: stepKey,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (context.eventName === "on_chain_end" && context.runType !== "tool" && context.name) {
|
|
156
|
+
const category = classifyStepCategory(context);
|
|
157
|
+
const key = createProjectionKey([category, "completed", context.name]);
|
|
158
|
+
if (!emittedStepKeys.has(key)) {
|
|
159
|
+
emittedStepKeys.add(key);
|
|
160
|
+
projections.push({
|
|
161
|
+
type: "step",
|
|
162
|
+
step: buildStepLabel(category, "completed", context.name),
|
|
163
|
+
category,
|
|
164
|
+
status: "completed",
|
|
165
|
+
key,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return projections;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
package/package.json
CHANGED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
export type NormalizedUpstreamEvent = {
|
|
2
|
-
kind: "text-delta";
|
|
3
|
-
source: "model" | "state";
|
|
4
|
-
text: string;
|
|
5
|
-
ns?: string[];
|
|
6
|
-
nodeName?: string;
|
|
7
|
-
} | {
|
|
8
|
-
kind: "reasoning-delta";
|
|
9
|
-
text: string;
|
|
10
|
-
ns?: string[];
|
|
11
|
-
nodeName?: string;
|
|
12
|
-
} | {
|
|
13
|
-
kind: "tool-start";
|
|
14
|
-
toolName: string;
|
|
15
|
-
input?: unknown;
|
|
16
|
-
ns?: string[];
|
|
17
|
-
nodeName?: string;
|
|
18
|
-
} | {
|
|
19
|
-
kind: "tool-end";
|
|
20
|
-
toolName: string;
|
|
21
|
-
output?: unknown;
|
|
22
|
-
isError?: boolean;
|
|
23
|
-
ns?: string[];
|
|
24
|
-
nodeName?: string;
|
|
25
|
-
} | {
|
|
26
|
-
kind: "interrupt";
|
|
27
|
-
interrupt: unknown;
|
|
28
|
-
ns?: string[];
|
|
29
|
-
nodeName?: string;
|
|
30
|
-
} | {
|
|
31
|
-
kind: "run-event";
|
|
32
|
-
eventName: string;
|
|
33
|
-
data?: Record<string, unknown>;
|
|
34
|
-
ns?: string[];
|
|
35
|
-
nodeName?: string;
|
|
36
|
-
};
|
|
37
|
-
export type CompatibleStreamPart = {
|
|
38
|
-
type: "messages";
|
|
39
|
-
ns: string[];
|
|
40
|
-
data: [unknown, Record<string, unknown>];
|
|
41
|
-
} | {
|
|
42
|
-
type: "updates";
|
|
43
|
-
ns: string[];
|
|
44
|
-
data: Record<string, unknown>;
|
|
45
|
-
} | {
|
|
46
|
-
type: "custom";
|
|
47
|
-
ns: string[];
|
|
48
|
-
data: unknown;
|
|
49
|
-
};
|
|
50
|
-
export type UpstreamRuntimeEvent = {
|
|
51
|
-
format: "langgraph-v2";
|
|
52
|
-
normalized: NormalizedUpstreamEvent;
|
|
53
|
-
streamPart: CompatibleStreamPart;
|
|
54
|
-
raw: unknown;
|
|
55
|
-
event?: string;
|
|
56
|
-
name?: string;
|
|
57
|
-
runType?: string;
|
|
58
|
-
data?: Record<string, unknown>;
|
|
59
|
-
metadata?: Record<string, unknown>;
|
|
60
|
-
tags?: string[];
|
|
61
|
-
ns?: string[];
|
|
62
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|