@langchain/langgraph 1.2.9 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/graph/graph.cjs +6 -2
- package/dist/graph/graph.cjs.map +1 -1
- package/dist/graph/graph.d.cts +58 -7
- package/dist/graph/graph.d.cts.map +1 -1
- package/dist/graph/graph.d.ts +58 -7
- package/dist/graph/graph.d.ts.map +1 -1
- package/dist/graph/graph.js +6 -2
- package/dist/graph/graph.js.map +1 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/state.cjs +3 -2
- package/dist/graph/state.cjs.map +1 -1
- package/dist/graph/state.d.cts +13 -5
- package/dist/graph/state.d.cts.map +1 -1
- package/dist/graph/state.d.ts +13 -5
- package/dist/graph/state.d.ts.map +1 -1
- package/dist/graph/state.js +3 -2
- package/dist/graph/state.js.map +1 -1
- package/dist/index.cjs +34 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/prebuilt/agent_executor.d.cts +1 -1
- package/dist/prebuilt/agent_executor.d.ts +1 -1
- package/dist/pregel/index.cjs +69 -25
- package/dist/pregel/index.cjs.map +1 -1
- package/dist/pregel/index.d.cts +25 -3
- package/dist/pregel/index.d.cts.map +1 -1
- package/dist/pregel/index.d.ts +25 -3
- package/dist/pregel/index.d.ts.map +1 -1
- package/dist/pregel/index.js +69 -25
- package/dist/pregel/index.js.map +1 -1
- package/dist/pregel/loop.cjs +51 -10
- package/dist/pregel/loop.cjs.map +1 -1
- package/dist/pregel/loop.js +51 -10
- package/dist/pregel/loop.js.map +1 -1
- package/dist/pregel/messages-v2.cjs +231 -0
- package/dist/pregel/messages-v2.cjs.map +1 -0
- package/dist/pregel/messages-v2.js +231 -0
- package/dist/pregel/messages-v2.js.map +1 -0
- package/dist/pregel/stream.cjs.map +1 -1
- package/dist/pregel/stream.d.cts +30 -0
- package/dist/pregel/stream.d.cts.map +1 -0
- package/dist/pregel/stream.d.ts +30 -1
- package/dist/pregel/stream.d.ts.map +1 -0
- package/dist/pregel/stream.js.map +1 -1
- package/dist/pregel/types.cjs.map +1 -1
- package/dist/pregel/types.d.cts +8 -1
- package/dist/pregel/types.d.cts.map +1 -1
- package/dist/pregel/types.d.ts +8 -1
- package/dist/pregel/types.d.ts.map +1 -1
- package/dist/pregel/types.js.map +1 -1
- package/dist/stream/convert.cjs +207 -0
- package/dist/stream/convert.cjs.map +1 -0
- package/dist/stream/convert.d.cts +69 -0
- package/dist/stream/convert.d.cts.map +1 -0
- package/dist/stream/convert.d.ts +69 -0
- package/dist/stream/convert.d.ts.map +1 -0
- package/dist/stream/convert.js +206 -0
- package/dist/stream/convert.js.map +1 -0
- package/dist/stream/index.cjs +11 -0
- package/dist/stream/index.d.cts +12 -0
- package/dist/stream/index.d.ts +12 -0
- package/dist/stream/index.js +12 -0
- package/dist/stream/mux.cjs +350 -0
- package/dist/stream/mux.cjs.map +1 -0
- package/dist/stream/mux.d.cts +160 -0
- package/dist/stream/mux.d.cts.map +1 -0
- package/dist/stream/mux.d.ts +160 -0
- package/dist/stream/mux.d.ts.map +1 -0
- package/dist/stream/mux.js +345 -0
- package/dist/stream/mux.js.map +1 -0
- package/dist/stream/run-stream.cjs +439 -0
- package/dist/stream/run-stream.cjs.map +1 -0
- package/dist/stream/run-stream.d.cts +286 -0
- package/dist/stream/run-stream.d.cts.map +1 -0
- package/dist/stream/run-stream.d.ts +285 -0
- package/dist/stream/run-stream.d.ts.map +1 -0
- package/dist/stream/run-stream.js +434 -0
- package/dist/stream/run-stream.js.map +1 -0
- package/dist/stream/stream-channel.cjs +208 -0
- package/dist/stream/stream-channel.cjs.map +1 -0
- package/dist/stream/stream-channel.d.cts +129 -0
- package/dist/stream/stream-channel.d.cts.map +1 -0
- package/dist/stream/stream-channel.d.ts +129 -0
- package/dist/stream/stream-channel.d.ts.map +1 -0
- package/dist/stream/stream-channel.js +207 -0
- package/dist/stream/stream-channel.js.map +1 -0
- package/dist/stream/transformers/index.cjs +4 -0
- package/dist/stream/transformers/index.d.ts +5 -0
- package/dist/stream/transformers/index.js +5 -0
- package/dist/stream/transformers/lifecycle.cjs +326 -0
- package/dist/stream/transformers/lifecycle.cjs.map +1 -0
- package/dist/stream/transformers/lifecycle.d.cts +53 -0
- package/dist/stream/transformers/lifecycle.d.cts.map +1 -0
- package/dist/stream/transformers/lifecycle.d.ts +53 -0
- package/dist/stream/transformers/lifecycle.d.ts.map +1 -0
- package/dist/stream/transformers/lifecycle.js +325 -0
- package/dist/stream/transformers/lifecycle.js.map +1 -0
- package/dist/stream/transformers/messages.cjs +94 -0
- package/dist/stream/transformers/messages.cjs.map +1 -0
- package/dist/stream/transformers/messages.d.cts +23 -0
- package/dist/stream/transformers/messages.d.cts.map +1 -0
- package/dist/stream/transformers/messages.d.ts +23 -0
- package/dist/stream/transformers/messages.d.ts.map +1 -0
- package/dist/stream/transformers/messages.js +94 -0
- package/dist/stream/transformers/messages.js.map +1 -0
- package/dist/stream/transformers/subgraphs.cjs +125 -0
- package/dist/stream/transformers/subgraphs.cjs.map +1 -0
- package/dist/stream/transformers/subgraphs.d.cts +95 -0
- package/dist/stream/transformers/subgraphs.d.cts.map +1 -0
- package/dist/stream/transformers/subgraphs.d.ts +95 -0
- package/dist/stream/transformers/subgraphs.d.ts.map +1 -0
- package/dist/stream/transformers/subgraphs.js +124 -0
- package/dist/stream/transformers/subgraphs.js.map +1 -0
- package/dist/stream/transformers/types.d.cts +89 -0
- package/dist/stream/transformers/types.d.cts.map +1 -0
- package/dist/stream/transformers/types.d.ts +89 -0
- package/dist/stream/transformers/types.d.ts.map +1 -0
- package/dist/stream/transformers/values.cjs +39 -0
- package/dist/stream/transformers/values.cjs.map +1 -0
- package/dist/stream/transformers/values.d.cts +21 -0
- package/dist/stream/transformers/values.d.cts.map +1 -0
- package/dist/stream/transformers/values.d.ts +21 -0
- package/dist/stream/transformers/values.d.ts.map +1 -0
- package/dist/stream/transformers/values.js +39 -0
- package/dist/stream/transformers/values.js.map +1 -0
- package/dist/stream/types.cjs +11 -0
- package/dist/stream/types.cjs.map +1 -0
- package/dist/stream/types.d.cts +255 -0
- package/dist/stream/types.d.cts.map +1 -0
- package/dist/stream/types.d.ts +255 -0
- package/dist/stream/types.d.ts.map +1 -0
- package/dist/stream/types.js +11 -0
- package/dist/stream/types.js.map +1 -0
- package/dist/web.cjs +39 -9
- package/dist/web.d.cts +12 -2
- package/dist/web.d.ts +12 -2
- package/dist/web.js +10 -1
- package/package.json +9 -8
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
//#region src/stream/stream-channel.ts
|
|
2
|
+
/**
|
|
3
|
+
* StreamChannel — projection channel for local or remote streaming.
|
|
4
|
+
*
|
|
5
|
+
* A `StreamChannel<T>` is an append-only async stream with independent
|
|
6
|
+
* cursors. Local channels stay in-process only. Remote channels declare a
|
|
7
|
+
* protocol channel name; when registered with a {@link StreamMux} (via a
|
|
8
|
+
* transformer's `init()` return value), every {@link push} is automatically
|
|
9
|
+
* forwarded as a {@link ProtocolEvent} on the named channel — making the data
|
|
10
|
+
* available both in-process (via `run.extensions`) and to remote clients (via
|
|
11
|
+
* `session.subscribe("custom:<channelName>")`).
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle (`close` / `fail`) is managed by the mux automatically;
|
|
14
|
+
* transformers do not need to call them.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Branded symbol placed on every {@link StreamChannel} instance.
|
|
18
|
+
*
|
|
19
|
+
* Uses `Symbol.for` so the same symbol is shared across multiple
|
|
20
|
+
* copies of this package that may coexist in a dependency graph
|
|
21
|
+
* (e.g. when a user app imports `@langchain/langgraph` directly and a
|
|
22
|
+
* wrapping library like `langchain` bundles its own copy). Using a
|
|
23
|
+
* symbol brand instead of `instanceof` lets channels created against
|
|
24
|
+
* one copy of the class be recognised by a mux from another.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
const STREAM_CHANNEL_BRAND = Symbol.for("langgraph.stream_channel");
|
|
28
|
+
/**
|
|
29
|
+
* A projection channel for {@link StreamTransformer}s.
|
|
30
|
+
*
|
|
31
|
+
* Implements `AsyncIterable<T>` so it can be iterated directly by
|
|
32
|
+
* in-process consumers via `run.extensions.<key>`. Channels created with
|
|
33
|
+
* {@link StreamChannel.remote} or `new StreamChannel(name)` are also
|
|
34
|
+
* auto-forwarded to remote clients.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam T - The type of items pushed into the channel.
|
|
37
|
+
*/
|
|
38
|
+
var StreamChannel = class StreamChannel {
|
|
39
|
+
/** @internal Brand used by {@link StreamChannel.isInstance}. */
|
|
40
|
+
[STREAM_CHANNEL_BRAND] = true;
|
|
41
|
+
/** Protocol channel name used for auto-forwarded events, if remote. */
|
|
42
|
+
channelName;
|
|
43
|
+
#items = [];
|
|
44
|
+
#waiters = [];
|
|
45
|
+
#done = false;
|
|
46
|
+
#error;
|
|
47
|
+
#onPush;
|
|
48
|
+
constructor(name) {
|
|
49
|
+
this.channelName = name;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create an in-process-only channel. Values remain available through
|
|
53
|
+
* `run.extensions.<key>` but are not forwarded to remote clients.
|
|
54
|
+
*/
|
|
55
|
+
static local() {
|
|
56
|
+
return new StreamChannel();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a channel whose pushes are forwarded to remote clients under
|
|
60
|
+
* the given protocol channel name.
|
|
61
|
+
*/
|
|
62
|
+
static remote(name) {
|
|
63
|
+
return new StreamChannel(name);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Brand-based type guard that recognises any {@link StreamChannel}
|
|
67
|
+
* instance, even ones originating from a different copy of this
|
|
68
|
+
* package. Prefer this over `instanceof StreamChannel` when code
|
|
69
|
+
* may observe channels that were constructed elsewhere.
|
|
70
|
+
*/
|
|
71
|
+
static isInstance(value) {
|
|
72
|
+
return typeof value === "object" && value !== null && STREAM_CHANNEL_BRAND in value && value[STREAM_CHANNEL_BRAND] === true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Append an item to the channel. If this is a remote channel wired to a
|
|
76
|
+
* mux, the item is also injected into the main protocol event stream under
|
|
77
|
+
* {@link channelName}.
|
|
78
|
+
*/
|
|
79
|
+
push(item) {
|
|
80
|
+
this.#items.push(item);
|
|
81
|
+
this.#wake();
|
|
82
|
+
this.#onPush?.(item);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns an async iterator starting at position {@link startAt}. Each call
|
|
86
|
+
* returns an independent cursor so multiple consumers can iterate the same
|
|
87
|
+
* channel concurrently.
|
|
88
|
+
*/
|
|
89
|
+
iterate(startAt = 0) {
|
|
90
|
+
let cursor = startAt;
|
|
91
|
+
return { next: async () => {
|
|
92
|
+
while (true) {
|
|
93
|
+
if (cursor < this.#items.length) return {
|
|
94
|
+
value: this.#items[cursor++],
|
|
95
|
+
done: false
|
|
96
|
+
};
|
|
97
|
+
if (this.#done) {
|
|
98
|
+
if (this.#error) throw this.#error;
|
|
99
|
+
return {
|
|
100
|
+
value: void 0,
|
|
101
|
+
done: true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
await new Promise((resolve) => this.#waiters.push(resolve));
|
|
105
|
+
}
|
|
106
|
+
} };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Creates an {@link AsyncIterable} backed by this channel, starting from
|
|
110
|
+
* {@link startAt}.
|
|
111
|
+
*/
|
|
112
|
+
toAsyncIterable(startAt = 0) {
|
|
113
|
+
return { [Symbol.asyncIterator]: () => this.iterate(startAt) };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a web {@link ReadableStream} that emits channel items as
|
|
117
|
+
* Server-Sent Events. Useful for returning a channel directly from
|
|
118
|
+
* `new Response(channel.toEventStream())`.
|
|
119
|
+
*/
|
|
120
|
+
toEventStream(options = {}) {
|
|
121
|
+
const encoder = new TextEncoder();
|
|
122
|
+
const iterator = this.iterate(options.startAt);
|
|
123
|
+
const event = options.event ?? this.channelName;
|
|
124
|
+
const serialize = options.serialize ?? ((item) => JSON.stringify(item) ?? "null");
|
|
125
|
+
return new ReadableStream({
|
|
126
|
+
async pull(controller) {
|
|
127
|
+
try {
|
|
128
|
+
const next = await iterator.next();
|
|
129
|
+
if (next.done) {
|
|
130
|
+
controller.close();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const lines = [];
|
|
134
|
+
if (event != null) lines.push(`event: ${event}`);
|
|
135
|
+
for (const line of serialize(next.value).split(/\r\n|\r|\n/)) lines.push(`data: ${line}`);
|
|
136
|
+
controller.enqueue(encoder.encode(`${lines.join("\n")}\n\n`));
|
|
137
|
+
} catch (error) {
|
|
138
|
+
controller.error(error);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
async cancel() {
|
|
142
|
+
await iterator.return?.();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Returns the item at the given zero-based index.
|
|
148
|
+
*
|
|
149
|
+
* @throws {RangeError} If the index is out of bounds.
|
|
150
|
+
*/
|
|
151
|
+
get(index) {
|
|
152
|
+
if (index < 0 || index >= this.#items.length) throw new RangeError(`StreamChannel index ${index} out of bounds (size=${this.#items.length})`);
|
|
153
|
+
return this.#items[index];
|
|
154
|
+
}
|
|
155
|
+
/** The number of items currently buffered in the channel. */
|
|
156
|
+
get size() {
|
|
157
|
+
return this.#items.length;
|
|
158
|
+
}
|
|
159
|
+
/** Whether the channel has been closed or failed. */
|
|
160
|
+
get done() {
|
|
161
|
+
return this.#done;
|
|
162
|
+
}
|
|
163
|
+
/** Mark the channel as complete after all buffered items are consumed. */
|
|
164
|
+
close() {
|
|
165
|
+
this.#done = true;
|
|
166
|
+
this.#wake();
|
|
167
|
+
}
|
|
168
|
+
/** Mark the channel as failed after all buffered items are consumed. */
|
|
169
|
+
fail(err) {
|
|
170
|
+
this.#error = err;
|
|
171
|
+
this.#done = true;
|
|
172
|
+
this.#wake();
|
|
173
|
+
}
|
|
174
|
+
/** @internal Called by the mux to wire auto-forwarding. */
|
|
175
|
+
_wire(fn) {
|
|
176
|
+
this.#onPush = fn;
|
|
177
|
+
}
|
|
178
|
+
/** @internal Called by the mux on normal completion. */
|
|
179
|
+
_close() {
|
|
180
|
+
this.close();
|
|
181
|
+
}
|
|
182
|
+
/** @internal Called by the mux on failure. */
|
|
183
|
+
_fail(err) {
|
|
184
|
+
this.fail(err);
|
|
185
|
+
}
|
|
186
|
+
[Symbol.asyncIterator]() {
|
|
187
|
+
return this.iterate();
|
|
188
|
+
}
|
|
189
|
+
#wake() {
|
|
190
|
+
const waiters = this.#waiters.splice(0);
|
|
191
|
+
for (const w of waiters) w();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Type guard that tests whether a value is a {@link StreamChannel}.
|
|
196
|
+
*
|
|
197
|
+
* Uses a symbol brand rather than `instanceof` so channels built
|
|
198
|
+
* against a different copy of this package (e.g. one bundled by the
|
|
199
|
+
* `langchain` umbrella package) are still recognised.
|
|
200
|
+
*/
|
|
201
|
+
function isStreamChannel(value) {
|
|
202
|
+
return StreamChannel.isInstance(value);
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { StreamChannel, isStreamChannel };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=stream-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-channel.js","names":["#items","#wake","#onPush","#done","#error","#waiters"],"sources":["../../src/stream/stream-channel.ts"],"sourcesContent":["/**\n * StreamChannel — projection channel for local or remote streaming.\n *\n * A `StreamChannel<T>` is an append-only async stream with independent\n * cursors. Local channels stay in-process only. Remote channels declare a\n * protocol channel name; when registered with a {@link StreamMux} (via a\n * transformer's `init()` return value), every {@link push} is automatically\n * forwarded as a {@link ProtocolEvent} on the named channel — making the data\n * available both in-process (via `run.extensions`) and to remote clients (via\n * `session.subscribe(\"custom:<channelName>\")`).\n *\n * Lifecycle (`close` / `fail`) is managed by the mux automatically;\n * transformers do not need to call them.\n */\n\n/**\n * Branded symbol placed on every {@link StreamChannel} instance.\n *\n * Uses `Symbol.for` so the same symbol is shared across multiple\n * copies of this package that may coexist in a dependency graph\n * (e.g. when a user app imports `@langchain/langgraph` directly and a\n * wrapping library like `langchain` bundles its own copy). Using a\n * symbol brand instead of `instanceof` lets channels created against\n * one copy of the class be recognised by a mux from another.\n * @internal\n */\nexport const STREAM_CHANNEL_BRAND: unique symbol = Symbol.for(\n \"langgraph.stream_channel\"\n) as typeof STREAM_CHANNEL_BRAND;\n\nexport interface StreamChannelEventStreamOptions<T> {\n /**\n * SSE event name. Defaults to the channel's remote protocol name, if any.\n * Set this for local channels or when exposing the same channel under a\n * route-specific event name.\n */\n event?: string;\n /**\n * Cursor position to start streaming from. Useful for reconnects or\n * secondary subscribers that already consumed the first N buffered items and\n * only need replay from a known offset.\n */\n startAt?: number;\n /**\n * Serialize each item into the SSE `data:` field. Defaults to JSON. Use this\n * when a channel item needs a wire format other than its raw JSON shape, or\n * when the consumer expects line-oriented text payloads.\n */\n serialize?: (item: T) => string;\n}\n\n/**\n * A projection channel for {@link StreamTransformer}s.\n *\n * Implements `AsyncIterable<T>` so it can be iterated directly by\n * in-process consumers via `run.extensions.<key>`. Channels created with\n * {@link StreamChannel.remote} or `new StreamChannel(name)` are also\n * auto-forwarded to remote clients.\n *\n * @typeParam T - The type of items pushed into the channel.\n */\nexport class StreamChannel<T> implements AsyncIterable<T> {\n /** @internal Brand used by {@link StreamChannel.isInstance}. */\n readonly [STREAM_CHANNEL_BRAND] = true as const;\n\n /** Protocol channel name used for auto-forwarded events, if remote. */\n readonly channelName?: string;\n\n #items: T[] = [];\n #waiters: Array<() => void> = [];\n #done = false;\n #error: unknown;\n #onPush?: (item: T) => void;\n\n constructor(name?: string) {\n this.channelName = name;\n }\n\n /**\n * Create an in-process-only channel. Values remain available through\n * `run.extensions.<key>` but are not forwarded to remote clients.\n */\n static local<T>(): StreamChannel<T> {\n return new StreamChannel<T>();\n }\n\n /**\n * Create a channel whose pushes are forwarded to remote clients under\n * the given protocol channel name.\n */\n static remote<T>(name: string): StreamChannel<T> {\n return new StreamChannel<T>(name);\n }\n\n /**\n * Brand-based type guard that recognises any {@link StreamChannel}\n * instance, even ones originating from a different copy of this\n * package. Prefer this over `instanceof StreamChannel` when code\n * may observe channels that were constructed elsewhere.\n */\n static isInstance(value: unknown): value is StreamChannel<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n STREAM_CHANNEL_BRAND in value &&\n (value as { [STREAM_CHANNEL_BRAND]: unknown })[STREAM_CHANNEL_BRAND] ===\n true\n );\n }\n\n /**\n * Append an item to the channel. If this is a remote channel wired to a\n * mux, the item is also injected into the main protocol event stream under\n * {@link channelName}.\n */\n push(item: T): void {\n this.#items.push(item);\n this.#wake();\n this.#onPush?.(item);\n }\n\n /**\n * Returns an async iterator starting at position {@link startAt}. Each call\n * returns an independent cursor so multiple consumers can iterate the same\n * channel concurrently.\n */\n iterate(startAt = 0): AsyncIterator<T> {\n let cursor = startAt;\n return {\n next: async (): Promise<IteratorResult<T>> => {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n if (cursor < this.#items.length) {\n return { value: this.#items[cursor++], done: false };\n }\n if (this.#done) {\n if (this.#error) throw this.#error;\n return { value: undefined as unknown as T, done: true };\n }\n await new Promise<void>((resolve) => this.#waiters.push(resolve));\n }\n },\n };\n }\n\n /**\n * Creates an {@link AsyncIterable} backed by this channel, starting from\n * {@link startAt}.\n */\n toAsyncIterable(startAt = 0): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator]: () => this.iterate(startAt),\n };\n }\n\n /**\n * Creates a web {@link ReadableStream} that emits channel items as\n * Server-Sent Events. Useful for returning a channel directly from\n * `new Response(channel.toEventStream())`.\n */\n toEventStream(\n options: StreamChannelEventStreamOptions<T> = {}\n ): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const iterator = this.iterate(options.startAt);\n const event = options.event ?? this.channelName;\n const serialize =\n options.serialize ?? ((item: T) => JSON.stringify(item) ?? \"null\");\n\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const next = await iterator.next();\n if (next.done) {\n controller.close();\n return;\n }\n\n const lines: string[] = [];\n if (event != null) {\n lines.push(`event: ${event}`);\n }\n for (const line of serialize(next.value).split(/\\r\\n|\\r|\\n/)) {\n lines.push(`data: ${line}`);\n }\n\n controller.enqueue(encoder.encode(`${lines.join(\"\\n\")}\\n\\n`));\n } catch (error) {\n controller.error(error);\n }\n },\n async cancel() {\n await iterator.return?.();\n },\n });\n }\n\n /**\n * Returns the item at the given zero-based index.\n *\n * @throws {RangeError} If the index is out of bounds.\n */\n get(index: number): T {\n if (index < 0 || index >= this.#items.length) {\n throw new RangeError(\n `StreamChannel index ${index} out of bounds (size=${this.#items.length})`\n );\n }\n return this.#items[index];\n }\n\n /** The number of items currently buffered in the channel. */\n get size(): number {\n return this.#items.length;\n }\n\n /** Whether the channel has been closed or failed. */\n get done(): boolean {\n return this.#done;\n }\n\n /** Mark the channel as complete after all buffered items are consumed. */\n close(): void {\n this.#done = true;\n this.#wake();\n }\n\n /** Mark the channel as failed after all buffered items are consumed. */\n fail(err: unknown): void {\n this.#error = err;\n this.#done = true;\n this.#wake();\n }\n\n /** @internal Called by the mux to wire auto-forwarding. */\n _wire(fn: (item: T) => void): void {\n this.#onPush = fn;\n }\n\n /** @internal Called by the mux on normal completion. */\n _close(): void {\n this.close();\n }\n\n /** @internal Called by the mux on failure. */\n _fail(err: unknown): void {\n this.fail(err);\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this.iterate();\n }\n\n #wake(): void {\n const waiters = this.#waiters.splice(0);\n for (const w of waiters) w();\n }\n}\n\n/**\n * Type guard that tests whether a value is a {@link StreamChannel}.\n *\n * Uses a symbol brand rather than `instanceof` so channels built\n * against a different copy of this package (e.g. one bundled by the\n * `langchain` umbrella package) are still recognised.\n */\nexport function isStreamChannel(\n value: unknown\n): value is StreamChannel<unknown> {\n return StreamChannel.isInstance(value);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { LifecycleEntry, LifecycleTransformerOptions, MessagesTransformerProjection, ValuesTransformerProjection } from "./types.js";
|
|
2
|
+
import { LifecycleProjection, createLifecycleTransformer, filterLifecycleEntries } from "./lifecycle.js";
|
|
3
|
+
import { createMessagesTransformer } from "./messages.js";
|
|
4
|
+
import { SubgraphDiscoveryProjection, SubgraphDiscoveryTransformerOptions, createSubgraphDiscoveryTransformer, filterSubgraphHandles } from "./subgraphs.js";
|
|
5
|
+
import { createValuesTransformer } from "./values.js";
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
const require_stream_channel = require("../stream-channel.cjs");
|
|
2
|
+
const require_mux = require("../mux.cjs");
|
|
3
|
+
//#region src/stream/transformers/lifecycle.ts
|
|
4
|
+
/**
|
|
5
|
+
* Filter a lifecycle {@link StreamChannel} to only the entries whose
|
|
6
|
+
* namespace lies within the subtree rooted at {@link path}.
|
|
7
|
+
*
|
|
8
|
+
* Returns an `AsyncIterable` whose iterator yields every entry whose
|
|
9
|
+
* namespace either equals {@link path} or is a descendant of it.
|
|
10
|
+
* Iteration begins at {@link startAt}, so callers can capture the
|
|
11
|
+
* log's current size at construction time to skip entries emitted
|
|
12
|
+
* before the caller existed (e.g. a subgraph stream discovered
|
|
13
|
+
* mid-run shouldn't replay the root's `started`).
|
|
14
|
+
*
|
|
15
|
+
* @param log - The shared lifecycle log owned by the transformer.
|
|
16
|
+
* @param path - Namespace prefix to scope entries by (use `[]` for
|
|
17
|
+
* the root subtree, i.e. everything).
|
|
18
|
+
* @param startAt - Zero-based index into the log to begin from.
|
|
19
|
+
* @returns An async iterable of matching lifecycle entries.
|
|
20
|
+
*/
|
|
21
|
+
function filterLifecycleEntries(log, path, startAt = 0) {
|
|
22
|
+
return { [Symbol.asyncIterator]() {
|
|
23
|
+
const base = log.iterate(startAt);
|
|
24
|
+
return { async next() {
|
|
25
|
+
while (true) {
|
|
26
|
+
const result = await base.next();
|
|
27
|
+
if (result.done) return {
|
|
28
|
+
value: void 0,
|
|
29
|
+
done: true
|
|
30
|
+
};
|
|
31
|
+
if (require_mux.hasPrefix(result.value.namespace, path)) return {
|
|
32
|
+
value: result.value,
|
|
33
|
+
done: false
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
} };
|
|
37
|
+
} };
|
|
38
|
+
}
|
|
39
|
+
const DEFAULT_ROOT_GRAPH_NAME = "root";
|
|
40
|
+
function defaultGuessGraphName(ns) {
|
|
41
|
+
if (ns.length === 0) return DEFAULT_ROOT_GRAPH_NAME;
|
|
42
|
+
const last = ns[ns.length - 1];
|
|
43
|
+
const colon = last.indexOf(":");
|
|
44
|
+
return colon === -1 ? last : last.slice(0, colon);
|
|
45
|
+
}
|
|
46
|
+
function defaultSerializeError(err) {
|
|
47
|
+
if (err instanceof Error) return err.message;
|
|
48
|
+
if (typeof err === "string") return err;
|
|
49
|
+
try {
|
|
50
|
+
return JSON.stringify(err);
|
|
51
|
+
} catch {
|
|
52
|
+
return String(err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isRecord(value) {
|
|
56
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extract an upstream `cause` from a `lifecycle.started` payload, if
|
|
60
|
+
* the shape matches one of the known variants. Shape validation is
|
|
61
|
+
* intentionally loose: any object with a string `type` is accepted, so
|
|
62
|
+
* future protocol variants flow through unchanged.
|
|
63
|
+
*/
|
|
64
|
+
function extractCause(data) {
|
|
65
|
+
if (!isRecord(data)) return void 0;
|
|
66
|
+
if (data.event !== "started") return void 0;
|
|
67
|
+
const cause = data.cause;
|
|
68
|
+
if (!isRecord(cause)) return void 0;
|
|
69
|
+
if (typeof cause.type !== "string") return void 0;
|
|
70
|
+
return cause;
|
|
71
|
+
}
|
|
72
|
+
function extractTaskResultCompletion(data) {
|
|
73
|
+
if (!isRecord(data)) return void 0;
|
|
74
|
+
if (!("result" in data)) return void 0;
|
|
75
|
+
if (typeof data.name !== "string") return void 0;
|
|
76
|
+
if (typeof data.id !== "string") return void 0;
|
|
77
|
+
if (data.name.startsWith("__")) return void 0;
|
|
78
|
+
return {
|
|
79
|
+
name: data.name,
|
|
80
|
+
id: data.id
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Create the built-in lifecycle transformer.
|
|
85
|
+
*
|
|
86
|
+
* Marked as a {@link NativeStreamTransformer} so the run stream
|
|
87
|
+
* factory can expose `_lifecycleLog` via a dedicated getter
|
|
88
|
+
* (`run.lifecycle`) rather than through `run.extensions`.
|
|
89
|
+
*/
|
|
90
|
+
function createLifecycleTransformer(options = {}) {
|
|
91
|
+
const rootGraphName = options.rootGraphName ?? DEFAULT_ROOT_GRAPH_NAME;
|
|
92
|
+
const initialStatus = options.initialStatus ?? "running";
|
|
93
|
+
const emitRootOnRegister = options.emitRootOnRegister ?? true;
|
|
94
|
+
const getGraphName = options.getGraphName ?? defaultGuessGraphName;
|
|
95
|
+
const serializeError = options.serializeError ?? defaultSerializeError;
|
|
96
|
+
const getTerminalStatusOverride = options.getTerminalStatusOverride;
|
|
97
|
+
const log = require_stream_channel.StreamChannel.local();
|
|
98
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
99
|
+
const namespaceCause = /* @__PURE__ */ new Map();
|
|
100
|
+
const pendingInterruptIds = /* @__PURE__ */ new Set();
|
|
101
|
+
/**
|
|
102
|
+
* Child namespaces whose parent just saw an `updates` event with a
|
|
103
|
+
* `node` attribution. We defer the `lifecycle.completed` emission
|
|
104
|
+
* until the *next* inbound event (or `finalize`) so the parent's
|
|
105
|
+
* `updates` lands on the wire before its child is marked complete -
|
|
106
|
+
* matching the previous session behavior.
|
|
107
|
+
*/
|
|
108
|
+
const pendingCompletions = [];
|
|
109
|
+
let emitter;
|
|
110
|
+
let inSelfEmit = 0;
|
|
111
|
+
let finalized = false;
|
|
112
|
+
const resolveGraphName = (ns) => ns.length === 0 ? rootGraphName : getGraphName(ns);
|
|
113
|
+
const emit = (ns, status, extras) => {
|
|
114
|
+
const key = require_mux.nsKey(ns);
|
|
115
|
+
let current = namespaces.get(key);
|
|
116
|
+
const graphName = current?.graphName ?? resolveGraphName(ns);
|
|
117
|
+
if (current != null && current.status === status && current.graphName === graphName && extras?.error == null) return;
|
|
118
|
+
if (current == null) {
|
|
119
|
+
current = {
|
|
120
|
+
namespace: ns,
|
|
121
|
+
graphName,
|
|
122
|
+
status
|
|
123
|
+
};
|
|
124
|
+
namespaces.set(key, current);
|
|
125
|
+
} else current.status = status;
|
|
126
|
+
const data = {
|
|
127
|
+
event: status,
|
|
128
|
+
graph_name: graphName,
|
|
129
|
+
...extras?.cause != null ? { cause: extras.cause } : {},
|
|
130
|
+
...extras?.error != null ? { error: extras.error } : {}
|
|
131
|
+
};
|
|
132
|
+
const timestamp = Date.now();
|
|
133
|
+
log.push({
|
|
134
|
+
namespace: ns,
|
|
135
|
+
timestamp,
|
|
136
|
+
...data
|
|
137
|
+
});
|
|
138
|
+
if (ns.length === 0 && !emitRootOnRegister) return;
|
|
139
|
+
if (emitter == null) return;
|
|
140
|
+
inSelfEmit += 1;
|
|
141
|
+
try {
|
|
142
|
+
emitter.push(ns, {
|
|
143
|
+
type: "event",
|
|
144
|
+
seq: 0,
|
|
145
|
+
method: "lifecycle",
|
|
146
|
+
params: {
|
|
147
|
+
namespace: ns,
|
|
148
|
+
timestamp,
|
|
149
|
+
data
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
} finally {
|
|
153
|
+
inSelfEmit -= 1;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Ensures a record exists for `ns` without mutating its status. Used
|
|
158
|
+
* by hooks that need a canonical `graphName` for lookups before emit
|
|
159
|
+
* writes the first status. Status remains `undefined` until `emit`
|
|
160
|
+
* fires.
|
|
161
|
+
*/
|
|
162
|
+
const trackNamespace = (ns) => {
|
|
163
|
+
const key = require_mux.nsKey(ns);
|
|
164
|
+
let rec = namespaces.get(key);
|
|
165
|
+
if (rec == null) {
|
|
166
|
+
rec = {
|
|
167
|
+
namespace: ns,
|
|
168
|
+
graphName: resolveGraphName(ns),
|
|
169
|
+
status: void 0
|
|
170
|
+
};
|
|
171
|
+
namespaces.set(key, rec);
|
|
172
|
+
}
|
|
173
|
+
return rec;
|
|
174
|
+
};
|
|
175
|
+
const flushPendingCompletions = () => {
|
|
176
|
+
if (pendingCompletions.length === 0) return;
|
|
177
|
+
const toFlush = pendingCompletions.splice(0, pendingCompletions.length);
|
|
178
|
+
for (const completion of toFlush) {
|
|
179
|
+
const key = require_mux.nsKey(completion.namespace);
|
|
180
|
+
const rec = namespaces.get(key);
|
|
181
|
+
if (rec == null || rec.status !== "started") continue;
|
|
182
|
+
emit(completion.namespace, "completed");
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const enqueueCompletion = (completion) => {
|
|
186
|
+
const key = require_mux.nsKey(completion.namespace);
|
|
187
|
+
const rec = namespaces.get(key);
|
|
188
|
+
if (rec == null || rec.status !== "started") return;
|
|
189
|
+
if (pendingCompletions.some((pending) => require_mux.nsKey(pending.namespace) === key)) return;
|
|
190
|
+
pendingCompletions.push(completion);
|
|
191
|
+
};
|
|
192
|
+
const removePendingNodeCompletions = (parent, node) => {
|
|
193
|
+
for (let index = pendingCompletions.length - 1; index >= 0; index -= 1) {
|
|
194
|
+
const pending = pendingCompletions[index];
|
|
195
|
+
if (pending.source.type !== "node") continue;
|
|
196
|
+
if (pending.source.node !== node) continue;
|
|
197
|
+
if (require_mux.nsKey(pending.source.parent) !== require_mux.nsKey(parent)) continue;
|
|
198
|
+
pendingCompletions.splice(index, 1);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
const ensureStarted = (ns) => {
|
|
202
|
+
for (let length = 1; length <= ns.length; length += 1) {
|
|
203
|
+
const prefix = ns.slice(0, length);
|
|
204
|
+
const key = require_mux.nsKey(prefix);
|
|
205
|
+
if (namespaces.has(key)) continue;
|
|
206
|
+
trackNamespace(prefix);
|
|
207
|
+
const cause = namespaceCause.get(key);
|
|
208
|
+
emit(prefix, "started", cause != null ? { cause } : void 0);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const defaultTerminalStatus = () => pendingInterruptIds.size > 0 ? "interrupted" : "completed";
|
|
212
|
+
const cascadeTerminalStatus = (status) => {
|
|
213
|
+
for (const rec of namespaces.values()) {
|
|
214
|
+
if (rec.namespace.length === 0) continue;
|
|
215
|
+
if (rec.status !== "started") continue;
|
|
216
|
+
emit(rec.namespace, status);
|
|
217
|
+
}
|
|
218
|
+
emit([], status);
|
|
219
|
+
log.close();
|
|
220
|
+
};
|
|
221
|
+
const resolveTerminalStatusOverride = async () => {
|
|
222
|
+
if (getTerminalStatusOverride == null) return defaultTerminalStatus();
|
|
223
|
+
try {
|
|
224
|
+
return await getTerminalStatusOverride() ?? defaultTerminalStatus();
|
|
225
|
+
} catch {
|
|
226
|
+
return defaultTerminalStatus();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const findStartedChildForNode = (parentNamespace, node) => {
|
|
230
|
+
const prefix = `${node}:`;
|
|
231
|
+
for (const rec of namespaces.values()) {
|
|
232
|
+
if (rec.namespace.length !== parentNamespace.length + 1) continue;
|
|
233
|
+
if (rec.status !== "started") continue;
|
|
234
|
+
if (!require_mux.hasPrefix(rec.namespace, parentNamespace)) continue;
|
|
235
|
+
const last = rec.namespace[rec.namespace.length - 1];
|
|
236
|
+
if (last === node || last.startsWith(prefix)) return rec.namespace;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const findStartedChildForTask = (parentNamespace, task) => {
|
|
240
|
+
const namespace = [...parentNamespace, `${task.name}:${task.id}`];
|
|
241
|
+
return namespaces.get(require_mux.nsKey(namespace))?.status === "started" ? namespace : void 0;
|
|
242
|
+
};
|
|
243
|
+
return {
|
|
244
|
+
__native: true,
|
|
245
|
+
init() {
|
|
246
|
+
return {
|
|
247
|
+
_lifecycleLog: log,
|
|
248
|
+
lifecycle: filterLifecycleEntries(log, [], 0)
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
onRegister(handle) {
|
|
252
|
+
emitter = handle;
|
|
253
|
+
trackNamespace([]);
|
|
254
|
+
if (emitRootOnRegister) emit([], initialStatus);
|
|
255
|
+
},
|
|
256
|
+
process(event) {
|
|
257
|
+
const ns = event.params.namespace;
|
|
258
|
+
if (inSelfEmit > 0) return true;
|
|
259
|
+
const taskCompletion = event.method === "tasks" ? extractTaskResultCompletion(event.params.data) : void 0;
|
|
260
|
+
if (taskCompletion != null) removePendingNodeCompletions(ns, taskCompletion.name);
|
|
261
|
+
flushPendingCompletions();
|
|
262
|
+
if (event.method === "lifecycle") {
|
|
263
|
+
const cause = extractCause(event.params.data);
|
|
264
|
+
if (cause != null) namespaceCause.set(require_mux.nsKey(ns), cause);
|
|
265
|
+
ensureStarted(ns);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
ensureStarted(ns);
|
|
269
|
+
if (event.method === "input" && isRecord(event.params.data) && event.params.data.event === "requested") {
|
|
270
|
+
const id = event.params.data.id;
|
|
271
|
+
if (typeof id === "string") pendingInterruptIds.add(id);
|
|
272
|
+
}
|
|
273
|
+
if (taskCompletion != null) {
|
|
274
|
+
const childNamespace = findStartedChildForTask(ns, taskCompletion);
|
|
275
|
+
if (childNamespace != null) enqueueCompletion({
|
|
276
|
+
namespace: childNamespace,
|
|
277
|
+
source: { type: "task" }
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (event.method === "updates") {
|
|
281
|
+
const node = event.params.node;
|
|
282
|
+
if (typeof node === "string" && !node.startsWith("__")) {
|
|
283
|
+
const childNamespace = findStartedChildForNode(ns, node);
|
|
284
|
+
if (childNamespace != null) enqueueCompletion({
|
|
285
|
+
namespace: childNamespace,
|
|
286
|
+
source: {
|
|
287
|
+
type: "node",
|
|
288
|
+
parent: ns,
|
|
289
|
+
node
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
},
|
|
296
|
+
finalize() {
|
|
297
|
+
if (finalized) return;
|
|
298
|
+
finalized = true;
|
|
299
|
+
flushPendingCompletions();
|
|
300
|
+
if (getTerminalStatusOverride == null) {
|
|
301
|
+
cascadeTerminalStatus(defaultTerminalStatus());
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
return resolveTerminalStatusOverride().then(cascadeTerminalStatus).catch((err) => {
|
|
305
|
+
log.fail(err);
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
fail(err) {
|
|
309
|
+
if (finalized) return;
|
|
310
|
+
finalized = true;
|
|
311
|
+
const errorMessage = serializeError(err);
|
|
312
|
+
for (const rec of namespaces.values()) {
|
|
313
|
+
if (rec.namespace.length === 0) continue;
|
|
314
|
+
if (rec.status !== "started") continue;
|
|
315
|
+
emit(rec.namespace, "failed");
|
|
316
|
+
}
|
|
317
|
+
emit([], "failed", { error: errorMessage });
|
|
318
|
+
log.fail(err);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
//#endregion
|
|
323
|
+
exports.createLifecycleTransformer = createLifecycleTransformer;
|
|
324
|
+
exports.filterLifecycleEntries = filterLifecycleEntries;
|
|
325
|
+
|
|
326
|
+
//# sourceMappingURL=lifecycle.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.cjs","names":["hasPrefix","StreamChannel","nsKey"],"sources":["../../../src/stream/transformers/lifecycle.ts"],"sourcesContent":["/**\n * LifecycleTransformer - synthesizes `lifecycle` channel events that\n * track the status of the root run and every subgraph it spawns.\n *\n * The transformer is registered first in `createGraphRunStream` so that\n * every other transformer / consumer sees a coherent, authoritative\n * lifecycle stream. It is product-agnostic: deeper semantics (e.g.\n * DeepAgents' `SubagentTransformer` tool-call causation) reach the wire\n * by way of the re-entrant {@link StreamEmitter} - the transformer\n * stashes any `cause` attached upstream and re-emits its own\n * authoritative `lifecycle.started` with the correlation in place.\n *\n * Events are also pushed to a local {@link StreamChannel} so in-process\n * consumers can iterate `run.lifecycle` without filtering the main\n * event stream.\n */\n\nimport type {\n AgentStatus,\n LifecycleCause,\n LifecycleData,\n} from \"@langchain/protocol\";\n\nimport { hasPrefix, nsKey } from \"../mux.js\";\nimport { StreamChannel } from \"../stream-channel.js\";\nimport type {\n NativeStreamTransformer,\n Namespace,\n ProtocolEvent,\n StreamEmitter,\n} from \"../types.js\";\nimport type { LifecycleEntry, LifecycleTransformerOptions } from \"./types.js\";\n\n/**\n * Projection returned from the lifecycle transformer's `init()`.\n *\n * The local `StreamChannel` is closed automatically when the transformer\n * finalizes or fails. `_lifecycleLog` is intentionally underscore-prefixed to\n * signal that it is consumed by the run stream wiring\n * (see `run-stream.ts`) and not meant for direct user access -\n * consumers should read `run.lifecycle` instead.\n *\n * The `lifecycle` iterable is the root-scoped projection (prefix\n * `[]`, starting at offset `0`) mirroring the pattern used by the\n * subgraph discovery transformer. Root stream wiring consumes it\n * via `SET_LIFECYCLE_ITERABLE`; child streams are wired with their\n * own path-scoped iterable produced by `filterLifecycleEntries`.\n */\nexport interface LifecycleProjection {\n _lifecycleLog: StreamChannel<LifecycleEntry>;\n lifecycle: AsyncIterable<LifecycleEntry>;\n}\n\n/**\n * Filter a lifecycle {@link StreamChannel} to only the entries whose\n * namespace lies within the subtree rooted at {@link path}.\n *\n * Returns an `AsyncIterable` whose iterator yields every entry whose\n * namespace either equals {@link path} or is a descendant of it.\n * Iteration begins at {@link startAt}, so callers can capture the\n * log's current size at construction time to skip entries emitted\n * before the caller existed (e.g. a subgraph stream discovered\n * mid-run shouldn't replay the root's `started`).\n *\n * @param log - The shared lifecycle log owned by the transformer.\n * @param path - Namespace prefix to scope entries by (use `[]` for\n * the root subtree, i.e. everything).\n * @param startAt - Zero-based index into the log to begin from.\n * @returns An async iterable of matching lifecycle entries.\n */\nexport function filterLifecycleEntries(\n log: StreamChannel<LifecycleEntry>,\n path: Namespace,\n startAt = 0\n): AsyncIterable<LifecycleEntry> {\n return {\n [Symbol.asyncIterator](): AsyncIterator<LifecycleEntry> {\n const base = log.iterate(startAt);\n return {\n async next(): Promise<IteratorResult<LifecycleEntry>> {\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const result = await base.next();\n if (result.done) {\n return {\n value: undefined as unknown as LifecycleEntry,\n done: true,\n };\n }\n if (hasPrefix(result.value.namespace, path)) {\n return { value: result.value, done: false };\n }\n }\n },\n };\n },\n };\n}\n\nconst DEFAULT_ROOT_GRAPH_NAME = \"root\";\n\nfunction defaultGuessGraphName(ns: Namespace): string {\n if (ns.length === 0) return DEFAULT_ROOT_GRAPH_NAME;\n const last = ns[ns.length - 1];\n const colon = last.indexOf(\":\");\n return colon === -1 ? last : last.slice(0, colon);\n}\n\nfunction defaultSerializeError(err: unknown): string {\n // oxlint-disable-next-line no-instanceof/no-instanceof\n if (err instanceof Error) return err.message;\n if (typeof err === \"string\") return err;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Extract an upstream `cause` from a `lifecycle.started` payload, if\n * the shape matches one of the known variants. Shape validation is\n * intentionally loose: any object with a string `type` is accepted, so\n * future protocol variants flow through unchanged.\n */\nfunction extractCause(data: unknown): LifecycleCause | undefined {\n if (!isRecord(data)) return undefined;\n if (data.event !== \"started\") return undefined;\n const cause = data.cause;\n if (!isRecord(cause)) return undefined;\n if (typeof cause.type !== \"string\") return undefined;\n return cause as unknown as LifecycleCause;\n}\n\nfunction extractTaskResultCompletion(\n data: unknown\n): TaskResultCompletion | undefined {\n if (!isRecord(data)) return undefined;\n if (!(\"result\" in data)) return undefined;\n if (typeof data.name !== \"string\") return undefined;\n if (typeof data.id !== \"string\") return undefined;\n if (data.name.startsWith(\"__\")) return undefined;\n return { name: data.name, id: data.id };\n}\n\ninterface NamespaceRecord {\n readonly namespace: Namespace;\n readonly graphName: string;\n /** Last emitted status; `undefined` until `emit` fires for this namespace. */\n status: AgentStatus | undefined;\n}\n\ninterface TaskResultCompletion {\n readonly name: string;\n readonly id: string;\n}\n\ninterface PendingCompletion {\n readonly namespace: Namespace;\n readonly source:\n | { readonly type: \"task\" }\n | {\n readonly type: \"node\";\n readonly parent: Namespace;\n readonly node: string;\n };\n}\n\n/**\n * Create the built-in lifecycle transformer.\n *\n * Marked as a {@link NativeStreamTransformer} so the run stream\n * factory can expose `_lifecycleLog` via a dedicated getter\n * (`run.lifecycle`) rather than through `run.extensions`.\n */\nexport function createLifecycleTransformer(\n options: LifecycleTransformerOptions = {}\n): NativeStreamTransformer<LifecycleProjection> {\n const rootGraphName = options.rootGraphName ?? DEFAULT_ROOT_GRAPH_NAME;\n const initialStatus: AgentStatus = options.initialStatus ?? \"running\";\n const emitRootOnRegister = options.emitRootOnRegister ?? true;\n const getGraphName = options.getGraphName ?? defaultGuessGraphName;\n const serializeError = options.serializeError ?? defaultSerializeError;\n const getTerminalStatusOverride = options.getTerminalStatusOverride;\n\n const log = StreamChannel.local<LifecycleEntry>();\n const namespaces = new Map<string, NamespaceRecord>();\n const namespaceCause = new Map<string, LifecycleCause>();\n const pendingInterruptIds = new Set<string>();\n /**\n * Child namespaces whose parent just saw an `updates` event with a\n * `node` attribution. We defer the `lifecycle.completed` emission\n * until the *next* inbound event (or `finalize`) so the parent's\n * `updates` lands on the wire before its child is marked complete -\n * matching the previous session behavior.\n */\n const pendingCompletions: PendingCompletion[] = [];\n\n let emitter: StreamEmitter | undefined;\n let inSelfEmit = 0;\n let finalized = false;\n\n const resolveGraphName = (ns: Namespace): string =>\n ns.length === 0 ? rootGraphName : getGraphName(ns);\n\n const emit = (\n ns: Namespace,\n status: AgentStatus,\n extras?: { cause?: LifecycleCause; error?: string }\n ): void => {\n const key = nsKey(ns);\n let current = namespaces.get(key);\n const graphName = current?.graphName ?? resolveGraphName(ns);\n\n // Dedup: identical status + graph name + no error override => skip.\n if (\n current != null &&\n current.status === status &&\n current.graphName === graphName &&\n extras?.error == null\n ) {\n return;\n }\n\n if (current == null) {\n current = { namespace: ns, graphName, status };\n namespaces.set(key, current);\n } else {\n current.status = status;\n }\n\n const data: LifecycleData = {\n event: status,\n graph_name: graphName,\n ...(extras?.cause != null ? { cause: extras.cause } : {}),\n ...(extras?.error != null ? { error: extras.error } : {}),\n };\n\n const timestamp = Date.now();\n\n log.push({ namespace: ns, timestamp, ...data });\n\n if (ns.length === 0 && !emitRootOnRegister) return;\n\n if (emitter == null) return;\n\n inSelfEmit += 1;\n try {\n emitter.push(ns, {\n type: \"event\",\n seq: 0,\n method: \"lifecycle\",\n params: { namespace: ns, timestamp, data },\n });\n } finally {\n inSelfEmit -= 1;\n }\n };\n\n /**\n * Ensures a record exists for `ns` without mutating its status. Used\n * by hooks that need a canonical `graphName` for lookups before emit\n * writes the first status. Status remains `undefined` until `emit`\n * fires.\n */\n const trackNamespace = (ns: Namespace): NamespaceRecord => {\n const key = nsKey(ns);\n let rec = namespaces.get(key);\n if (rec == null) {\n rec = {\n namespace: ns,\n graphName: resolveGraphName(ns),\n status: undefined,\n };\n namespaces.set(key, rec);\n }\n return rec;\n };\n\n const flushPendingCompletions = (): void => {\n if (pendingCompletions.length === 0) return;\n const toFlush = pendingCompletions.splice(0, pendingCompletions.length);\n for (const completion of toFlush) {\n const key = nsKey(completion.namespace);\n const rec = namespaces.get(key);\n if (rec == null || rec.status !== \"started\") continue;\n emit(completion.namespace, \"completed\");\n }\n };\n\n const enqueueCompletion = (completion: PendingCompletion): void => {\n const key = nsKey(completion.namespace);\n const rec = namespaces.get(key);\n if (rec == null || rec.status !== \"started\") return;\n if (\n pendingCompletions.some((pending) => nsKey(pending.namespace) === key)\n ) {\n return;\n }\n pendingCompletions.push(completion);\n };\n\n const removePendingNodeCompletions = (\n parent: Namespace,\n node: string\n ): void => {\n for (let index = pendingCompletions.length - 1; index >= 0; index -= 1) {\n const pending = pendingCompletions[index];\n if (pending.source.type !== \"node\") continue;\n if (pending.source.node !== node) continue;\n if (nsKey(pending.source.parent) !== nsKey(parent)) continue;\n pendingCompletions.splice(index, 1);\n }\n };\n\n const ensureStarted = (ns: Namespace): void => {\n // Synthesize `lifecycle.started` for each unseen prefix of `ns`,\n // outermost first. Deepest-first would force consumers to see a\n // child's started before its parent, which is wrong.\n for (let length = 1; length <= ns.length; length += 1) {\n const prefix = ns.slice(0, length);\n const key = nsKey(prefix);\n if (namespaces.has(key)) continue;\n trackNamespace(prefix);\n const cause = namespaceCause.get(key);\n emit(prefix, \"started\", cause != null ? { cause } : undefined);\n }\n };\n\n const defaultTerminalStatus = (): AgentStatus =>\n pendingInterruptIds.size > 0 ? \"interrupted\" : \"completed\";\n\n const cascadeTerminalStatus = (status: AgentStatus): void => {\n for (const rec of namespaces.values()) {\n if (rec.namespace.length === 0) continue;\n if (rec.status !== \"started\") continue;\n emit(rec.namespace, status);\n }\n emit([], status);\n log.close();\n };\n\n const resolveTerminalStatusOverride = async (): Promise<AgentStatus> => {\n if (getTerminalStatusOverride == null) return defaultTerminalStatus();\n try {\n return (await getTerminalStatusOverride()) ?? defaultTerminalStatus();\n } catch {\n return defaultTerminalStatus();\n }\n };\n\n const findStartedChildForNode = (\n parentNamespace: Namespace,\n node: string\n ): Namespace | undefined => {\n const prefix = `${node}:`;\n for (const rec of namespaces.values()) {\n if (rec.namespace.length !== parentNamespace.length + 1) continue;\n if (rec.status !== \"started\") continue;\n if (!hasPrefix(rec.namespace, parentNamespace)) continue;\n const last = rec.namespace[rec.namespace.length - 1];\n if (last === node || last.startsWith(prefix)) return rec.namespace;\n }\n return undefined;\n };\n\n const findStartedChildForTask = (\n parentNamespace: Namespace,\n task: TaskResultCompletion\n ): Namespace | undefined => {\n const namespace = [...parentNamespace, `${task.name}:${task.id}`];\n const rec = namespaces.get(nsKey(namespace));\n return rec?.status === \"started\" ? namespace : undefined;\n };\n\n const transformer: NativeStreamTransformer<LifecycleProjection> = {\n __native: true,\n\n init() {\n return {\n _lifecycleLog: log,\n lifecycle: filterLifecycleEntries(log, [], 0),\n };\n },\n\n onRegister(handle: StreamEmitter) {\n emitter = handle;\n // Seed root record so cascade logic can see it, even when the\n // outer authority owns root emission.\n trackNamespace([]);\n if (emitRootOnRegister) {\n emit([], initialStatus);\n }\n },\n\n process(event: ProtocolEvent): boolean {\n const ns = event.params.namespace;\n\n // Re-entrant loopback: an event we emitted via `emitter.push`\n // is being routed back through this transformer by the mux.\n // Allow it through the wire unchanged.\n if (inSelfEmit > 0) return true;\n\n const taskCompletion =\n event.method === \"tasks\"\n ? extractTaskResultCompletion(event.params.data)\n : undefined;\n if (taskCompletion != null) {\n // Prefer exact task-result attribution over any ambiguous\n // `updates.node` completion deferred from the previous event.\n removePendingNodeCompletions(ns, taskCompletion.name);\n }\n\n // Flush any completions deferred by the previous event so the\n // wire order is [triggering event] -> [deferred completed].\n flushPendingCompletions();\n\n // Upstream `lifecycle` events: stash any `cause` attached by a\n // product-specific transformer (e.g. deepagents' SubagentTransformer),\n // synthesize our authoritative started/... for the namespace, and\n // suppress the original so we are the single source of truth.\n if (event.method === \"lifecycle\") {\n const cause = extractCause(event.params.data);\n if (cause != null) {\n namespaceCause.set(nsKey(ns), cause);\n }\n ensureStarted(ns);\n return false;\n }\n\n // Lifecycle for parent + any unseen prefix => synthesize started.\n ensureStarted(ns);\n\n // Track interrupt ids so `finalize` can decide between\n // `completed` and `interrupted`.\n if (\n event.method === \"input\" &&\n isRecord(event.params.data) &&\n event.params.data.event === \"requested\"\n ) {\n const id = (event.params.data as { id?: unknown }).id;\n if (typeof id === \"string\") {\n pendingInterruptIds.add(id);\n }\n }\n\n if (taskCompletion != null) {\n const childNamespace = findStartedChildForTask(ns, taskCompletion);\n if (childNamespace != null) {\n enqueueCompletion({\n namespace: childNamespace,\n source: { type: \"task\" },\n });\n }\n }\n\n // Defer child-node completion: the `updates` event carries the\n // node attribution; the corresponding child namespace is\n // `[...ns, \"<node>:<uuid>\"]`. We pick the oldest still-started\n // matching child (LangGraph emits one updates per completed task\n // so repeated calls drain parallel fan-outs in order).\n if (event.method === \"updates\") {\n const node = event.params.node;\n if (typeof node === \"string\" && !node.startsWith(\"__\")) {\n const childNamespace = findStartedChildForNode(ns, node);\n if (childNamespace != null) {\n enqueueCompletion({\n namespace: childNamespace,\n source: { type: \"node\", parent: ns, node },\n });\n }\n }\n }\n\n return true;\n },\n\n finalize(): void | PromiseLike<void> {\n if (finalized) return;\n finalized = true;\n flushPendingCompletions();\n\n if (getTerminalStatusOverride == null) {\n cascadeTerminalStatus(defaultTerminalStatus());\n return;\n }\n\n return resolveTerminalStatusOverride()\n .then(cascadeTerminalStatus)\n .catch((err) => {\n log.fail(err);\n });\n },\n\n fail(err: unknown) {\n if (finalized) return;\n finalized = true;\n const errorMessage = serializeError(err);\n\n // Cascade `failed` to every still-started namespace. Children\n // that had already entered a terminal state are left untouched\n // by the dedup guard in `emit`.\n for (const rec of namespaces.values()) {\n if (rec.namespace.length === 0) continue;\n if (rec.status !== \"started\") continue;\n emit(rec.namespace, \"failed\");\n }\n\n emit([], \"failed\", { error: errorMessage });\n log.fail(err);\n },\n };\n\n return transformer;\n}\n"],"mappings":"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"}
|