@langchain/langgraph-api 1.1.16 → 1.2.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.
Files changed (52) hide show
  1. package/dist/api/protocol.d.mts +7 -0
  2. package/dist/api/protocol.mjs +157 -0
  3. package/dist/api/runs.mjs +15 -4
  4. package/dist/command.mjs +1 -1
  5. package/dist/experimental/embed/constants.d.mts +3 -0
  6. package/dist/experimental/embed/constants.mjs +11 -0
  7. package/dist/experimental/embed/protocol.d.mts +9 -0
  8. package/dist/experimental/embed/protocol.mjs +453 -0
  9. package/dist/experimental/embed/runs.d.mts +8 -0
  10. package/dist/experimental/embed/runs.mjs +202 -0
  11. package/dist/experimental/embed/threads.d.mts +8 -0
  12. package/dist/experimental/embed/threads.mjs +151 -0
  13. package/dist/experimental/embed/types.d.mts +77 -0
  14. package/dist/experimental/embed/types.mjs +1 -0
  15. package/dist/experimental/embed/utils.d.mts +29 -0
  16. package/dist/experimental/embed/utils.mjs +62 -0
  17. package/dist/experimental/embed.d.mts +11 -31
  18. package/dist/experimental/embed.mjs +19 -399
  19. package/dist/graph/load.d.mts +2 -2
  20. package/dist/graph/load.utils.mjs +13 -3
  21. package/dist/protocol/constants.d.mts +7 -0
  22. package/dist/protocol/constants.mjs +7 -0
  23. package/dist/protocol/service.d.mts +101 -0
  24. package/dist/protocol/service.mjs +568 -0
  25. package/dist/protocol/session/event-normalizers.d.mts +52 -0
  26. package/dist/protocol/session/event-normalizers.mjs +162 -0
  27. package/dist/protocol/session/index.d.mts +261 -0
  28. package/dist/protocol/session/index.mjs +826 -0
  29. package/dist/protocol/session/internal-types.d.mts +67 -0
  30. package/dist/protocol/session/internal-types.mjs +28 -0
  31. package/dist/protocol/session/metadata.d.mts +24 -0
  32. package/dist/protocol/session/metadata.mjs +95 -0
  33. package/dist/protocol/session/namespace.d.mts +47 -0
  34. package/dist/protocol/session/namespace.mjs +62 -0
  35. package/dist/protocol/session/state-normalizers.d.mts +57 -0
  36. package/dist/protocol/session/state-normalizers.mjs +430 -0
  37. package/dist/protocol/session/tool-calls.d.mts +27 -0
  38. package/dist/protocol/session/tool-calls.mjs +59 -0
  39. package/dist/protocol/types.d.mts +121 -0
  40. package/dist/protocol/types.mjs +1 -0
  41. package/dist/queue.mjs +8 -2
  42. package/dist/schemas.d.mts +58 -58
  43. package/dist/semver/index.mjs +19 -1
  44. package/dist/server.mjs +22 -3
  45. package/dist/state.mjs +1 -1
  46. package/dist/storage/ops.mjs +17 -5
  47. package/dist/storage/persist.mjs +1 -1
  48. package/dist/storage/types.d.mts +10 -1
  49. package/dist/stream.d.mts +25 -4
  50. package/dist/stream.mjs +203 -11
  51. package/dist/utils/serde.mjs +1 -1
  52. package/package.json +16 -14
