@langchain/langgraph 1.3.6 → 1.3.7

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.
@@ -8,4 +8,5 @@ require("./transformers/values.cjs");
8
8
  require("./transformers/index.cjs");
9
9
  require("./types.cjs");
10
10
  require("./run-stream.cjs");
11
+ require("./subscription.cjs");
11
12
  require("@langchain/core/language_models/stream");
@@ -1,5 +1,5 @@
1
1
  import { AgentStatus, ChatModelStream as ChatModelStream$1, InferExtensions, InterruptPayload, LifecycleCause, LifecycleData, MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, ToolsEventData, UpdatesEventData, UsageInfo, isNativeTransformer } from "./types.cjs";
2
- import { StreamChannel } from "./stream-channel.cjs";
2
+ import { StreamChannel, isStreamChannel } from "./stream-channel.cjs";
3
3
  import { ConvertToProtocolEventOptions, STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./convert.cjs";
4
4
  import { REJECT_VALUES, RESOLVE_VALUES, StreamHandle, StreamMux, SubgraphDiscovery } from "./mux.cjs";
5
5
  import { LifecycleEntry, LifecycleTransformerOptions } from "./transformers/types.cjs";
@@ -8,5 +8,6 @@ import { createMessagesTransformer } from "./transformers/messages.cjs";
8
8
  import { SubgraphDiscoveryProjection, SubgraphDiscoveryTransformerOptions, createSubgraphDiscoveryTransformer, filterSubgraphHandles } from "./transformers/subgraphs.cjs";
9
9
  import { createValuesTransformer } from "./transformers/values.cjs";
10
10
  import { CreateGraphRunStreamOptions, GraphRunStream, SET_LIFECYCLE_ITERABLE, SET_MESSAGES_ITERABLE, SET_VALUES_LOG, SubgraphRunStream, createGraphRunStream } from "./run-stream.cjs";
11
+ import { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment } from "./subscription.cjs";
11
12
  import { ChatModelStream as ChatModelStreamImpl } from "@langchain/core/language_models/stream";
12
13
  export { ChatModelStreamImpl };
@@ -1,5 +1,5 @@
1
1
  import { AgentStatus, ChatModelStream as ChatModelStream$1, InferExtensions, InterruptPayload, LifecycleCause, LifecycleData, MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, ToolsEventData, UpdatesEventData, UsageInfo, isNativeTransformer } from "./types.js";
2
- import { StreamChannel } from "./stream-channel.js";
2
+ import { StreamChannel, isStreamChannel } from "./stream-channel.js";
3
3
  import { ConvertToProtocolEventOptions, STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./convert.js";
4
4
  import { REJECT_VALUES, RESOLVE_VALUES, StreamHandle, StreamMux, SubgraphDiscovery } from "./mux.js";
5
5
  import { LifecycleEntry, LifecycleTransformerOptions } from "./transformers/types.js";
@@ -8,5 +8,6 @@ import { createMessagesTransformer } from "./transformers/messages.js";
8
8
  import { SubgraphDiscoveryProjection, SubgraphDiscoveryTransformerOptions, createSubgraphDiscoveryTransformer, filterSubgraphHandles } from "./transformers/subgraphs.js";
9
9
  import { createValuesTransformer } from "./transformers/values.js";
10
10
  import { CreateGraphRunStreamOptions, GraphRunStream, SET_LIFECYCLE_ITERABLE, SET_MESSAGES_ITERABLE, SET_VALUES_LOG, SubgraphRunStream, createGraphRunStream } from "./run-stream.js";
11
+ import { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment } from "./subscription.js";
11
12
  import { ChatModelStream as ChatModelStreamImpl } from "@langchain/core/language_models/stream";
12
13
  export { ChatModelStreamImpl };
@@ -8,5 +8,6 @@ import "./transformers/values.js";
8
8
  import "./transformers/index.js";
9
9
  import "./types.js";
10
10
  import "./run-stream.js";
11
+ import "./subscription.js";
11
12
  import { ChatModelStream as ChatModelStreamImpl } from "@langchain/core/language_models/stream";
12
13
  export { ChatModelStreamImpl };
@@ -124,6 +124,14 @@ declare class StreamChannel<T> implements AsyncIterable<T> {
124
124
  _fail(err: unknown): void;
125
125
  [Symbol.asyncIterator](): AsyncIterator<T>;
126
126
  }
127
+ /**
128
+ * Type guard that tests whether a value is a {@link StreamChannel}.
129
+ *
130
+ * Uses a symbol brand rather than `instanceof` so channels built
131
+ * against a different copy of this package (e.g. one bundled by the
132
+ * `langchain` umbrella package) are still recognised.
133
+ */
134
+ declare function isStreamChannel(value: unknown): value is StreamChannel<unknown>;
127
135
  //#endregion
128
- export { StreamChannel };
136
+ export { StreamChannel, isStreamChannel };
129
137
  //# sourceMappingURL=stream-channel.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-channel.d.cts","names":[],"sources":["../../src/stream/stream-channel.ts"],"mappings":";;AA0BA;;;;;AAIA;;;;;;;;;;;;AA+BA;;;;;;;cAnCa,oBAAA;AAAA,UAII,+BAAA;EAsE6B;;;;;EAhE5C,KAAA;EA6H2C;;;;;EAvH3C,OAAA;EA+MwC;;;;;EAzMxC,SAAA,IAAa,IAAA,EAAM,CAAA;AAAA;;;;;;;;;;;cAaR,aAAA,eAA4B,aAAA,CAAc,CAAA;EAAA;EA6BvC;EAAA,UA3BJ,oBAAA;EA2BsB;EAAA,SAxBvB,WAAA;EAQT,WAAA,CAAY,IAAA;EA0BM;;;;EAAA,OAlBX,KAAA,GAAA,CAAA,GAAY,aAAA,CAAc,CAAA;EAiC5B;;;;EAAA,OAzBE,MAAA,GAAA,CAAU,IAAA,WAAe,aAAA,CAAc,CAAA;EA2D9C;;;;;;EAAA,OAjDO,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,aAAA;EA6D1C;;;;;EA9CF,IAAA,CAAK,IAAA,EAAM,CAAA;EAiGP;;;;;EAtFJ,OAAA,CAAQ,OAAA,YAAc,aAAA,CAAc,CAAA;EA6GnB;;;;EAtFjB,eAAA,CAAgB,OAAA,YAAc,aAAA,CAAc,CAAA;EAgGtC;;;;;EArFN,aAAA,CACE,OAAA,GAAS,+BAAA,CAAgC,CAAA,IACxC,cAAA,CAAe,UAAA;EAuFuB;;;;;EA/CzC,GAAA,CAAI,KAAA,WAAgB,CAAA;;MAUhB,IAAA,CAAA;;MAKA,IAAA,CAAA;;EAKJ,KAAA,CAAA;;EAMA,IAAA,CAAK,GAAA;;EAOL,KAAA,CAAM,EAAA,GAAK,IAAA,EAAM,CAAA;;EAKjB,MAAA,CAAA;;EAKA,KAAA,CAAM,GAAA;EAAA,CAIL,MAAA,CAAO,aAAA,KAAkB,aAAA,CAAc,CAAA;AAAA"}
1
+ {"version":3,"file":"stream-channel.d.cts","names":[],"sources":["../../src/stream/stream-channel.ts"],"mappings":";;AA0BA;;;;;AAIA;;;;;;;;;;;;AA+BA;;;;;;;cAnCa,oBAAA;AAAA,UAII,+BAAA;EAsE6B;;;;;EAhE5C,KAAA;EA6H2C;;;;;EAvH3C,OAAA;EA+MwC;;;;;EAzMxC,SAAA,IAAa,IAAA,EAAM,CAAA;AAAA;;;;;;;;;;;cAaR,aAAA,eAA4B,aAAA,CAAc,CAAA;EAAA;EA6BvC;EAAA,UA3BJ,oBAAA;EA2BsB;EAAA,SAxBvB,WAAA;EAQT,WAAA,CAAY,IAAA;EA0BM;;;;EAAA,OAlBX,KAAA,GAAA,CAAA,GAAY,aAAA,CAAc,CAAA;EAiC5B;;;;EAAA,OAzBE,MAAA,GAAA,CAAU,IAAA,WAAe,aAAA,CAAc,CAAA;EA2D9C;;;;;;EAAA,OAjDO,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,aAAA;EA6D1C;;;;;EA9CF,IAAA,CAAK,IAAA,EAAM,CAAA;EAiGP;;;;;EAtFJ,OAAA,CAAQ,OAAA,YAAc,aAAA,CAAc,CAAA;EA6GnB;;;;EAtFjB,eAAA,CAAgB,OAAA,YAAc,aAAA,CAAc,CAAA;EAgGtC;;;;;EArFN,aAAA,CACE,OAAA,GAAS,+BAAA,CAAgC,CAAA,IACxC,cAAA,CAAe,UAAA;EAuFuB;AAiB3C;;;;EAhEE,GAAA,CAAI,KAAA,WAAgB,CAAA;EAkEnB;EAAA,IAxDG,IAAA,CAAA;EAwDmB;EAAA,IAnDnB,IAAA,CAAA;;EAKJ,KAAA,CAAA;;EAMA,IAAA,CAAK,GAAA;;EAOL,KAAA,CAAM,EAAA,GAAK,IAAA,EAAM,CAAA;;EAKjB,MAAA,CAAA;;EAKA,KAAA,CAAM,GAAA;EAAA,CAIL,MAAA,CAAO,aAAA,KAAkB,aAAA,CAAc,CAAA;AAAA;;;;;;;;iBAiB1B,eAAA,CACd,KAAA,YACC,KAAA,IAAS,aAAA"}
@@ -124,6 +124,14 @@ declare class StreamChannel<T> implements AsyncIterable<T> {
124
124
  _fail(err: unknown): void;
125
125
  [Symbol.asyncIterator](): AsyncIterator<T>;
126
126
  }
127
+ /**
128
+ * Type guard that tests whether a value is a {@link StreamChannel}.
129
+ *
130
+ * Uses a symbol brand rather than `instanceof` so channels built
131
+ * against a different copy of this package (e.g. one bundled by the
132
+ * `langchain` umbrella package) are still recognised.
133
+ */
134
+ declare function isStreamChannel(value: unknown): value is StreamChannel<unknown>;
127
135
  //#endregion
128
- export { StreamChannel };
136
+ export { StreamChannel, isStreamChannel };
129
137
  //# sourceMappingURL=stream-channel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream-channel.d.ts","names":[],"sources":["../../src/stream/stream-channel.ts"],"mappings":";;AA0BA;;;;;AAIA;;;;;;;;;;;;AA+BA;;;;;;;cAnCa,oBAAA;AAAA,UAII,+BAAA;EAsE6B;;;;;EAhE5C,KAAA;EA6H2C;;;;;EAvH3C,OAAA;EA+MwC;;;;;EAzMxC,SAAA,IAAa,IAAA,EAAM,CAAA;AAAA;;;;;;;;;;;cAaR,aAAA,eAA4B,aAAA,CAAc,CAAA;EAAA;EA6BvC;EAAA,UA3BJ,oBAAA;EA2BsB;EAAA,SAxBvB,WAAA;EAQT,WAAA,CAAY,IAAA;EA0BM;;;;EAAA,OAlBX,KAAA,GAAA,CAAA,GAAY,aAAA,CAAc,CAAA;EAiC5B;;;;EAAA,OAzBE,MAAA,GAAA,CAAU,IAAA,WAAe,aAAA,CAAc,CAAA;EA2D9C;;;;;;EAAA,OAjDO,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,aAAA;EA6D1C;;;;;EA9CF,IAAA,CAAK,IAAA,EAAM,CAAA;EAiGP;;;;;EAtFJ,OAAA,CAAQ,OAAA,YAAc,aAAA,CAAc,CAAA;EA6GnB;;;;EAtFjB,eAAA,CAAgB,OAAA,YAAc,aAAA,CAAc,CAAA;EAgGtC;;;;;EArFN,aAAA,CACE,OAAA,GAAS,+BAAA,CAAgC,CAAA,IACxC,cAAA,CAAe,UAAA;EAuFuB;;;;;EA/CzC,GAAA,CAAI,KAAA,WAAgB,CAAA;;MAUhB,IAAA,CAAA;;MAKA,IAAA,CAAA;;EAKJ,KAAA,CAAA;;EAMA,IAAA,CAAK,GAAA;;EAOL,KAAA,CAAM,EAAA,GAAK,IAAA,EAAM,CAAA;;EAKjB,MAAA,CAAA;;EAKA,KAAA,CAAM,GAAA;EAAA,CAIL,MAAA,CAAO,aAAA,KAAkB,aAAA,CAAc,CAAA;AAAA"}
1
+ {"version":3,"file":"stream-channel.d.ts","names":[],"sources":["../../src/stream/stream-channel.ts"],"mappings":";;AA0BA;;;;;AAIA;;;;;;;;;;;;AA+BA;;;;;;;cAnCa,oBAAA;AAAA,UAII,+BAAA;EAsE6B;;;;;EAhE5C,KAAA;EA6H2C;;;;;EAvH3C,OAAA;EA+MwC;;;;;EAzMxC,SAAA,IAAa,IAAA,EAAM,CAAA;AAAA;;;;;;;;;;;cAaR,aAAA,eAA4B,aAAA,CAAc,CAAA;EAAA;EA6BvC;EAAA,UA3BJ,oBAAA;EA2BsB;EAAA,SAxBvB,WAAA;EAQT,WAAA,CAAY,IAAA;EA0BM;;;;EAAA,OAlBX,KAAA,GAAA,CAAA,GAAY,aAAA,CAAc,CAAA;EAiC5B;;;;EAAA,OAzBE,MAAA,GAAA,CAAU,IAAA,WAAe,aAAA,CAAc,CAAA;EA2D9C;;;;;;EAAA,OAjDO,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,aAAA;EA6D1C;;;;;EA9CF,IAAA,CAAK,IAAA,EAAM,CAAA;EAiGP;;;;;EAtFJ,OAAA,CAAQ,OAAA,YAAc,aAAA,CAAc,CAAA;EA6GnB;;;;EAtFjB,eAAA,CAAgB,OAAA,YAAc,aAAA,CAAc,CAAA;EAgGtC;;;;;EArFN,aAAA,CACE,OAAA,GAAS,+BAAA,CAAgC,CAAA,IACxC,cAAA,CAAe,UAAA;EAuFuB;AAiB3C;;;;EAhEE,GAAA,CAAI,KAAA,WAAgB,CAAA;EAkEnB;EAAA,IAxDG,IAAA,CAAA;EAwDmB;EAAA,IAnDnB,IAAA,CAAA;;EAKJ,KAAA,CAAA;;EAMA,IAAA,CAAK,GAAA;;EAOL,KAAA,CAAM,EAAA,GAAK,IAAA,EAAM,CAAA;;EAKjB,MAAA,CAAA;;EAKA,KAAA,CAAM,GAAA;EAAA,CAIL,MAAA,CAAO,aAAA,KAAkB,aAAA,CAAc,CAAA;AAAA;;;;;;;;iBAiB1B,eAAA,CACd,KAAA,YACC,KAAA,IAAS,aAAA"}
@@ -0,0 +1,136 @@
1
+ //#region src/stream/subscription.ts
2
+ /**
3
+ * Strip dynamic suffixes (after `:`) from a namespace segment.
4
+ *
5
+ * Server-emitted namespaces contain runtime-generated suffixes like
6
+ * `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
7
+ * static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
8
+ * `api/langgraph_api/protocol/namespace.py`.
9
+ *
10
+ * @param segment - Raw namespace segment.
11
+ * @returns The stable graph-oriented portion of the segment.
12
+ */
13
+ function normalizeNamespaceSegment(segment) {
14
+ const idx = segment.indexOf(":");
15
+ return idx === -1 ? segment : segment.slice(0, idx);
16
+ }
17
+ /**
18
+ * Whether `namespace` starts with `prefix`.
19
+ *
20
+ * Segments are compared literally first; if the prefix segment itself contains
21
+ * no `:`, the candidate segment is also compared after its dynamic suffix is
22
+ * stripped (see {@link normalizeNamespaceSegment}). This mirrors
23
+ * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
24
+ * filtering and client-side per-subscription narrowing stay consistent.
25
+ *
26
+ * @param namespace - Event namespace to test.
27
+ * @param prefix - Subscription namespace prefix.
28
+ */
29
+ function isPrefixMatch(namespace, prefix) {
30
+ if (prefix.length > namespace.length) return false;
31
+ for (let i = 0; i < prefix.length; i += 1) {
32
+ const segment = prefix[i];
33
+ const candidate = namespace[i];
34
+ if (candidate === segment) continue;
35
+ if (segment.includes(":")) return false;
36
+ if (normalizeNamespaceSegment(candidate) === segment) continue;
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ function namespaceMatches(eventNamespace, prefixes, depth) {
42
+ if (!prefixes || prefixes.length === 0) return true;
43
+ return prefixes.some((prefix) => {
44
+ if (!isPrefixMatch(eventNamespace, prefix)) return false;
45
+ if (depth === void 0) return true;
46
+ return eventNamespace.length - prefix.length <= depth;
47
+ });
48
+ }
49
+ /**
50
+ * The base protocol subscription channels, excluding the templated
51
+ * `custom:<name>` form. This is the runtime counterpart to the `Channel`
52
+ * union from `@langchain/protocol` and mirrors the channel set a server
53
+ * recognizes when filtering its event sinks.
54
+ */
55
+ const SUPPORTED_CHANNELS = new Set([
56
+ "values",
57
+ "updates",
58
+ "messages",
59
+ "tools",
60
+ "lifecycle",
61
+ "input",
62
+ "checkpoints",
63
+ "tasks",
64
+ "custom"
65
+ ]);
66
+ /**
67
+ * Whether `value` names a protocol subscription channel — either a base
68
+ * channel (`"messages"`, `"values"`, …) or a named custom channel
69
+ * (`"custom:<name>"`). Unknown/future method names return `false`,
70
+ * mirroring {@link inferChannel}.
71
+ *
72
+ * @param value - Candidate channel name.
73
+ */
74
+ function isSupportedChannel(value) {
75
+ return SUPPORTED_CHANNELS.has(value) || value.startsWith("custom:");
76
+ }
77
+ /**
78
+ * Maps a protocol event method to its subscription {@link Channel}.
79
+ *
80
+ * Returns `undefined` for unrecognized methods so that new server-side
81
+ * channels (e.g. from extension transformers) don't break existing
82
+ * subscribers. The `custom` method resolves to the named `custom:<name>`
83
+ * channel when the payload carries a `name`, otherwise the bare `custom`
84
+ * channel. Both `"input"` and the wire-level `"input.requested"` map to the
85
+ * `input` channel.
86
+ *
87
+ * @param event - Event whose method should be mapped to a channel.
88
+ */
89
+ function inferChannel(event) {
90
+ switch (event.method) {
91
+ case "values": return "values";
92
+ case "checkpoints": return "checkpoints";
93
+ case "updates": return "updates";
94
+ case "messages": return "messages";
95
+ case "tools": return "tools";
96
+ case "custom": {
97
+ const data = event.params.data;
98
+ return data?.name != null ? `custom:${data.name}` : "custom";
99
+ }
100
+ case "lifecycle": return "lifecycle";
101
+ case "input":
102
+ case "input.requested": return "input";
103
+ case "tasks": return "tasks";
104
+ default: return;
105
+ }
106
+ }
107
+ /**
108
+ * Returns whether an event should be delivered for a subscription definition.
109
+ *
110
+ * When the definition carries a `since` replay cursor, events at or before
111
+ * that sequence number are excluded — letting the same predicate drive both
112
+ * live fan-out and buffered replay over a {@link StreamChannel}.
113
+ *
114
+ * @param event - Event being checked for delivery.
115
+ * @param definition - Subscription filter definition to evaluate against.
116
+ * The optional `since` field (a `seq` cursor) is read leniently because it
117
+ * is not a declared field on the base {@link SubscribeParams} shape.
118
+ */
119
+ function matchesSubscription(event, definition) {
120
+ const since = definition.since;
121
+ if (typeof since === "number" && (event.seq ?? 0) <= since) return false;
122
+ const channel = inferChannel(event);
123
+ if (channel === void 0) return false;
124
+ const channels = definition.channels;
125
+ if (!(channels.includes(channel) || channel.startsWith("custom:") && channels.includes("custom"))) return false;
126
+ return namespaceMatches(event.params.namespace, definition.namespaces, definition.depth);
127
+ }
128
+ //#endregion
129
+ exports.SUPPORTED_CHANNELS = SUPPORTED_CHANNELS;
130
+ exports.inferChannel = inferChannel;
131
+ exports.isPrefixMatch = isPrefixMatch;
132
+ exports.isSupportedChannel = isSupportedChannel;
133
+ exports.matchesSubscription = matchesSubscription;
134
+ exports.normalizeNamespaceSegment = normalizeNamespaceSegment;
135
+
136
+ //# sourceMappingURL=subscription.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.cjs","names":[],"sources":["../../src/stream/subscription.ts"],"sourcesContent":["/**\n * Subscription matching and channel inference for the v2 streaming protocol.\n *\n * These helpers are the building blocks a custom transport / server needs to\n * fan protocol events out to subscribers: map an event to its logical\n * {@link Channel} ({@link inferChannel}) and decide whether a buffered event\n * should be delivered for a given {@link SubscribeParams} filter\n * ({@link matchesSubscription}). They are typed against the minimal\n * {@link MatchableEvent} shape so the same predicate works on the core\n * {@link ProtocolEvent} produced by {@link convertToProtocolEvent} /\n * {@link StreamChannel} and on the wire-level `Event` from\n * `@langchain/protocol`.\n */\n\nimport type { Channel, SubscribeParams } from \"@langchain/protocol\";\nimport type { Namespace } from \"./types.js\";\n\n/**\n * Minimal protocol-event shape consumed by {@link inferChannel} and\n * {@link matchesSubscription}.\n *\n * Both the core {@link ProtocolEvent} and the wire-level `Event` from\n * `@langchain/protocol` structurally satisfy this contract, so the same\n * predicates can drive in-process fan-out, buffered replay, and server-side\n * (SSE / WebSocket) event-sink filtering without coupling to a single event\n * type.\n */\nexport interface MatchableEvent {\n /** Logical stream channel; see {@link inferChannel}. */\n readonly method: string;\n\n /** Monotonic sequence number, when present. Used by the `since` cursor. */\n readonly seq?: number;\n\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data?: unknown;\n };\n}\n\n/**\n * Strip dynamic suffixes (after `:`) from a namespace segment.\n *\n * Server-emitted namespaces contain runtime-generated suffixes like\n * `\"fetcher:abc-uuid\"`, while user-supplied subscription filters are typically\n * static names (`\"fetcher\"`). Mirrors `normalize_namespace_segment` in\n * `api/langgraph_api/protocol/namespace.py`.\n *\n * @param segment - Raw namespace segment.\n * @returns The stable graph-oriented portion of the segment.\n */\nexport function normalizeNamespaceSegment(segment: string): string {\n const idx = segment.indexOf(\":\");\n return idx === -1 ? segment : segment.slice(0, idx);\n}\n\n/**\n * Whether `namespace` starts with `prefix`.\n *\n * Segments are compared literally first; if the prefix segment itself contains\n * no `:`, the candidate segment is also compared after its dynamic suffix is\n * stripped (see {@link normalizeNamespaceSegment}). This mirrors\n * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side\n * filtering and client-side per-subscription narrowing stay consistent.\n *\n * @param namespace - Event namespace to test.\n * @param prefix - Subscription namespace prefix.\n */\nexport function isPrefixMatch(\n namespace: Namespace,\n prefix: Namespace\n): boolean {\n if (prefix.length > namespace.length) return false;\n for (let i = 0; i < prefix.length; i += 1) {\n const segment = prefix[i]!;\n const candidate = namespace[i]!;\n if (candidate === segment) continue;\n if (segment.includes(\":\")) return false;\n if (normalizeNamespaceSegment(candidate) === segment) continue;\n return false;\n }\n return true;\n}\n\nfunction namespaceMatches(\n eventNamespace: Namespace,\n prefixes: Namespace[] | undefined,\n depth: number | undefined\n): boolean {\n if (!prefixes || prefixes.length === 0) {\n return true;\n }\n\n return prefixes.some((prefix) => {\n if (!isPrefixMatch(eventNamespace, prefix)) return false;\n if (depth === undefined) return true;\n return eventNamespace.length - prefix.length <= depth;\n });\n}\n\n/**\n * The base protocol subscription channels, excluding the templated\n * `custom:<name>` form. This is the runtime counterpart to the `Channel`\n * union from `@langchain/protocol` and mirrors the channel set a server\n * recognizes when filtering its event sinks.\n */\nexport const SUPPORTED_CHANNELS = new Set<Channel>([\n \"values\",\n \"updates\",\n \"messages\",\n \"tools\",\n \"lifecycle\",\n \"input\",\n \"checkpoints\",\n \"tasks\",\n \"custom\",\n]);\n\n/**\n * Whether `value` names a protocol subscription channel — either a base\n * channel (`\"messages\"`, `\"values\"`, …) or a named custom channel\n * (`\"custom:<name>\"`). Unknown/future method names return `false`,\n * mirroring {@link inferChannel}.\n *\n * @param value - Candidate channel name.\n */\nexport function isSupportedChannel(value: string): value is Channel {\n return (\n SUPPORTED_CHANNELS.has(value as Channel) || value.startsWith(\"custom:\")\n );\n}\n\n/**\n * Maps a protocol event method to its subscription {@link Channel}.\n *\n * Returns `undefined` for unrecognized methods so that new server-side\n * channels (e.g. from extension transformers) don't break existing\n * subscribers. The `custom` method resolves to the named `custom:<name>`\n * channel when the payload carries a `name`, otherwise the bare `custom`\n * channel. Both `\"input\"` and the wire-level `\"input.requested\"` map to the\n * `input` channel.\n *\n * @param event - Event whose method should be mapped to a channel.\n */\nexport function inferChannel(event: MatchableEvent): Channel | undefined {\n switch (event.method) {\n case \"values\":\n return \"values\";\n case \"checkpoints\":\n return \"checkpoints\";\n case \"updates\":\n return \"updates\";\n case \"messages\":\n return \"messages\";\n case \"tools\":\n return \"tools\";\n case \"custom\": {\n const data = event.params.data as { name?: string } | undefined;\n return data?.name != null ? `custom:${data.name}` : \"custom\";\n }\n case \"lifecycle\":\n return \"lifecycle\";\n case \"input\":\n case \"input.requested\":\n return \"input\";\n case \"tasks\":\n return \"tasks\";\n default:\n return undefined;\n }\n}\n\n/**\n * Returns whether an event should be delivered for a subscription definition.\n *\n * When the definition carries a `since` replay cursor, events at or before\n * that sequence number are excluded — letting the same predicate drive both\n * live fan-out and buffered replay over a {@link StreamChannel}.\n *\n * @param event - Event being checked for delivery.\n * @param definition - Subscription filter definition to evaluate against.\n * The optional `since` field (a `seq` cursor) is read leniently because it\n * is not a declared field on the base {@link SubscribeParams} shape.\n */\nexport function matchesSubscription(\n event: MatchableEvent,\n definition: SubscribeParams\n): boolean {\n const since = (definition as { since?: unknown }).since;\n if (typeof since === \"number\" && (event.seq ?? 0) <= since) {\n return false;\n }\n\n const channel = inferChannel(event);\n if (channel === undefined) return false;\n\n const channels = definition.channels;\n const channelMatched =\n channels.includes(channel) ||\n (channel.startsWith(\"custom:\") && channels.includes(\"custom\"));\n if (!channelMatched) {\n return false;\n }\n\n return namespaceMatches(\n event.params.namespace,\n definition.namespaces,\n definition.depth\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,0BAA0B,SAAyB;CACjE,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAChC,QAAO,QAAQ,KAAK,UAAU,QAAQ,MAAM,GAAG,IAAI;;;;;;;;;;;;;;AAerD,SAAgB,cACd,WACA,QACS;AACT,KAAI,OAAO,SAAS,UAAU,OAAQ,QAAO;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY,UAAU;AAC5B,MAAI,cAAc,QAAS;AAC3B,MAAI,QAAQ,SAAS,IAAI,CAAE,QAAO;AAClC,MAAI,0BAA0B,UAAU,KAAK,QAAS;AACtD,SAAO;;AAET,QAAO;;AAGT,SAAS,iBACP,gBACA,UACA,OACS;AACT,KAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,QAAO,SAAS,MAAM,WAAW;AAC/B,MAAI,CAAC,cAAc,gBAAgB,OAAO,CAAE,QAAO;AACnD,MAAI,UAAU,KAAA,EAAW,QAAO;AAChC,SAAO,eAAe,SAAS,OAAO,UAAU;GAChD;;;;;;;;AASJ,MAAa,qBAAqB,IAAI,IAAa;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;AAUF,SAAgB,mBAAmB,OAAiC;AAClE,QACE,mBAAmB,IAAI,MAAiB,IAAI,MAAM,WAAW,UAAU;;;;;;;;;;;;;;AAgB3E,SAAgB,aAAa,OAA4C;AACvE,SAAQ,MAAM,QAAd;EACE,KAAK,SACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UAAU;GACb,MAAM,OAAO,MAAM,OAAO;AAC1B,UAAO,MAAM,QAAQ,OAAO,UAAU,KAAK,SAAS;;EAEtD,KAAK,YACH,QAAO;EACT,KAAK;EACL,KAAK,kBACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE;;;;;;;;;;;;;;;AAgBN,SAAgB,oBACd,OACA,YACS;CACT,MAAM,QAAS,WAAmC;AAClD,KAAI,OAAO,UAAU,aAAa,MAAM,OAAO,MAAM,MACnD,QAAO;CAGT,MAAM,UAAU,aAAa,MAAM;AACnC,KAAI,YAAY,KAAA,EAAW,QAAO;CAElC,MAAM,WAAW,WAAW;AAI5B,KAAI,EAFF,SAAS,SAAS,QAAQ,IACzB,QAAQ,WAAW,UAAU,IAAI,SAAS,SAAS,SAAS,EAE7D,QAAO;AAGT,QAAO,iBACL,MAAM,OAAO,WACb,WAAW,YACX,WAAW,MACZ"}
@@ -0,0 +1,94 @@
1
+ import { Namespace } from "./types.cjs";
2
+ import { Channel, SubscribeParams } from "@langchain/protocol";
3
+
4
+ //#region src/stream/subscription.d.ts
5
+ /**
6
+ * Minimal protocol-event shape consumed by {@link inferChannel} and
7
+ * {@link matchesSubscription}.
8
+ *
9
+ * Both the core {@link ProtocolEvent} and the wire-level `Event` from
10
+ * `@langchain/protocol` structurally satisfy this contract, so the same
11
+ * predicates can drive in-process fan-out, buffered replay, and server-side
12
+ * (SSE / WebSocket) event-sink filtering without coupling to a single event
13
+ * type.
14
+ */
15
+ interface MatchableEvent {
16
+ /** Logical stream channel; see {@link inferChannel}. */
17
+ readonly method: string;
18
+ /** Monotonic sequence number, when present. Used by the `since` cursor. */
19
+ readonly seq?: number;
20
+ readonly params: {
21
+ /** Namespace of the node or scope that emitted this event. */readonly namespace: Namespace; /** Opaque channel payload; shape depends on `method`. */
22
+ readonly data?: unknown;
23
+ };
24
+ }
25
+ /**
26
+ * Strip dynamic suffixes (after `:`) from a namespace segment.
27
+ *
28
+ * Server-emitted namespaces contain runtime-generated suffixes like
29
+ * `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
30
+ * static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
31
+ * `api/langgraph_api/protocol/namespace.py`.
32
+ *
33
+ * @param segment - Raw namespace segment.
34
+ * @returns The stable graph-oriented portion of the segment.
35
+ */
36
+ declare function normalizeNamespaceSegment(segment: string): string;
37
+ /**
38
+ * Whether `namespace` starts with `prefix`.
39
+ *
40
+ * Segments are compared literally first; if the prefix segment itself contains
41
+ * no `:`, the candidate segment is also compared after its dynamic suffix is
42
+ * stripped (see {@link normalizeNamespaceSegment}). This mirrors
43
+ * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
44
+ * filtering and client-side per-subscription narrowing stay consistent.
45
+ *
46
+ * @param namespace - Event namespace to test.
47
+ * @param prefix - Subscription namespace prefix.
48
+ */
49
+ declare function isPrefixMatch(namespace: Namespace, prefix: Namespace): boolean;
50
+ /**
51
+ * The base protocol subscription channels, excluding the templated
52
+ * `custom:<name>` form. This is the runtime counterpart to the `Channel`
53
+ * union from `@langchain/protocol` and mirrors the channel set a server
54
+ * recognizes when filtering its event sinks.
55
+ */
56
+ declare const SUPPORTED_CHANNELS: Set<Channel>;
57
+ /**
58
+ * Whether `value` names a protocol subscription channel — either a base
59
+ * channel (`"messages"`, `"values"`, …) or a named custom channel
60
+ * (`"custom:<name>"`). Unknown/future method names return `false`,
61
+ * mirroring {@link inferChannel}.
62
+ *
63
+ * @param value - Candidate channel name.
64
+ */
65
+ declare function isSupportedChannel(value: string): value is Channel;
66
+ /**
67
+ * Maps a protocol event method to its subscription {@link Channel}.
68
+ *
69
+ * Returns `undefined` for unrecognized methods so that new server-side
70
+ * channels (e.g. from extension transformers) don't break existing
71
+ * subscribers. The `custom` method resolves to the named `custom:<name>`
72
+ * channel when the payload carries a `name`, otherwise the bare `custom`
73
+ * channel. Both `"input"` and the wire-level `"input.requested"` map to the
74
+ * `input` channel.
75
+ *
76
+ * @param event - Event whose method should be mapped to a channel.
77
+ */
78
+ declare function inferChannel(event: MatchableEvent): Channel | undefined;
79
+ /**
80
+ * Returns whether an event should be delivered for a subscription definition.
81
+ *
82
+ * When the definition carries a `since` replay cursor, events at or before
83
+ * that sequence number are excluded — letting the same predicate drive both
84
+ * live fan-out and buffered replay over a {@link StreamChannel}.
85
+ *
86
+ * @param event - Event being checked for delivery.
87
+ * @param definition - Subscription filter definition to evaluate against.
88
+ * The optional `since` field (a `seq` cursor) is read leniently because it
89
+ * is not a declared field on the base {@link SubscribeParams} shape.
90
+ */
91
+ declare function matchesSubscription(event: MatchableEvent, definition: SubscribeParams): boolean;
92
+ //#endregion
93
+ export { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
94
+ //# sourceMappingURL=subscription.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.d.cts","names":[],"sources":["../../src/stream/subscription.ts"],"mappings":";;;;;AAsDA;;;;;AAiBA;;;;UA5CiB,cAAA;EA6Cf;EAAA,SA3CS,MAAA;EA4CT;EAAA,SAzCS,GAAA;EAAA,SAEA,MAAA;IA2EE,uEAzEA,SAAA,EAAW,SAAA,EAyEO;IAAA,SAtElB,IAAA;EAAA;AAAA;;;;;;;;;AA4Gb;;;iBA7FgB,yBAAA,CAA0B,OAAA;;;;;;AAqI1C;;;;;;;iBApHgB,aAAA,CACd,SAAA,EAAW,SAAA,EACX,MAAA,EAAQ,SAAA;;;;;;;cAoCG,kBAAA,EAAkB,GAAA,CAAA,OAAA;;;;;;;;;iBAoBf,kBAAA,CAAmB,KAAA,WAAgB,KAAA,IAAS,OAAA;;;;;;;;;;;;;iBAkB5C,YAAA,CAAa,KAAA,EAAO,cAAA,GAAiB,OAAA;;;;;;;;;;;;;iBAwCrC,mBAAA,CACd,KAAA,EAAO,cAAA,EACP,UAAA,EAAY,eAAA"}
@@ -0,0 +1,94 @@
1
+ import { Namespace } from "./types.js";
2
+ import { Channel, SubscribeParams } from "@langchain/protocol";
3
+
4
+ //#region src/stream/subscription.d.ts
5
+ /**
6
+ * Minimal protocol-event shape consumed by {@link inferChannel} and
7
+ * {@link matchesSubscription}.
8
+ *
9
+ * Both the core {@link ProtocolEvent} and the wire-level `Event` from
10
+ * `@langchain/protocol` structurally satisfy this contract, so the same
11
+ * predicates can drive in-process fan-out, buffered replay, and server-side
12
+ * (SSE / WebSocket) event-sink filtering without coupling to a single event
13
+ * type.
14
+ */
15
+ interface MatchableEvent {
16
+ /** Logical stream channel; see {@link inferChannel}. */
17
+ readonly method: string;
18
+ /** Monotonic sequence number, when present. Used by the `since` cursor. */
19
+ readonly seq?: number;
20
+ readonly params: {
21
+ /** Namespace of the node or scope that emitted this event. */readonly namespace: Namespace; /** Opaque channel payload; shape depends on `method`. */
22
+ readonly data?: unknown;
23
+ };
24
+ }
25
+ /**
26
+ * Strip dynamic suffixes (after `:`) from a namespace segment.
27
+ *
28
+ * Server-emitted namespaces contain runtime-generated suffixes like
29
+ * `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
30
+ * static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
31
+ * `api/langgraph_api/protocol/namespace.py`.
32
+ *
33
+ * @param segment - Raw namespace segment.
34
+ * @returns The stable graph-oriented portion of the segment.
35
+ */
36
+ declare function normalizeNamespaceSegment(segment: string): string;
37
+ /**
38
+ * Whether `namespace` starts with `prefix`.
39
+ *
40
+ * Segments are compared literally first; if the prefix segment itself contains
41
+ * no `:`, the candidate segment is also compared after its dynamic suffix is
42
+ * stripped (see {@link normalizeNamespaceSegment}). This mirrors
43
+ * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
44
+ * filtering and client-side per-subscription narrowing stay consistent.
45
+ *
46
+ * @param namespace - Event namespace to test.
47
+ * @param prefix - Subscription namespace prefix.
48
+ */
49
+ declare function isPrefixMatch(namespace: Namespace, prefix: Namespace): boolean;
50
+ /**
51
+ * The base protocol subscription channels, excluding the templated
52
+ * `custom:<name>` form. This is the runtime counterpart to the `Channel`
53
+ * union from `@langchain/protocol` and mirrors the channel set a server
54
+ * recognizes when filtering its event sinks.
55
+ */
56
+ declare const SUPPORTED_CHANNELS: Set<Channel>;
57
+ /**
58
+ * Whether `value` names a protocol subscription channel — either a base
59
+ * channel (`"messages"`, `"values"`, …) or a named custom channel
60
+ * (`"custom:<name>"`). Unknown/future method names return `false`,
61
+ * mirroring {@link inferChannel}.
62
+ *
63
+ * @param value - Candidate channel name.
64
+ */
65
+ declare function isSupportedChannel(value: string): value is Channel;
66
+ /**
67
+ * Maps a protocol event method to its subscription {@link Channel}.
68
+ *
69
+ * Returns `undefined` for unrecognized methods so that new server-side
70
+ * channels (e.g. from extension transformers) don't break existing
71
+ * subscribers. The `custom` method resolves to the named `custom:<name>`
72
+ * channel when the payload carries a `name`, otherwise the bare `custom`
73
+ * channel. Both `"input"` and the wire-level `"input.requested"` map to the
74
+ * `input` channel.
75
+ *
76
+ * @param event - Event whose method should be mapped to a channel.
77
+ */
78
+ declare function inferChannel(event: MatchableEvent): Channel | undefined;
79
+ /**
80
+ * Returns whether an event should be delivered for a subscription definition.
81
+ *
82
+ * When the definition carries a `since` replay cursor, events at or before
83
+ * that sequence number are excluded — letting the same predicate drive both
84
+ * live fan-out and buffered replay over a {@link StreamChannel}.
85
+ *
86
+ * @param event - Event being checked for delivery.
87
+ * @param definition - Subscription filter definition to evaluate against.
88
+ * The optional `since` field (a `seq` cursor) is read leniently because it
89
+ * is not a declared field on the base {@link SubscribeParams} shape.
90
+ */
91
+ declare function matchesSubscription(event: MatchableEvent, definition: SubscribeParams): boolean;
92
+ //#endregion
93
+ export { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
94
+ //# sourceMappingURL=subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.d.ts","names":[],"sources":["../../src/stream/subscription.ts"],"mappings":";;;;;AAsDA;;;;;AAiBA;;;;UA5CiB,cAAA;EA6Cf;EAAA,SA3CS,MAAA;EA4CT;EAAA,SAzCS,GAAA;EAAA,SAEA,MAAA;IA2EE,uEAzEA,SAAA,EAAW,SAAA,EAyEO;IAAA,SAtElB,IAAA;EAAA;AAAA;;;;;;;;;AA4Gb;;;iBA7FgB,yBAAA,CAA0B,OAAA;;;;;;AAqI1C;;;;;;;iBApHgB,aAAA,CACd,SAAA,EAAW,SAAA,EACX,MAAA,EAAQ,SAAA;;;;;;;cAoCG,kBAAA,EAAkB,GAAA,CAAA,OAAA;;;;;;;;;iBAoBf,kBAAA,CAAmB,KAAA,WAAgB,KAAA,IAAS,OAAA;;;;;;;;;;;;;iBAkB5C,YAAA,CAAa,KAAA,EAAO,cAAA,GAAiB,OAAA;;;;;;;;;;;;;iBAwCrC,mBAAA,CACd,KAAA,EAAO,cAAA,EACP,UAAA,EAAY,eAAA"}
@@ -0,0 +1,131 @@
1
+ //#region src/stream/subscription.ts
2
+ /**
3
+ * Strip dynamic suffixes (after `:`) from a namespace segment.
4
+ *
5
+ * Server-emitted namespaces contain runtime-generated suffixes like
6
+ * `"fetcher:abc-uuid"`, while user-supplied subscription filters are typically
7
+ * static names (`"fetcher"`). Mirrors `normalize_namespace_segment` in
8
+ * `api/langgraph_api/protocol/namespace.py`.
9
+ *
10
+ * @param segment - Raw namespace segment.
11
+ * @returns The stable graph-oriented portion of the segment.
12
+ */
13
+ function normalizeNamespaceSegment(segment) {
14
+ const idx = segment.indexOf(":");
15
+ return idx === -1 ? segment : segment.slice(0, idx);
16
+ }
17
+ /**
18
+ * Whether `namespace` starts with `prefix`.
19
+ *
20
+ * Segments are compared literally first; if the prefix segment itself contains
21
+ * no `:`, the candidate segment is also compared after its dynamic suffix is
22
+ * stripped (see {@link normalizeNamespaceSegment}). This mirrors
23
+ * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side
24
+ * filtering and client-side per-subscription narrowing stay consistent.
25
+ *
26
+ * @param namespace - Event namespace to test.
27
+ * @param prefix - Subscription namespace prefix.
28
+ */
29
+ function isPrefixMatch(namespace, prefix) {
30
+ if (prefix.length > namespace.length) return false;
31
+ for (let i = 0; i < prefix.length; i += 1) {
32
+ const segment = prefix[i];
33
+ const candidate = namespace[i];
34
+ if (candidate === segment) continue;
35
+ if (segment.includes(":")) return false;
36
+ if (normalizeNamespaceSegment(candidate) === segment) continue;
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ function namespaceMatches(eventNamespace, prefixes, depth) {
42
+ if (!prefixes || prefixes.length === 0) return true;
43
+ return prefixes.some((prefix) => {
44
+ if (!isPrefixMatch(eventNamespace, prefix)) return false;
45
+ if (depth === void 0) return true;
46
+ return eventNamespace.length - prefix.length <= depth;
47
+ });
48
+ }
49
+ /**
50
+ * The base protocol subscription channels, excluding the templated
51
+ * `custom:<name>` form. This is the runtime counterpart to the `Channel`
52
+ * union from `@langchain/protocol` and mirrors the channel set a server
53
+ * recognizes when filtering its event sinks.
54
+ */
55
+ const SUPPORTED_CHANNELS = new Set([
56
+ "values",
57
+ "updates",
58
+ "messages",
59
+ "tools",
60
+ "lifecycle",
61
+ "input",
62
+ "checkpoints",
63
+ "tasks",
64
+ "custom"
65
+ ]);
66
+ /**
67
+ * Whether `value` names a protocol subscription channel — either a base
68
+ * channel (`"messages"`, `"values"`, …) or a named custom channel
69
+ * (`"custom:<name>"`). Unknown/future method names return `false`,
70
+ * mirroring {@link inferChannel}.
71
+ *
72
+ * @param value - Candidate channel name.
73
+ */
74
+ function isSupportedChannel(value) {
75
+ return SUPPORTED_CHANNELS.has(value) || value.startsWith("custom:");
76
+ }
77
+ /**
78
+ * Maps a protocol event method to its subscription {@link Channel}.
79
+ *
80
+ * Returns `undefined` for unrecognized methods so that new server-side
81
+ * channels (e.g. from extension transformers) don't break existing
82
+ * subscribers. The `custom` method resolves to the named `custom:<name>`
83
+ * channel when the payload carries a `name`, otherwise the bare `custom`
84
+ * channel. Both `"input"` and the wire-level `"input.requested"` map to the
85
+ * `input` channel.
86
+ *
87
+ * @param event - Event whose method should be mapped to a channel.
88
+ */
89
+ function inferChannel(event) {
90
+ switch (event.method) {
91
+ case "values": return "values";
92
+ case "checkpoints": return "checkpoints";
93
+ case "updates": return "updates";
94
+ case "messages": return "messages";
95
+ case "tools": return "tools";
96
+ case "custom": {
97
+ const data = event.params.data;
98
+ return data?.name != null ? `custom:${data.name}` : "custom";
99
+ }
100
+ case "lifecycle": return "lifecycle";
101
+ case "input":
102
+ case "input.requested": return "input";
103
+ case "tasks": return "tasks";
104
+ default: return;
105
+ }
106
+ }
107
+ /**
108
+ * Returns whether an event should be delivered for a subscription definition.
109
+ *
110
+ * When the definition carries a `since` replay cursor, events at or before
111
+ * that sequence number are excluded — letting the same predicate drive both
112
+ * live fan-out and buffered replay over a {@link StreamChannel}.
113
+ *
114
+ * @param event - Event being checked for delivery.
115
+ * @param definition - Subscription filter definition to evaluate against.
116
+ * The optional `since` field (a `seq` cursor) is read leniently because it
117
+ * is not a declared field on the base {@link SubscribeParams} shape.
118
+ */
119
+ function matchesSubscription(event, definition) {
120
+ const since = definition.since;
121
+ if (typeof since === "number" && (event.seq ?? 0) <= since) return false;
122
+ const channel = inferChannel(event);
123
+ if (channel === void 0) return false;
124
+ const channels = definition.channels;
125
+ if (!(channels.includes(channel) || channel.startsWith("custom:") && channels.includes("custom"))) return false;
126
+ return namespaceMatches(event.params.namespace, definition.namespaces, definition.depth);
127
+ }
128
+ //#endregion
129
+ export { SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
130
+
131
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","names":[],"sources":["../../src/stream/subscription.ts"],"sourcesContent":["/**\n * Subscription matching and channel inference for the v2 streaming protocol.\n *\n * These helpers are the building blocks a custom transport / server needs to\n * fan protocol events out to subscribers: map an event to its logical\n * {@link Channel} ({@link inferChannel}) and decide whether a buffered event\n * should be delivered for a given {@link SubscribeParams} filter\n * ({@link matchesSubscription}). They are typed against the minimal\n * {@link MatchableEvent} shape so the same predicate works on the core\n * {@link ProtocolEvent} produced by {@link convertToProtocolEvent} /\n * {@link StreamChannel} and on the wire-level `Event` from\n * `@langchain/protocol`.\n */\n\nimport type { Channel, SubscribeParams } from \"@langchain/protocol\";\nimport type { Namespace } from \"./types.js\";\n\n/**\n * Minimal protocol-event shape consumed by {@link inferChannel} and\n * {@link matchesSubscription}.\n *\n * Both the core {@link ProtocolEvent} and the wire-level `Event` from\n * `@langchain/protocol` structurally satisfy this contract, so the same\n * predicates can drive in-process fan-out, buffered replay, and server-side\n * (SSE / WebSocket) event-sink filtering without coupling to a single event\n * type.\n */\nexport interface MatchableEvent {\n /** Logical stream channel; see {@link inferChannel}. */\n readonly method: string;\n\n /** Monotonic sequence number, when present. Used by the `since` cursor. */\n readonly seq?: number;\n\n readonly params: {\n /** Namespace of the node or scope that emitted this event. */\n readonly namespace: Namespace;\n\n /** Opaque channel payload; shape depends on `method`. */\n readonly data?: unknown;\n };\n}\n\n/**\n * Strip dynamic suffixes (after `:`) from a namespace segment.\n *\n * Server-emitted namespaces contain runtime-generated suffixes like\n * `\"fetcher:abc-uuid\"`, while user-supplied subscription filters are typically\n * static names (`\"fetcher\"`). Mirrors `normalize_namespace_segment` in\n * `api/langgraph_api/protocol/namespace.py`.\n *\n * @param segment - Raw namespace segment.\n * @returns The stable graph-oriented portion of the segment.\n */\nexport function normalizeNamespaceSegment(segment: string): string {\n const idx = segment.indexOf(\":\");\n return idx === -1 ? segment : segment.slice(0, idx);\n}\n\n/**\n * Whether `namespace` starts with `prefix`.\n *\n * Segments are compared literally first; if the prefix segment itself contains\n * no `:`, the candidate segment is also compared after its dynamic suffix is\n * stripped (see {@link normalizeNamespaceSegment}). This mirrors\n * `is_prefix_match` in `api/langgraph_api/protocol/namespace.py` so server-side\n * filtering and client-side per-subscription narrowing stay consistent.\n *\n * @param namespace - Event namespace to test.\n * @param prefix - Subscription namespace prefix.\n */\nexport function isPrefixMatch(\n namespace: Namespace,\n prefix: Namespace\n): boolean {\n if (prefix.length > namespace.length) return false;\n for (let i = 0; i < prefix.length; i += 1) {\n const segment = prefix[i]!;\n const candidate = namespace[i]!;\n if (candidate === segment) continue;\n if (segment.includes(\":\")) return false;\n if (normalizeNamespaceSegment(candidate) === segment) continue;\n return false;\n }\n return true;\n}\n\nfunction namespaceMatches(\n eventNamespace: Namespace,\n prefixes: Namespace[] | undefined,\n depth: number | undefined\n): boolean {\n if (!prefixes || prefixes.length === 0) {\n return true;\n }\n\n return prefixes.some((prefix) => {\n if (!isPrefixMatch(eventNamespace, prefix)) return false;\n if (depth === undefined) return true;\n return eventNamespace.length - prefix.length <= depth;\n });\n}\n\n/**\n * The base protocol subscription channels, excluding the templated\n * `custom:<name>` form. This is the runtime counterpart to the `Channel`\n * union from `@langchain/protocol` and mirrors the channel set a server\n * recognizes when filtering its event sinks.\n */\nexport const SUPPORTED_CHANNELS = new Set<Channel>([\n \"values\",\n \"updates\",\n \"messages\",\n \"tools\",\n \"lifecycle\",\n \"input\",\n \"checkpoints\",\n \"tasks\",\n \"custom\",\n]);\n\n/**\n * Whether `value` names a protocol subscription channel — either a base\n * channel (`\"messages\"`, `\"values\"`, …) or a named custom channel\n * (`\"custom:<name>\"`). Unknown/future method names return `false`,\n * mirroring {@link inferChannel}.\n *\n * @param value - Candidate channel name.\n */\nexport function isSupportedChannel(value: string): value is Channel {\n return (\n SUPPORTED_CHANNELS.has(value as Channel) || value.startsWith(\"custom:\")\n );\n}\n\n/**\n * Maps a protocol event method to its subscription {@link Channel}.\n *\n * Returns `undefined` for unrecognized methods so that new server-side\n * channels (e.g. from extension transformers) don't break existing\n * subscribers. The `custom` method resolves to the named `custom:<name>`\n * channel when the payload carries a `name`, otherwise the bare `custom`\n * channel. Both `\"input\"` and the wire-level `\"input.requested\"` map to the\n * `input` channel.\n *\n * @param event - Event whose method should be mapped to a channel.\n */\nexport function inferChannel(event: MatchableEvent): Channel | undefined {\n switch (event.method) {\n case \"values\":\n return \"values\";\n case \"checkpoints\":\n return \"checkpoints\";\n case \"updates\":\n return \"updates\";\n case \"messages\":\n return \"messages\";\n case \"tools\":\n return \"tools\";\n case \"custom\": {\n const data = event.params.data as { name?: string } | undefined;\n return data?.name != null ? `custom:${data.name}` : \"custom\";\n }\n case \"lifecycle\":\n return \"lifecycle\";\n case \"input\":\n case \"input.requested\":\n return \"input\";\n case \"tasks\":\n return \"tasks\";\n default:\n return undefined;\n }\n}\n\n/**\n * Returns whether an event should be delivered for a subscription definition.\n *\n * When the definition carries a `since` replay cursor, events at or before\n * that sequence number are excluded — letting the same predicate drive both\n * live fan-out and buffered replay over a {@link StreamChannel}.\n *\n * @param event - Event being checked for delivery.\n * @param definition - Subscription filter definition to evaluate against.\n * The optional `since` field (a `seq` cursor) is read leniently because it\n * is not a declared field on the base {@link SubscribeParams} shape.\n */\nexport function matchesSubscription(\n event: MatchableEvent,\n definition: SubscribeParams\n): boolean {\n const since = (definition as { since?: unknown }).since;\n if (typeof since === \"number\" && (event.seq ?? 0) <= since) {\n return false;\n }\n\n const channel = inferChannel(event);\n if (channel === undefined) return false;\n\n const channels = definition.channels;\n const channelMatched =\n channels.includes(channel) ||\n (channel.startsWith(\"custom:\") && channels.includes(\"custom\"));\n if (!channelMatched) {\n return false;\n }\n\n return namespaceMatches(\n event.params.namespace,\n definition.namespaces,\n definition.depth\n );\n}\n"],"mappings":";;;;;;;;;;;;AAsDA,SAAgB,0BAA0B,SAAyB;CACjE,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAChC,QAAO,QAAQ,KAAK,UAAU,QAAQ,MAAM,GAAG,IAAI;;;;;;;;;;;;;;AAerD,SAAgB,cACd,WACA,QACS;AACT,KAAI,OAAO,SAAS,UAAU,OAAQ,QAAO;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;EACzC,MAAM,UAAU,OAAO;EACvB,MAAM,YAAY,UAAU;AAC5B,MAAI,cAAc,QAAS;AAC3B,MAAI,QAAQ,SAAS,IAAI,CAAE,QAAO;AAClC,MAAI,0BAA0B,UAAU,KAAK,QAAS;AACtD,SAAO;;AAET,QAAO;;AAGT,SAAS,iBACP,gBACA,UACA,OACS;AACT,KAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,QAAO,SAAS,MAAM,WAAW;AAC/B,MAAI,CAAC,cAAc,gBAAgB,OAAO,CAAE,QAAO;AACnD,MAAI,UAAU,KAAA,EAAW,QAAO;AAChC,SAAO,eAAe,SAAS,OAAO,UAAU;GAChD;;;;;;;;AASJ,MAAa,qBAAqB,IAAI,IAAa;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;AAUF,SAAgB,mBAAmB,OAAiC;AAClE,QACE,mBAAmB,IAAI,MAAiB,IAAI,MAAM,WAAW,UAAU;;;;;;;;;;;;;;AAgB3E,SAAgB,aAAa,OAA4C;AACvE,SAAQ,MAAM,QAAd;EACE,KAAK,SACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UAAU;GACb,MAAM,OAAO,MAAM,OAAO;AAC1B,UAAO,MAAM,QAAQ,OAAO,UAAU,KAAK,SAAS;;EAEtD,KAAK,YACH,QAAO;EACT,KAAK;EACL,KAAK,kBACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE;;;;;;;;;;;;;;;AAgBN,SAAgB,oBACd,OACA,YACS;CACT,MAAM,QAAS,WAAmC;AAClD,KAAI,OAAO,UAAU,aAAa,MAAM,OAAO,MAAM,MACnD,QAAO;CAGT,MAAM,UAAU,aAAa,MAAM;AACnC,KAAI,YAAY,KAAA,EAAW,QAAO;CAElC,MAAM,WAAW,WAAW;AAI5B,KAAI,EAFF,SAAS,SAAS,QAAQ,IACzB,QAAQ,WAAW,UAAU,IAAI,SAAS,SAAS,SAAS,EAE7D,QAAO;AAGT,QAAO,iBACL,MAAM,OAAO,WACb,WAAW,YACX,WAAW,MACZ"}
@@ -251,5 +251,5 @@ interface InterruptPayload<TPayload = unknown> {
251
251
  payload: TPayload;
252
252
  }
253
253
  //#endregion
254
- export { type AgentStatus$1 as AgentStatus, ChatModelStream$1 as ChatModelStream, ChatModelStreamHandle, InferExtensions, InterruptPayload, type LifecycleCause, type LifecycleData$1 as LifecycleData, type MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, type ToolsEventData, type UpdatesEventData, type UsageInfo, isNativeTransformer };
254
+ export { type AgentStatus$1 as AgentStatus, ChatModelStream$1 as ChatModelStream, ChatModelStreamHandle, InferExtensions, InterruptPayload, type LifecycleCause, type LifecycleData$1 as LifecycleData, type MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, ProtocolMethod, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, type ToolsEventData, type UpdatesEventData, type UsageInfo, isNativeTransformer };
255
255
  //# sourceMappingURL=types.d.cts.map
@@ -251,5 +251,5 @@ interface InterruptPayload<TPayload = unknown> {
251
251
  payload: TPayload;
252
252
  }
253
253
  //#endregion
254
- export { type AgentStatus$1 as AgentStatus, ChatModelStream$1 as ChatModelStream, ChatModelStreamHandle, InferExtensions, InterruptPayload, type LifecycleCause, type LifecycleData$1 as LifecycleData, type MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, type ToolsEventData, type UpdatesEventData, type UsageInfo, isNativeTransformer };
254
+ export { type AgentStatus$1 as AgentStatus, ChatModelStream$1 as ChatModelStream, ChatModelStreamHandle, InferExtensions, InterruptPayload, type LifecycleCause, type LifecycleData$1 as LifecycleData, type MessagesEventData, Namespace, NativeStreamTransformer, ProtocolEvent, ProtocolMethod, StreamEmitter, StreamTransformer, ToolCallStatus, ToolCallStream, type ToolsEventData, type UpdatesEventData, type UsageInfo, isNativeTransformer };
255
255
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,16 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_convert = require("./stream/convert.cjs");
3
+ const require_stream_channel = require("./stream/stream-channel.cjs");
4
+ const require_subscription = require("./stream/subscription.cjs");
5
+ exports.EventLog = require_stream_channel.StreamChannel;
6
+ exports.STREAM_EVENTS_V3_MODES = require_convert.STREAM_EVENTS_V3_MODES;
7
+ exports.SUPPORTED_CHANNELS = require_subscription.SUPPORTED_CHANNELS;
8
+ exports.StreamChannel = require_stream_channel.StreamChannel;
9
+ exports.convertToProtocolEvent = require_convert.convertToProtocolEvent;
10
+ exports.inferChannel = require_subscription.inferChannel;
11
+ exports.isCheckpointEnvelope = require_convert.isCheckpointEnvelope;
12
+ exports.isPrefixMatch = require_subscription.isPrefixMatch;
13
+ exports.isStreamChannel = require_stream_channel.isStreamChannel;
14
+ exports.isSupportedChannel = require_subscription.isSupportedChannel;
15
+ exports.matchesSubscription = require_subscription.matchesSubscription;
16
+ exports.normalizeNamespaceSegment = require_subscription.normalizeNamespaceSegment;
@@ -0,0 +1,5 @@
1
+ import { Namespace, ProtocolEvent, ProtocolMethod } from "./stream/types.cjs";
2
+ import { StreamChannel, isStreamChannel } from "./stream/stream-channel.cjs";
3
+ import { ConvertToProtocolEventOptions, STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./stream/convert.cjs";
4
+ import { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment } from "./stream/subscription.cjs";
5
+ export { type ConvertToProtocolEventOptions, StreamChannel as EventLog, type MatchableEvent, type Namespace, type ProtocolEvent, type ProtocolMethod, STREAM_EVENTS_V3_MODES, SUPPORTED_CHANNELS, StreamChannel, convertToProtocolEvent, inferChannel, isCheckpointEnvelope, isPrefixMatch, isStreamChannel, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
@@ -0,0 +1,5 @@
1
+ import { Namespace, ProtocolEvent, ProtocolMethod } from "./stream/types.js";
2
+ import { StreamChannel, isStreamChannel } from "./stream/stream-channel.js";
3
+ import { ConvertToProtocolEventOptions, STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./stream/convert.js";
4
+ import { MatchableEvent, SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment } from "./stream/subscription.js";
5
+ export { type ConvertToProtocolEventOptions, StreamChannel as EventLog, type MatchableEvent, type Namespace, type ProtocolEvent, type ProtocolMethod, STREAM_EVENTS_V3_MODES, SUPPORTED_CHANNELS, StreamChannel, convertToProtocolEvent, inferChannel, isCheckpointEnvelope, isPrefixMatch, isStreamChannel, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
package/dist/stream.js ADDED
@@ -0,0 +1,4 @@
1
+ import { STREAM_EVENTS_V3_MODES, convertToProtocolEvent, isCheckpointEnvelope } from "./stream/convert.js";
2
+ import { StreamChannel, isStreamChannel } from "./stream/stream-channel.js";
3
+ import { SUPPORTED_CHANNELS, inferChannel, isPrefixMatch, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment } from "./stream/subscription.js";
4
+ export { StreamChannel as EventLog, STREAM_EVENTS_V3_MODES, SUPPORTED_CHANNELS, StreamChannel, convertToProtocolEvent, inferChannel, isCheckpointEnvelope, isPrefixMatch, isStreamChannel, isSupportedChannel, matchesSubscription, normalizeNamespaceSegment };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph",
3
- "version": "1.3.6",
3
+ "version": "1.3.7",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {
@@ -20,7 +20,7 @@
20
20
  "@standard-schema/spec": "1.1.0",
21
21
  "uuid": "^14.0.0",
22
22
  "@langchain/langgraph-checkpoint": "^1.0.4",
23
- "@langchain/langgraph-sdk": "~1.9.17"
23
+ "@langchain/langgraph-sdk": "~1.9.19"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@langchain/core": "^1.1.48",
@@ -56,10 +56,10 @@
56
56
  "tsx": "^4.19.3",
57
57
  "typescript": "^4.9.5 || ^5.4.5",
58
58
  "vite-plugin-node-polyfills": "^0.28.0",
59
- "vitest": "^4.1.8",
59
+ "vitest": "^4.1.0",
60
60
  "zod-to-json-schema": "^3.22.4",
61
61
  "@langchain/langgraph-checkpoint-postgres": "1.0.2",
62
- "@langchain/langgraph-checkpoint-sqlite": "1.0.1"
62
+ "@langchain/langgraph-checkpoint-sqlite": "1.0.2"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public",
@@ -139,6 +139,18 @@
139
139
  "default": "./dist/remote.cjs"
140
140
  }
141
141
  },
142
+ "./stream": {
143
+ "input": "./src/stream.ts",
144
+ "typedoc": "./src/stream.ts",
145
+ "import": {
146
+ "types": "./dist/stream.d.ts",
147
+ "default": "./dist/stream.js"
148
+ },
149
+ "require": {
150
+ "types": "./dist/stream.d.cts",
151
+ "default": "./dist/stream.cjs"
152
+ }
153
+ },
142
154
  "./zod": {
143
155
  "input": "./src/graph/zod/index.ts",
144
156
  "typedoc": "./src/graph/zod/index.ts",