@@ -0,0 +1,430 @@
1
+ import { isRecord } from "./internal-types.mjs";
2
+ import { getTupleToolCallArgs, getTupleToolCallIdentity, normalizeFinalToolCallArgs, } from "./tool-calls.mjs";
3
+ const PROTOCOL_STATE_MESSAGE_TYPES = new Set([
4
+ "human",
5
+ "user",
6
+ "ai",
7
+ "assistant",
8
+ "system",
9
+ "tool",
10
+ "function",
11
+ "remove",
12
+ ]);
13
+ const MIME_TYPE_BY_AUDIO_FORMAT = {
14
+ mp3: "audio/mpeg",
15
+ wav: "audio/wav",
16
+ pcm16: "audio/wav",
17
+ pcm: "audio/wav",
18
+ opus: "audio/opus",
19
+ aac: "audio/aac",
20
+ flac: "audio/flac",
21
+ };
22
+ const PROTOCOL_CONTENT_BLOCK_TYPES = new Set([
23
+ "text",
24
+ "reasoning",
25
+ "tool_call",
26
+ "tool_call_chunk",
27
+ "invalid_tool_call",
28
+ "server_tool_call",
29
+ "server_tool_call_chunk",
30
+ "server_tool_call_result",
31
+ "image",
32
+ "audio",
33
+ "video",
34
+ "file",
35
+ "non_standard",
36
+ ]);
37
+ const normalizeAudioBlockFromAdditionalKwargs = (additionalKwargs) => {
38
+ const audio = isRecord(additionalKwargs?.audio)
39
+ ? additionalKwargs.audio
40
+ : undefined;
41
+ if (audio == null)
42
+ return undefined;
43
+ const data = typeof audio.data === "string" ? audio.data : undefined;
44
+ const url = typeof audio.url === "string" ? audio.url : undefined;
45
+ if (data == null && url == null)
46
+ return undefined;
47
+ const explicitMimeType = typeof audio.mime_type === "string"
48
+ ? audio.mime_type
49
+ : typeof audio.mimeType === "string"
50
+ ? audio.mimeType
51
+ : undefined;
52
+ const format = typeof audio.format === "string"
53
+ ? audio.format.toLowerCase()
54
+ : explicitMimeType != null
55
+ ? undefined
56
+ : "wav";
57
+ return {
58
+ type: "audio",
59
+ ...(typeof audio.id === "string" ? { id: audio.id } : {}),
60
+ ...(url != null ? { url } : {}),
61
+ ...(data != null ? { data } : {}),
62
+ ...(explicitMimeType != null
63
+ ? { mime_type: explicitMimeType }
64
+ : format != null && MIME_TYPE_BY_AUDIO_FORMAT[format] != null
65
+ ? { mime_type: MIME_TYPE_BY_AUDIO_FORMAT[format] }
66
+ : {}),
67
+ ...(typeof audio.transcript === "string"
68
+ ? { transcript: audio.transcript }
69
+ : {}),
70
+ };
71
+ };
72
+ const MEDIA_BLOCK_TYPES = new Set([
73
+ "image",
74
+ "audio",
75
+ "video",
76
+ "file",
77
+ ]);
78
+ const MIME_TYPE_BY_IMAGE_FORMAT = {
79
+ png: "image/png",
80
+ jpeg: "image/jpeg",
81
+ jpg: "image/jpeg",
82
+ webp: "image/webp",
83
+ gif: "image/gif",
84
+ };
85
+ /**
86
+ * Extracts OpenAI Responses API `image_generation_call` outputs from
87
+ * `additional_kwargs.tool_outputs` as protocol-shaped image blocks. These
88
+ * outputs carry base64 image payloads that do not appear in
89
+ * `message.content`, so we lift them into standard blocks for downstream
90
+ * consumers (including synthetic subagent emission on the public namespace).
91
+ *
92
+ * @param additionalKwargs - The message's `additional_kwargs` record.
93
+ * @returns An array of image blocks in the order they appear, or `[]`.
94
+ */
95
+ const normalizeImageBlocksFromAdditionalKwargs = (additionalKwargs) => {
96
+ const toolOutputs = additionalKwargs?.tool_outputs;
97
+ if (!Array.isArray(toolOutputs))
98
+ return [];
99
+ const blocks = [];
100
+ for (const entry of toolOutputs) {
101
+ if (!isRecord(entry) || entry.type !== "image_generation_call")
102
+ continue;
103
+ const data = typeof entry.result === "string" ? entry.result : undefined;
104
+ const url = typeof entry.url === "string" ? entry.url : undefined;
105
+ if (data == null && url == null)
106
+ continue;
107
+ const outputFormat = typeof entry.output_format === "string"
108
+ ? entry.output_format.toLowerCase()
109
+ : undefined;
110
+ const mimeType = (outputFormat != null
111
+ ? MIME_TYPE_BY_IMAGE_FORMAT[outputFormat]
112
+ : undefined) ?? "image/png";
113
+ blocks.push({
114
+ type: "image",
115
+ ...(typeof entry.id === "string" ? { id: entry.id } : {}),
116
+ ...(url != null ? { url } : {}),
117
+ ...(data != null ? { data } : {}),
118
+ mime_type: mimeType,
119
+ });
120
+ }
121
+ return blocks;
122
+ };
123
+ /**
124
+ * Converts common camelCase field names produced by LangChain content blocks
125
+ * (e.g. `mimeType`) into the snake_case shape mandated by the protocol
126
+ * (`mime_type`). This keeps media blocks emitted by providers such as
127
+ * `ChatOpenAI` (Responses API `image_generation` tool) readable by downstream
128
+ * clients without requiring each provider to opt into protocol casing.
129
+ */
130
+ const normalizeMediaBlockCasing = (block) => {
131
+ if (!MEDIA_BLOCK_TYPES.has(block.type))
132
+ return block;
133
+ const { mimeType, ...rest } = block;
134
+ if (typeof mimeType !== "string")
135
+ return block;
136
+ if (typeof rest.mime_type === "string")
137
+ return rest;
138
+ return { ...rest, mime_type: mimeType };
139
+ };
140
+ export const normalizeProtocolContentBlock = (value) => {
141
+ if (!isRecord(value) || typeof value.type !== "string")
142
+ return undefined;
143
+ if (PROTOCOL_CONTENT_BLOCK_TYPES.has(value.type)) {
144
+ return normalizeMediaBlockCasing(value);
145
+ }
146
+ if (value.type === "image_url") {
147
+ const rawImage = value.image_url;
148
+ if (typeof rawImage === "string") {
149
+ return { type: "image", url: rawImage };
150
+ }
151
+ if (isRecord(rawImage) && typeof rawImage.url === "string") {
152
+ return {
153
+ type: "image",
154
+ url: rawImage.url,
155
+ };
156
+ }
157
+ return undefined;
158
+ }
159
+ if (value.type === "input_audio") {
160
+ const rawAudio = isRecord(value.input_audio)
161
+ ? value.input_audio
162
+ : undefined;
163
+ if (rawAudio == null)
164
+ return undefined;
165
+ return {
166
+ type: "audio",
167
+ ...(typeof rawAudio.data === "string" ? { data: rawAudio.data } : {}),
168
+ ...(typeof rawAudio.mimeType === "string"
169
+ ? { mimeType: rawAudio.mimeType }
170
+ : {}),
171
+ };
172
+ }
173
+ return {
174
+ type: "non_standard",
175
+ value: { ...value },
176
+ };
177
+ };
178
+ export const normalizeProtocolMessageContent = (content, options) => {
179
+ const additionalKwargs = options?.additionalKwargs;
180
+ const extractExtras = () => {
181
+ const audioBlock = normalizeAudioBlockFromAdditionalKwargs(additionalKwargs);
182
+ const imageBlocks = normalizeImageBlocksFromAdditionalKwargs(additionalKwargs);
183
+ return { audioBlock, imageBlocks };
184
+ };
185
+ if (typeof content === "string") {
186
+ const { audioBlock, imageBlocks } = extractExtras();
187
+ if (audioBlock == null && imageBlocks.length === 0)
188
+ return content;
189
+ const blocks = [];
190
+ if (content.length > 0) {
191
+ blocks.push({ type: "text", text: content });
192
+ }
193
+ blocks.push(...imageBlocks);
194
+ if (audioBlock != null)
195
+ blocks.push(audioBlock);
196
+ return blocks;
197
+ }
198
+ if (!Array.isArray(content)) {
199
+ const { audioBlock, imageBlocks } = extractExtras();
200
+ if (audioBlock == null && imageBlocks.length === 0)
201
+ return content;
202
+ const blocks = [...imageBlocks];
203
+ if (audioBlock != null)
204
+ blocks.push(audioBlock);
205
+ return blocks;
206
+ }
207
+ const blocks = [];
208
+ for (const entry of content) {
209
+ if (typeof entry === "string") {
210
+ blocks.push({ type: "text", text: entry });
211
+ continue;
212
+ }
213
+ const normalized = normalizeProtocolContentBlock(entry);
214
+ if (normalized != null) {
215
+ blocks.push(normalized);
216
+ }
217
+ }
218
+ const { audioBlock, imageBlocks } = extractExtras();
219
+ for (const imageBlock of imageBlocks) {
220
+ const imageId = imageBlock.id;
221
+ const hasMatchingImage = blocks.some((block) => block.type === "image" &&
222
+ block.id === imageId &&
223
+ imageId != null);
224
+ if (!hasMatchingImage)
225
+ blocks.push(imageBlock);
226
+ }
227
+ if (audioBlock != null && !blocks.some((block) => block.type === "audio")) {
228
+ blocks.push(audioBlock);
229
+ }
230
+ return blocks.length > 0 ? blocks : content;
231
+ };
232
+ /**
233
+ * Normalizes protocol message type aliases into LangChain core message types.
234
+ *
235
+ * @param value - Raw message type.
236
+ * @returns The normalized message type, if recognized.
237
+ */
238
+ export const normalizeProtocolStateMessageType = (value) => {
239
+ switch (value) {
240
+ case "assistant":
241
+ return "ai";
242
+ case "user":
243
+ return "human";
244
+ default:
245
+ return typeof value === "string" ? value : undefined;
246
+ }
247
+ };
248
+ /**
249
+ * Checks whether a value matches the message shape used in state payloads.
250
+ *
251
+ * @param value - Raw state value.
252
+ * @returns Whether the value is a protocol-state message object.
253
+ */
254
+ export const isProtocolStateMessage = (value) => isRecord(value) &&
255
+ PROTOCOL_STATE_MESSAGE_TYPES.has(normalizeProtocolStateMessageType(value.type) ?? "");
256
+ /**
257
+ * Normalizes invalid tool calls embedded in serialized AI messages.
258
+ *
259
+ * @param value - Raw invalid tool call list.
260
+ * @returns Normalized invalid tool calls.
261
+ */
262
+ export const normalizeProtocolStateInvalidToolCalls = (value) => {
263
+ if (!Array.isArray(value))
264
+ return [];
265
+ const invalidToolCalls = [];
266
+ for (const rawInvalidToolCall of value) {
267
+ if (!isRecord(rawInvalidToolCall))
268
+ continue;
269
+ const identity = getTupleToolCallIdentity(rawInvalidToolCall);
270
+ invalidToolCalls.push({
271
+ ...(identity.id != null ? { id: identity.id } : {}),
272
+ ...(identity.name != null ? { name: identity.name } : {}),
273
+ ...(typeof rawInvalidToolCall.args === "string"
274
+ ? { args: rawInvalidToolCall.args }
275
+ : {}),
276
+ error: typeof rawInvalidToolCall.error === "string"
277
+ ? rawInvalidToolCall.error
278
+ : "Malformed args.",
279
+ type: "invalid_tool_call",
280
+ });
281
+ }
282
+ return invalidToolCalls;
283
+ };
284
+ /**
285
+ * Normalizes tool calls embedded in serialized AI messages.
286
+ *
287
+ * @param value - Raw tool call list.
288
+ * @returns Normalized valid and invalid tool call arrays.
289
+ */
290
+ export const normalizeProtocolStateToolCalls = (value) => {
291
+ if (!Array.isArray(value)) {
292
+ return {
293
+ toolCalls: [],
294
+ invalidToolCalls: [],
295
+ };
296
+ }
297
+ const toolCalls = [];
298
+ const invalidToolCalls = [];
299
+ for (const rawToolCall of value) {
300
+ if (!isRecord(rawToolCall))
301
+ continue;
302
+ const identity = getTupleToolCallIdentity(rawToolCall);
303
+ const rawArgs = getTupleToolCallArgs(rawToolCall);
304
+ const normalizedArgs = normalizeFinalToolCallArgs(rawArgs);
305
+ if (identity.name == null) {
306
+ invalidToolCalls.push({
307
+ ...(identity.id != null ? { id: identity.id } : {}),
308
+ ...(typeof rawArgs === "string" ? { args: rawArgs } : {}),
309
+ error: "Incomplete tool call.",
310
+ type: "invalid_tool_call",
311
+ });
312
+ continue;
313
+ }
314
+ if (!normalizedArgs.valid) {
315
+ invalidToolCalls.push({
316
+ ...(identity.id != null ? { id: identity.id } : {}),
317
+ name: identity.name,
318
+ ...(typeof normalizedArgs.args === "string"
319
+ ? { args: normalizedArgs.args }
320
+ : {}),
321
+ error: "Malformed args.",
322
+ type: "invalid_tool_call",
323
+ });
324
+ continue;
325
+ }
326
+ toolCalls.push({
327
+ ...(identity.id != null ? { id: identity.id } : {}),
328
+ name: identity.name,
329
+ args: normalizedArgs.args,
330
+ type: "tool_call",
331
+ });
332
+ }
333
+ return { toolCalls, invalidToolCalls };
334
+ };
335
+ /**
336
+ * Normalizes a single message embedded in protocol state payloads.
337
+ *
338
+ * @param value - Raw message object.
339
+ * @returns The normalized message shape.
340
+ */
341
+ export const normalizeProtocolStateMessage = (value) => {
342
+ const type = normalizeProtocolStateMessageType(value.type);
343
+ if (type == null)
344
+ return value;
345
+ const additionalKwargs = isRecord(value.additional_kwargs)
346
+ ? value.additional_kwargs
347
+ : undefined;
348
+ const message = {
349
+ type,
350
+ content: normalizeProtocolMessageContent("content" in value ? value.content : "", {
351
+ additionalKwargs: type === "ai" ? additionalKwargs : undefined,
352
+ }),
353
+ };
354
+ if (typeof value.id === "string") {
355
+ message.id = value.id;
356
+ }
357
+ if (typeof value.name === "string") {
358
+ message.name = value.name;
359
+ }
360
+ if ((type === "ai" || type === "human") &&
361
+ typeof value.example === "boolean") {
362
+ message.example = value.example;
363
+ }
364
+ if (type === "tool") {
365
+ if (typeof value.tool_call_id === "string") {
366
+ message.tool_call_id = value.tool_call_id;
367
+ }
368
+ if (value.status === "success" || value.status === "error") {
369
+ message.status = value.status;
370
+ }
371
+ if ("artifact" in value) {
372
+ message.artifact = value.artifact;
373
+ }
374
+ }
375
+ if (type === "ai") {
376
+ const rawToolCalls = Array.isArray(value.tool_calls) && value.tool_calls.length > 0
377
+ ? value.tool_calls
378
+ : Array.isArray(additionalKwargs?.tool_calls)
379
+ ? additionalKwargs.tool_calls
380
+ : undefined;
381
+ const normalizedToolCalls = normalizeProtocolStateToolCalls(rawToolCalls);
382
+ const normalizedInvalidToolCalls = Array.isArray(value.invalid_tool_calls) &&
383
+ value.invalid_tool_calls.length > 0
384
+ ? normalizeProtocolStateInvalidToolCalls(value.invalid_tool_calls)
385
+ : normalizedToolCalls.invalidToolCalls;
386
+ if (normalizedToolCalls.toolCalls.length > 0) {
387
+ message.tool_calls = normalizedToolCalls.toolCalls;
388
+ }
389
+ if (normalizedInvalidToolCalls.length > 0) {
390
+ message.invalid_tool_calls = normalizedInvalidToolCalls;
391
+ }
392
+ }
393
+ return message;
394
+ };
395
+ /**
396
+ * Recursively normalizes protocol state payloads before they are emitted.
397
+ *
398
+ * @param value - Raw state payload.
399
+ * @returns The normalized state payload.
400
+ */
401
+ export const normalizeProtocolStatePayload = (value) => {
402
+ if (Array.isArray(value)) {
403
+ return value.map((item) => isProtocolStateMessage(item)
404
+ ? normalizeProtocolStateMessage(item)
405
+ : normalizeProtocolStatePayload(item));
406
+ }
407
+ if (!isRecord(value))
408
+ return value;
409
+ const normalized = {};
410
+ for (const [key, entry] of Object.entries(value)) {
411
+ if (key === "__interrupt__") {
412
+ continue;
413
+ }
414
+ if (key === "messages" && Array.isArray(entry)) {
415
+ normalized[key] = entry.map((item) => isProtocolStateMessage(item)
416
+ ? normalizeProtocolStateMessage(item)
417
+ : item);
418
+ continue;
419
+ }
420
+ normalized[key] = normalizeProtocolStatePayload(entry);
421
+ }
422
+ return normalized;
423
+ };
424
+ /**
425
+ * Coerces arbitrary update payloads into the protocol values shape.
426
+ *
427
+ * @param value - Raw update payload.
428
+ * @returns A valid updates values payload.
429
+ */
430
+ export const asUpdateValues = (value) => isRecord(value) ? value : { value };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Extracts the stable identity fields for a tuple tool call.
3
+ *
4
+ * @param value - Serialized tool call payload.
5
+ * @returns The resolved identifier and function name, when present.
6
+ */
7
+ export declare const getTupleToolCallIdentity: (value: Record<string, unknown>) => {
8
+ id: string | undefined;
9
+ name: string | undefined;
10
+ };
11
+ /**
12
+ * Extracts the raw argument payload for a tuple tool call.
13
+ *
14
+ * @param value - Serialized tool call payload.
15
+ * @returns The raw arguments field, if present.
16
+ */
17
+ export declare const getTupleToolCallArgs: (value: Record<string, unknown>) => unknown;
18
+ /**
19
+ * Parses the final argument payload for a completed tool call.
20
+ *
21
+ * @param value - Raw tool call arguments.
22
+ * @returns The parsed arguments plus a validity flag.
23
+ */
24
+ export declare const normalizeFinalToolCallArgs: (value: unknown) => {
25
+ valid: boolean;
26
+ args: unknown;
27
+ };
@@ -0,0 +1,59 @@
1
+ import { isRecord } from "./internal-types.mjs";
2
+ /**
3
+ * Extracts the stable identity fields for a tuple tool call.
4
+ *
5
+ * @param value - Serialized tool call payload.
6
+ * @returns The resolved identifier and function name, when present.
7
+ */
8
+ export const getTupleToolCallIdentity = (value) => {
9
+ const nestedFunction = isRecord(value.function) ? value.function : undefined;
10
+ return {
11
+ id: typeof value.id === "string" ? value.id : undefined,
12
+ name: typeof value.name === "string"
13
+ ? value.name
14
+ : typeof nestedFunction?.name === "string"
15
+ ? nestedFunction.name
16
+ : undefined,
17
+ };
18
+ };
19
+ /**
20
+ * Extracts the raw argument payload for a tuple tool call.
21
+ *
22
+ * @param value - Serialized tool call payload.
23
+ * @returns The raw arguments field, if present.
24
+ */
25
+ export const getTupleToolCallArgs = (value) => {
26
+ if ("args" in value)
27
+ return value.args;
28
+ const nestedFunction = isRecord(value.function) ? value.function : undefined;
29
+ return nestedFunction?.arguments;
30
+ };
31
+ /**
32
+ * Parses the final argument payload for a completed tool call.
33
+ *
34
+ * @param value - Raw tool call arguments.
35
+ * @returns The parsed arguments plus a validity flag.
36
+ */
37
+ export const normalizeFinalToolCallArgs = (value) => {
38
+ if (isRecord(value)) {
39
+ return { valid: true, args: value };
40
+ }
41
+ if (typeof value === "string") {
42
+ if (value.length === 0) {
43
+ return { valid: true, args: {} };
44
+ }
45
+ try {
46
+ return {
47
+ valid: true,
48
+ args: JSON.parse(value),
49
+ };
50
+ }
51
+ catch {
52
+ return { valid: false, args: value };
53
+ }
54
+ }
55
+ if (value == null) {
56
+ return { valid: true, args: {} };
57
+ }
58
+ return { valid: true, args: value };
59
+ };
@@ -0,0 +1,121 @@
1
+ import type { AgentResult, AgentStatus, AgentTreeNode, Channel, Checkpoint, CheckpointSource, Command, CommandResponse, ContentBlockDeltaData, ContentBlockFinishData, ContentBlockStartData, CustomData, ErrorCode, ErrorResponse, Event, LifecycleCause, MessageErrorData, MessageFinishData, MessageMetadata, MessageStartData, MessagesData, Namespace, ResponseMeta, RunStartParams, RunResult, StateGetResult, SubscribeParams, SubscribeResult, ToolErrorData, ToolFinishedData, ToolOutputDeltaData, ToolStartedData, ToolsData, UnsubscribeParams, UpdatesEvent } from "@langchain/protocol";
2
+ export type { LifecycleCause };
3
+ import type { AuthContext } from "../auth/index.mjs";
4
+ import type { RunProtocolSession } from "./session/index.mjs";
5
+ /**
6
+ * Raw events emitted by the existing LangGraph run stream implementation
7
+ * before they are normalized into protocol-framed events.
8
+ *
9
+ * The session emits two independent events per persisted checkpoint:
10
+ * - `event: "values"` carries the full state snapshot on the `values`
11
+ * protocol channel.
12
+ * - `event: "checkpoints"` carries the lightweight {@link Checkpoint}
13
+ * envelope on the dedicated `checkpoints` channel, paired with the
14
+ * adjacent `values` event by `(namespace, step)` so fork/time-travel
15
+ * UIs can subscribe without also paying for full-state payloads.
16
+ *
17
+ * When {@link normalized} is `true` the payload has already been converted
18
+ * to its protocol shape by the in-process streaming layer
19
+ * (`streamEvents(..., { version: "v3" })`) and should be passed through
20
+ * without re-normalization.
21
+ */
22
+ export type SourceStreamEvent = {
23
+ id?: string;
24
+ event: string;
25
+ data: unknown;
26
+ normalized?: boolean;
27
+ };
28
+ /**
29
+ * Transport profiles currently exposed by the LangGraph API implementation.
30
+ */
31
+ export type ProtocolTransportName = "websocket" | "sse-http";
32
+ export type SupportedChannel = Extract<Channel, "values" | "updates" | "checkpoints" | "messages" | "tools" | "custom" | "lifecycle" | "input" | "tasks">;
33
+ export type EventMethodByChannel = {
34
+ values: "values";
35
+ updates: "updates";
36
+ checkpoints: "checkpoints";
37
+ messages: "messages";
38
+ tools: "tools";
39
+ custom: "custom";
40
+ lifecycle: "lifecycle";
41
+ input: "input.requested";
42
+ tasks: "tasks";
43
+ };
44
+ export type ProtocolResponseMeta = ResponseMeta;
45
+ export type ProtocolSuccess = CommandResponse;
46
+ export type ProtocolError = ErrorResponse;
47
+ export type ProtocolEvent = Event;
48
+ export type ProtocolCommand = Command;
49
+ export type ProtocolCommandByMethod<Method extends ProtocolCommand["method"]> = Extract<ProtocolCommand, {
50
+ method: Method;
51
+ }>;
52
+ export type ProtocolEventByMethod<Method extends SupportedChannel> = Extract<ProtocolEvent, {
53
+ method: EventMethodByChannel[Method];
54
+ }>;
55
+ export type ProtocolEventDataByMethod<Method extends SupportedChannel> = ProtocolEventByMethod<Method>["params"]["data"];
56
+ export type { AgentResult, AgentStatus, AgentTreeNode, Checkpoint, CheckpointSource, ContentBlockDeltaData, ContentBlockFinishData, ContentBlockStartData, CustomData, ErrorCode, MessageErrorData, MessageFinishData, MessageMetadata, MessagesData, MessageStartData, Namespace, RunStartParams, RunResult, StateGetResult, SubscribeParams, SubscribeResult, ToolErrorData, ToolFinishedData, ToolOutputDeltaData, ToolStartedData, ToolsData, UnsubscribeParams, UpdatesEvent, };
57
+ /**
58
+ * Per-connection filter for SSE event sinks.
59
+ *
60
+ * Each SSE `POST .../events` connection carries its own filter so the server
61
+ * can deliver only matching events without persisting subscription state.
62
+ */
63
+ export type EventSinkFilter = {
64
+ channels: Set<string>;
65
+ namespaces?: string[][];
66
+ depth?: number;
67
+ since?: number;
68
+ };
69
+ /**
70
+ * A single SSE event sink attached to a thread.
71
+ *
72
+ * `pendingReplay` is true while {@link ProtocolService.attachFilteredEventSink}
73
+ * is draining buffered events into this sink. While true, the live `send`
74
+ * callback must skip this sink so that the replay loop — not the live path —
75
+ * owns strict in-order delivery.
76
+ */
77
+ export type EventSinkEntry = {
78
+ id: string;
79
+ filter: EventSinkFilter;
80
+ send: (message: ProtocolEvent) => Promise<void> | void;
81
+ pendingReplay?: boolean;
82
+ };
83
+ /**
84
+ * Runtime state tracked for an active thread connection.
85
+ *
86
+ * In the thread-centric protocol, threads are durable but ephemeral
87
+ * connection state lives here: the active run session, attached SSE
88
+ * sinks, and a queue of events waiting for a sink.
89
+ */
90
+ export type ThreadRecord = {
91
+ threadId: string;
92
+ transport: ProtocolTransportName;
93
+ auth?: AuthContext;
94
+ assistantId?: string;
95
+ seq: number;
96
+ session?: RunProtocolSession;
97
+ currentRunId?: string;
98
+ /** WebSocket-only: single event delivery callback. */
99
+ sendEvent?: ((message: ProtocolEvent) => Promise<void> | void) | undefined;
100
+ /** SSE: per-connection filtered event sinks. */
101
+ eventSinks: Map<string, EventSinkEntry>;
102
+ /** Events buffered when no sink is attached yet. */
103
+ queuedEvents: ProtocolEvent[];
104
+ /** WebSocket-only: subscription commands replayed on new run sessions. */
105
+ activeSubscriptions: ProtocolCommand[];
106
+ /**
107
+ * WebSocket-only: subscribes that arrived before a run session was bound.
108
+ *
109
+ * The SDK opens its root-pump subscription (and legacy lifecycle/values
110
+ * subs) eagerly on thread creation so that no events are missed on fast
111
+ * runs. On WebSocket those subscribes can race ahead of the concurrent
112
+ * `run.start` and hit the service before `ensureRunSession` has bound a
113
+ * session. Rather than rejecting them with `no_such_run`, we park each
114
+ * command's response promise here and resolve it once the first session
115
+ * is bound — mirroring the cross-run `activeSubscriptions` replay path.
116
+ */
117
+ pendingSubscribes: Array<{
118
+ command: ProtocolCommand;
119
+ resolve: (response: ProtocolSuccess | ProtocolError | null) => void;
120
+ }>;
121
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/queue.mjs CHANGED
@@ -54,8 +54,14 @@ const worker = async (ops, run, attempt, signal) => {
54
54
  signal,
55
55
  ...(!temporary ? { onCheckpoint, onTaskResult } : undefined),
56
56
  });
57
- for await (const { event, data } of stream) {
58
- await ops.runs.stream.publish({ runId, resumable, event, data });
57
+ for await (const { event, data, normalized } of stream) {
58
+ await ops.runs.stream.publish({
59
+ runId,
60
+ resumable,
61
+ event,
62
+ data,
63
+ ...(normalized != null ? { normalized } : {}),
64
+ });
59
65
  }
60
66
  }
61
67
  catch (error) {