@langchain/langgraph 1.4.21 → 1.5.1

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 (87) hide show
  1. package/dist/channels/base.cjs +123 -11
  2. package/dist/channels/base.cjs.map +1 -1
  3. package/dist/channels/base.d.cts +26 -4
  4. package/dist/channels/base.d.cts.map +1 -1
  5. package/dist/channels/base.d.ts +26 -4
  6. package/dist/channels/base.d.ts.map +1 -1
  7. package/dist/channels/base.js +120 -13
  8. package/dist/channels/base.js.map +1 -1
  9. package/dist/channels/binop.cjs +2 -1
  10. package/dist/channels/binop.cjs.map +1 -1
  11. package/dist/channels/binop.d.cts.map +1 -1
  12. package/dist/channels/binop.d.ts.map +1 -1
  13. package/dist/channels/binop.js +2 -1
  14. package/dist/channels/binop.js.map +1 -1
  15. package/dist/channels/index.cjs +2 -0
  16. package/dist/channels/index.d.cts +2 -2
  17. package/dist/channels/index.d.ts +2 -2
  18. package/dist/channels/index.js +2 -2
  19. package/dist/constants.cjs +14 -0
  20. package/dist/constants.cjs.map +1 -1
  21. package/dist/constants.d.cts.map +1 -1
  22. package/dist/constants.d.ts.map +1 -1
  23. package/dist/constants.js +13 -1
  24. package/dist/constants.js.map +1 -1
  25. package/dist/errors.cjs +22 -0
  26. package/dist/errors.cjs.map +1 -1
  27. package/dist/errors.d.cts +10 -1
  28. package/dist/errors.d.cts.map +1 -1
  29. package/dist/errors.d.ts +10 -1
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +21 -1
  32. package/dist/errors.js.map +1 -1
  33. package/dist/graph/index.cjs +1 -1
  34. package/dist/graph/index.js +1 -1
  35. package/dist/graph/messages_reducer.cjs +30 -6
  36. package/dist/graph/messages_reducer.cjs.map +1 -1
  37. package/dist/graph/messages_reducer.d.cts +4 -2
  38. package/dist/graph/messages_reducer.d.cts.map +1 -1
  39. package/dist/graph/messages_reducer.d.ts +4 -2
  40. package/dist/graph/messages_reducer.d.ts.map +1 -1
  41. package/dist/graph/messages_reducer.js +31 -8
  42. package/dist/graph/messages_reducer.js.map +1 -1
  43. package/dist/index.cjs +3 -1
  44. package/dist/index.d.cts +2 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +3 -3
  47. package/dist/interrupt.cjs +9 -1
  48. package/dist/interrupt.cjs.map +1 -1
  49. package/dist/interrupt.d.cts.map +1 -1
  50. package/dist/interrupt.d.ts.map +1 -1
  51. package/dist/interrupt.js +10 -2
  52. package/dist/interrupt.js.map +1 -1
  53. package/dist/pregel/algo.cjs +46 -45
  54. package/dist/pregel/algo.cjs.map +1 -1
  55. package/dist/pregel/algo.d.cts.map +1 -1
  56. package/dist/pregel/algo.d.ts.map +1 -1
  57. package/dist/pregel/algo.js +47 -47
  58. package/dist/pregel/algo.js.map +1 -1
  59. package/dist/pregel/checkpoint.cjs +70 -0
  60. package/dist/pregel/checkpoint.cjs.map +1 -0
  61. package/dist/pregel/checkpoint.js +67 -0
  62. package/dist/pregel/checkpoint.js.map +1 -0
  63. package/dist/pregel/debug.cjs +1 -0
  64. package/dist/pregel/debug.cjs.map +1 -1
  65. package/dist/pregel/debug.js +1 -0
  66. package/dist/pregel/debug.js.map +1 -1
  67. package/dist/pregel/index.cjs +87 -34
  68. package/dist/pregel/index.cjs.map +1 -1
  69. package/dist/pregel/index.d.cts.map +1 -1
  70. package/dist/pregel/index.d.ts.map +1 -1
  71. package/dist/pregel/index.js +91 -38
  72. package/dist/pregel/index.js.map +1 -1
  73. package/dist/pregel/loop.cjs +336 -113
  74. package/dist/pregel/loop.cjs.map +1 -1
  75. package/dist/pregel/loop.js +341 -119
  76. package/dist/pregel/loop.js.map +1 -1
  77. package/dist/pregel/runner.cjs +1 -1
  78. package/dist/pregel/runner.cjs.map +1 -1
  79. package/dist/pregel/runner.js +2 -2
  80. package/dist/pregel/runner.js.map +1 -1
  81. package/dist/state/prebuilt/messages.cjs +1 -1
  82. package/dist/state/prebuilt/messages.js +1 -1
  83. package/dist/web.cjs +3 -1
  84. package/dist/web.d.cts +2 -2
  85. package/dist/web.d.ts +2 -2
  86. package/dist/web.js +3 -3
  87. package/package.json +5 -5
@@ -1,4 +1,4 @@
1
- import { getDeltaMaxSuperstepsSinceSnapshot } from "../constants.js";
1
+ import { EXIT_DELTA_NULL_TASK_ID, SNAPSHOT_BUMPS, getDeltaMaxSuperstepsSinceSnapshot } from "../constants.js";
2
2
  import { EmptyChannelError } from "../errors.js";
3
3
  import { DeltaSnapshot, uuid6 } from "@langchain/langgraph-checkpoint";
4
4
  //#region src/channels/base.ts
@@ -11,6 +11,9 @@ const STRUCTURED_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-
11
11
  function isDeltaChannel(channel) {
12
12
  return channel != null && channel.lc_graph_name === "DeltaChannel";
13
13
  }
14
+ function deltaChannelWrites(channels, writes) {
15
+ return writes.filter(([ch]) => isDeltaChannel(channels[String(ch)]));
16
+ }
14
17
  function isBaseChannel(obj) {
15
18
  return obj != null && obj.lg_is_channel === true;
16
19
  }
@@ -93,12 +96,46 @@ function emptyChannels(channels, checkpoint) {
93
96
  *
94
97
  * Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`
95
98
  * preserves chronological order while remaining a valid RFC UUID (required by
96
- * Postgres `checkpoint_writes.task_id uuid` columns).
99
+ * Postgres `checkpoint_writes.task_id uuid` columns). Never `NULL_TASK_ID`:
100
+ * readers apply writes under it as the anchor checkpoint's own pending writes.
97
101
  */
98
102
  function exitDeltaTaskId(step, taskId) {
99
103
  if (!STRUCTURED_UUID.test(taskId)) throw new TypeError(`Invalid task id for exit delta: ${taskId}`);
100
104
  const parts = taskId.toLowerCase().split("-");
101
- return `${String(step).padStart(8, "0")}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
105
+ const synthetic = `${String(step).padStart(8, "0")}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
106
+ return synthetic === "00000000-0000-0000-0000-000000000000" ? EXIT_DELTA_NULL_TASK_ID : synthetic;
107
+ }
108
+ /**
109
+ * Synthetic task id for exit-mode writes of a superstep after the anchor's
110
+ * own.
111
+ *
112
+ * Sorts after every real task id, in step order, so replay keeps them after
113
+ * the anchor's own superstep whether a saver orders writes by task path
114
+ * (`writesSortKey`) or by task id. Mirrors Python's
115
+ * `exit_delta_late_task_id` exactly, including the two hexadecimal step
116
+ * groups (step is split across them).
117
+ */
118
+ function exitDeltaLateTaskId(step, taskId) {
119
+ if (!STRUCTURED_UUID.test(taskId)) throw new TypeError(`Invalid task id for exit delta: ${taskId}`);
120
+ const parts = taskId.toLowerCase().split("-");
121
+ return `ffffffff-${(step >> 16).toString(16).padStart(4, "0")}-${(step & 65535).toString(16).padStart(4, "0")}-${parts[3]}-${parts[4]}`;
122
+ }
123
+ /**
124
+ * DeltaChannels a branch starting from this checkpoint must snapshot.
125
+ *
126
+ * A checkpoint's pending writes belong to the child that consumed them, and
127
+ * nothing records which child that was. A new branch snapshots every delta
128
+ * channel they touch, so its ancestor walk never replays them (port of
129
+ * Python #8548's `delta_channels_with_pending_writes`).
130
+ */
131
+ function deltaChannelsWithPendingWrites(channels, pendingWrites) {
132
+ const result = /* @__PURE__ */ new Set();
133
+ if (pendingWrites === void 0) return result;
134
+ for (const [, ch] of pendingWrites) {
135
+ const channel = channels[ch];
136
+ if (channel != null && isDeltaChannel(channel)) result.add(ch);
137
+ }
138
+ return result;
102
139
  }
103
140
  /**
104
141
  * Return the set of {@link DeltaChannel} names that should snapshot now.
@@ -113,19 +150,23 @@ function exitDeltaTaskId(step, taskId) {
113
150
  */
114
151
  function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot, channelVersions) {
115
152
  const result = /* @__PURE__ */ new Set();
116
- const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();
117
153
  for (const name in channels) {
118
154
  if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;
119
155
  const ch = channels[name];
120
156
  if (!isDeltaChannel(ch) || !ch.isAvailable() || channelVersions !== void 0 && channelVersions[name] === void 0) continue;
121
- const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];
122
- if (updates >= ch.snapshotFrequency || supersteps >= maxSupersteps) result.add(name);
157
+ if (reachedSnapshotBound(ch, countersSinceDeltaSnapshot[name])) result.add(name);
123
158
  }
124
159
  return result;
125
160
  }
161
+ /** Whether a delta channel's counters reached the bound where it snapshots. */
162
+ function reachedSnapshotBound(channel, counters) {
163
+ const [updates, supersteps] = counters ?? [0, 0];
164
+ return updates >= channel.snapshotFrequency || supersteps >= getDeltaMaxSuperstepsSinceSnapshot();
165
+ }
126
166
  function createCheckpoint(checkpoint, channels, step, options) {
127
167
  const channelsToSnapshot = options?.channelsToSnapshot ?? /* @__PURE__ */ new Set();
128
- const { updatedChannels, getNextVersion } = options ?? {};
168
+ const { storedVersions, getNextVersion } = options ?? {};
169
+ const bumped = {};
129
170
  let values;
130
171
  let channelVersions = checkpoint.channel_versions;
131
172
  if (channels === void 0) values = checkpoint.channel_values;
@@ -136,7 +177,11 @@ function createCheckpoint(checkpoint, channels, step, options) {
136
177
  if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;
137
178
  const channel = channels[k];
138
179
  if (channelsToSnapshot.has(k)) {
139
- if (getNextVersion !== void 0 && (updatedChannels === void 0 || !updatedChannels.has(k))) channelVersions[k] = getNextVersion(channelVersions[k]);
180
+ if (getNextVersion !== void 0 && storedVersions !== void 0 && channelVersions[k] === storedVersions[k]) {
181
+ const old = channelVersions[k];
182
+ channelVersions[k] = getNextVersion(old);
183
+ bumped[k] = [old, channelVersions[k]];
184
+ }
140
185
  values[k] = new DeltaSnapshot(channel.get());
141
186
  continue;
142
187
  }
@@ -154,10 +199,65 @@ function createCheckpoint(checkpoint, channels, step, options) {
154
199
  ts: (/* @__PURE__ */ new Date()).toISOString(),
155
200
  channel_values: values,
156
201
  channel_versions: channelVersions,
157
- versions_seen: checkpoint.versions_seen
202
+ versions_seen: markBumpsSeen(checkpoint.versions_seen, bumped, options?.triggerToNodes)
158
203
  };
159
204
  }
160
205
  /**
206
+ * Advance whoever had seen a bumped channel's old version to the new one.
207
+ *
208
+ * A bump that only stores a snapshot is not a write. Left unseen, it would
209
+ * re-fire `interruptBefore` and rerun the channel's subscribers. A channel
210
+ * bumped from no version was never written, so it also goes to the
211
+ * subscribers that never ran: they have no entry, and would start on the bump.
212
+ * For each entry it advances, {@link SNAPSHOT_BUMPS} keeps the new version and
213
+ * the one the node really read, so {@link versionsSeenWithoutBumps} can put
214
+ * the read back.
215
+ */
216
+ function markBumpsSeen(versionsSeen, bumped, triggerToNodes = {}) {
217
+ if (Object.keys(bumped).length === 0) return versionsSeen;
218
+ const out = { ...versionsSeen };
219
+ for (const [k, [old]] of Object.entries(bumped)) {
220
+ if (old !== void 0) continue;
221
+ for (const node of triggerToNodes[k] ?? []) out[node] ??= {};
222
+ }
223
+ const marks = { ...versionsSeen[SNAPSHOT_BUMPS] };
224
+ for (const node in out) {
225
+ if (!Object.prototype.hasOwnProperty.call(out, node) || node === "__snapshot_bumps__") continue;
226
+ for (const [k, [old, next]] of Object.entries(bumped)) {
227
+ if (out[node][k] !== old) continue;
228
+ const [advanced, read] = bumpKeys(node, k);
229
+ if (old !== void 0 && marks[advanced] !== old) marks[read] = old;
230
+ marks[advanced] = next;
231
+ out[node] = {
232
+ ...out[node],
233
+ [k]: next
234
+ };
235
+ }
236
+ }
237
+ if (Object.keys(marks).length > 0) out[SNAPSHOT_BUMPS] = marks;
238
+ return out;
239
+ }
240
+ const bumpKeys = (node, channel) => [`${node}|${channel}`, `${node}:${channel}`];
241
+ /**
242
+ * `versions_seen` as the nodes read it: an entry a snapshot bump advanced goes
243
+ * back to the version the node really read, or away if it never read one.
244
+ */
245
+ function versionsSeenWithoutBumps(versionsSeen) {
246
+ const marks = versionsSeen[SNAPSHOT_BUMPS];
247
+ if (marks === void 0 || Object.keys(marks).length === 0) return versionsSeen;
248
+ const out = {};
249
+ for (const node in versionsSeen) {
250
+ if (!Object.prototype.hasOwnProperty.call(versionsSeen, node) || node === "__snapshot_bumps__") continue;
251
+ out[node] = {};
252
+ for (const [k, v] of Object.entries(versionsSeen[node])) {
253
+ const [advanced, read] = bumpKeys(node, k);
254
+ if (marks[advanced] !== v) out[node][k] = v;
255
+ else if (read in marks) out[node][k] = marks[read];
256
+ }
257
+ }
258
+ return out;
259
+ }
260
+ /**
161
261
  * Checkpoint metadata key, `true` when every DeltaChannel write on the
162
262
  * checkpoint's branch carries a version. A delta channel with no version there
163
263
  * was never written, so it needs no walk. Branches started before this key
@@ -169,19 +269,25 @@ function isDeltaWritesVersioned(metadata) {
169
269
  return metadata?.[DELTA_WRITES_VERSIONED] === true;
170
270
  }
171
271
  /**
172
- * Delta channels whose value at `checkpoint` the ancestor walk rebuilds.
272
+ * Channels whose value at `checkpoint` the ancestor walk rebuilds.
173
273
  *
174
274
  * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`
175
275
  * resolves directly, so only a channel with nothing stored there needs the
176
276
  * walk. With `versionedOnly`, a channel with no version was never written, so
177
277
  * it is empty without one; a walk for it would find no snapshot to stop at and
178
278
  * read every ancestor, every time the thread is loaded.
279
+ *
280
+ * A `BinaryOperatorAggregate` stores its value at every version, so one with a
281
+ * version and nothing stored was written as a DeltaChannel before its
282
+ * annotation changed, and is rebuilt the same way.
179
283
  */
180
284
  function deltaChannelsToReplay(specs, checkpoint, versionedOnly) {
181
285
  const keys = [];
182
286
  for (const k in specs) {
183
287
  if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;
184
- if (isDeltaChannel(specs[k]) && (!versionedOnly || checkpoint.channel_versions[k] !== void 0) && !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) keys.push(k);
288
+ if (Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) continue;
289
+ const versioned = checkpoint.channel_versions[k] !== void 0;
290
+ if (isDeltaChannel(specs[k]) && (versioned || !versionedOnly) || specs[k].lc_graph_name === "BinaryOperatorAggregate" && versioned) keys.push(k);
185
291
  }
186
292
  return keys;
187
293
  }
@@ -215,12 +321,13 @@ async function channelsFromCheckpoint(specs, checkpoint, options) {
215
321
  const history = histories[k];
216
322
  if (history === void 0) continue;
217
323
  const replayCh = filteredSpecs[k].fromCheckpoint(history.seed);
218
- replayCh.replayWrites(history.writes);
324
+ if (isDeltaChannel(replayCh)) replayCh.replayWrites(history.writes);
325
+ else for (const [, , value] of history.writes) replayCh.update([value]);
219
326
  channels[k] = replayCh;
220
327
  }
221
328
  return channels;
222
329
  }
223
330
  //#endregion
224
- export { BaseChannel, DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel, isDeltaWritesVersioned };
331
+ export { BaseChannel, DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelWrites, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel, isDeltaWritesVersioned, reachedSnapshotBound, versionsSeenWithoutBumps };
225
332
 
226
333
  //# sourceMappingURL=base.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n ReadonlyCheckpoint,\n uuid6,\n Checkpoint,\n DeltaSnapshot,\n type BaseCheckpointSaver,\n type CheckpointMetadata,\n type DeltaChannelHistory,\n} from \"@langchain/langgraph-checkpoint\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport { getDeltaMaxSuperstepsSinceSnapshot } from \"../constants.js\";\n\n/** Matches Postgres `uuid` / Python `uuid.UUID` (128-bit, 8-4-4-4-12 hex). */\nconst STRUCTURED_UUID =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Structural check for a {@link DeltaChannel} without importing it (avoids an\n * import cycle: `delta.ts` imports `base.ts`).\n */\nexport function isDeltaChannel(channel: BaseChannel): boolean {\n return channel != null && channel.lc_graph_name === \"DeltaChannel\";\n}\n\nexport function isBaseChannel(obj: unknown): obj is BaseChannel {\n return obj != null && (obj as BaseChannel).lg_is_channel === true;\n}\n\n/** @internal */\nexport abstract class BaseChannel<\n ValueType = unknown,\n UpdateType = unknown,\n CheckpointType = unknown,\n> {\n ValueType: ValueType;\n\n UpdateType: UpdateType;\n\n /**\n * The name of the channel.\n */\n abstract lc_graph_name: string;\n\n /** @ignore */\n lg_is_channel = true;\n\n /**\n * Return a new identical channel, optionally initialized from a checkpoint.\n * Can be thought of as a \"restoration\" from a checkpoint which is a \"snapshot\" of the channel's state.\n *\n * @param {CheckpointType | undefined} checkpoint\n * @param {CheckpointType | undefined} initialValue\n * @returns {this}\n */\n abstract fromCheckpoint(checkpoint?: CheckpointType): this;\n\n /**\n * Update the channel's value with the given sequence of updates.\n * The order of the updates in the sequence is arbitrary.\n * This method is called by Pregel for all channels at the end of each step.\n * If there are no updates, it is called with an empty sequence.\n *\n * Raises InvalidUpdateError if the sequence of updates is invalid.\n * Returns True if the channel was updated, False otherwise.\n *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n abstract update(values: UpdateType[]): boolean;\n\n /**\n * Return the current value of the channel.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet).\n * @returns {ValueType}\n */\n abstract get(): ValueType;\n\n /**\n * Return a string representation of the channel's current state.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet), or doesn't support checkpoints.\n * @returns {CheckpointType | undefined}\n */\n abstract checkpoint(): CheckpointType | undefined;\n\n /**\n * Mark the current value of the channel as consumed. By default, no-op.\n * A channel can use this method to modify its state, preventing the value\n * from being consumed again.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n consume(): boolean {\n return false;\n }\n\n /**\n * Notify the channel that the Pregel run is finishing. By default, no-op.\n * A channel can use this method to modify its state, preventing finish.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n finish(): boolean {\n return false;\n }\n\n /**\n * Return True if the channel is available (not empty), False otherwise.\n * Subclasses should override this method to provide a more efficient\n * implementation than calling get() and catching EmptyChannelError.\n */\n isAvailable(): boolean {\n try {\n this.get();\n return true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Used to determine if two channels with the same key are semantically equivalent.\n * Subclasses should override this method to provide a meaningful comparison.\n *\n * @param {BaseChannel} other - The other channel to compare with.\n * @returns {boolean} True if the channels are equal, false otherwise.\n */\n equals(other: BaseChannel): boolean {\n return this === other;\n }\n}\n\nconst IS_ONLY_BASE_CHANNEL = Symbol.for(\"LG_IS_ONLY_BASE_CHANNEL\");\nexport function getOnlyChannels(\n channels: Record<string, BaseChannel>\n): Record<string, BaseChannel> {\n // @ts-expect-error - we know it's a record of base channels\n if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;\n\n const newChannels = {} as Record<string, BaseChannel>;\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const value = channels[k];\n if (isBaseChannel(value)) newChannels[k] = value;\n }\n\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\nexport function emptyChannels<Cc extends Record<string, BaseChannel>>(\n channels: Cc,\n checkpoint: ReadonlyCheckpoint\n): Cc {\n const filteredChannels = getOnlyChannels(channels) as Cc;\n\n const newChannels = {} as Cc;\n for (const k in filteredChannels) {\n if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;\n const channelValue = checkpoint.channel_values[k];\n newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);\n }\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\n/**\n * Minimal structural view of a {@link DeltaChannel}, used by helpers in this\n * module that must not import the concrete class (import-cycle avoidance).\n */\ninterface DeltaChannelLike extends BaseChannel {\n snapshotFrequency: number;\n replayWrites(writes: DeltaChannelHistory[\"writes\"]): void;\n}\n\n/**\n * Synthetic task id for exit-mode DeltaChannel writes.\n *\n * Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`\n * preserves chronological order while remaining a valid RFC UUID (required by\n * Postgres `checkpoint_writes.task_id uuid` columns).\n */\nexport function exitDeltaTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepPart = String(step).padStart(8, \"0\");\n return `${stepPart}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;\n}\n\n/**\n * Return the set of {@link DeltaChannel} names that should snapshot now.\n *\n * A channel snapshots when EITHER its accumulated update count reaches\n * `snapshotFrequency` OR the total supersteps since its last snapshot reaches\n * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel\n * without a version is skipped: it was never written on this branch, so it\n * has nothing to snapshot. Pass `undefined` on a branch without\n * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes\n * and this snapshot is what bounds its walk. Pure predicate, no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>,\n channelVersions: Record<string, number | string> | undefined\n): Set<string> {\n const result = new Set<string>();\n const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();\n for (const name in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;\n const ch = channels[name];\n if (\n !isDeltaChannel(ch) ||\n !ch.isAvailable() ||\n (channelVersions !== undefined && channelVersions[name] === undefined)\n ) {\n continue;\n }\n const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];\n if (\n updates >= (ch as DeltaChannelLike).snapshotFrequency ||\n supersteps >= maxSupersteps\n ) {\n result.add(name);\n }\n }\n return result;\n}\n\nexport function createCheckpoint<ValueType>(\n checkpoint: ReadonlyCheckpoint,\n channels: Record<string, BaseChannel<ValueType>> | undefined,\n step: number,\n options?: {\n id?: string;\n channelsToSnapshot?: Set<string>;\n updatedChannels?: Set<string>;\n getNextVersion?: (current: number | string | undefined) => number | string;\n }\n): Checkpoint {\n const channelsToSnapshot = options?.channelsToSnapshot ?? new Set<string>();\n const { updatedChannels, getNextVersion } = options ?? {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let values: Record<string, any>;\n let channelVersions: Record<string, number | string> =\n checkpoint.channel_versions;\n if (channels === undefined) {\n values = checkpoint.channel_values;\n } else {\n values = {};\n channelVersions = { ...checkpoint.channel_versions };\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const channel = channels[k];\n if (channelsToSnapshot.has(k)) {\n // Snapshot a DeltaChannel: store the materialized value directly. In\n // exit/deferred modes the channel may have reached its snapshot\n // threshold over several supersteps without the LAST superstep\n // writing to it, so its version wouldn't be bumped by applyWrites —\n // bump it here so the saver includes the snapshot blob.\n if (\n getNextVersion !== undefined &&\n (updatedChannels === undefined || !updatedChannels.has(k))\n ) {\n channelVersions[k] = getNextVersion(channelVersions[k]);\n }\n values[k] = new DeltaSnapshot(channel.get());\n continue;\n }\n if (isDeltaChannel(channel)) {\n // Omitted from channel_values; reconstructed from ancestor writes.\n continue;\n }\n try {\n values[k] = channel.checkpoint();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n // no-op\n } else {\n throw error; // Rethrow unexpected errors\n }\n }\n }\n }\n\n return {\n v: 4,\n id: options?.id ?? uuid6(step),\n ts: new Date().toISOString(),\n channel_values: values,\n channel_versions: channelVersions,\n versions_seen: checkpoint.versions_seen,\n };\n}\n\n/**\n * Checkpoint metadata key, `true` when every DeltaChannel write on the\n * checkpoint's branch carries a version. A delta channel with no version there\n * was never written, so it needs no walk. Branches started before this key\n * existed never get it: an older `updateState` stored a delta channel's first\n * write without a version, and only the walk finds such a write.\n */\nexport const DELTA_WRITES_VERSIONED = \"delta_writes_versioned\";\n\nexport function isDeltaWritesVersioned(\n metadata: CheckpointMetadata | undefined\n): boolean {\n return (\n (metadata as Record<string, unknown> | undefined)?.[\n DELTA_WRITES_VERSIONED\n ] === true\n );\n}\n\n/**\n * Delta channels whose value at `checkpoint` the ancestor walk rebuilds.\n *\n * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`\n * resolves directly, so only a channel with nothing stored there needs the\n * walk. With `versionedOnly`, a channel with no version was never written, so\n * it is empty without one; a walk for it would find no snapshot to stop at and\n * read every ancestor, every time the thread is loaded.\n */\nfunction deltaChannelsToReplay(\n specs: Record<string, BaseChannel>,\n checkpoint: ReadonlyCheckpoint,\n versionedOnly: boolean\n): string[] {\n const keys: string[] = [];\n for (const k in specs) {\n if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;\n if (\n isDeltaChannel(specs[k]) &&\n (!versionedOnly || checkpoint.channel_versions[k] !== undefined) &&\n !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)\n ) {\n keys.push(k);\n }\n }\n return keys;\n}\n\n/**\n * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}\n * whose value is absent from `channel_values` by replaying ancestor writes.\n *\n * For most channels (and for delta channels with a {@link DeltaSnapshot} or a\n * migrated plain value in `channel_values`), {@link emptyChannels} is\n * sufficient and no saver access is required. When a delta channel is absent\n * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`\n * finds the nearest seed and accumulates the writes between it and the\n * target. All delta channels needing replay are batched into a single saver\n * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries\n * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are\n * left empty instead of walked.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: {\n saver?: BaseCheckpointSaver;\n config?: RunnableConfig;\n deltaWritesVersioned?: boolean;\n }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys = deltaChannelsToReplay(\n filteredSpecs,\n checkpoint,\n options?.deltaWritesVersioned ?? false\n );\n\n const written = deltaKeys.filter(\n (k) => checkpoint.channel_versions[k] !== undefined\n );\n if ((saver === undefined || config === undefined) && written.length > 0) {\n throw new Error(\n `DeltaChannel ${written.join(\", \")} has history to replay but no checkpointer or config was passed to read it`\n );\n }\n if (deltaKeys.length === 0 || saver === undefined || config === undefined) {\n return channels;\n }\n\n const histories = await saver.getDeltaChannelHistory({\n config,\n channels: deltaKeys,\n });\n for (const k of deltaKeys) {\n const history = histories[k];\n if (history === undefined) continue;\n const replayCh = filteredSpecs[k].fromCheckpoint(\n history.seed\n ) as unknown as DeltaChannelLike;\n replayCh.replayWrites(history.writes);\n (channels as Record<string, BaseChannel>)[k] = replayCh;\n }\n return channels;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":[],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n ReadonlyCheckpoint,\n uuid6,\n Checkpoint,\n DeltaSnapshot,\n type BaseCheckpointSaver,\n type CheckpointMetadata,\n type DeltaChannelHistory,\n} from \"@langchain/langgraph-checkpoint\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport {\n CHECKPOINT_NAMESPACE_END,\n CHECKPOINT_NAMESPACE_SEPARATOR,\n EXIT_DELTA_NULL_TASK_ID,\n getDeltaMaxSuperstepsSinceSnapshot,\n NULL_TASK_ID,\n SNAPSHOT_BUMPS,\n} from \"../constants.js\";\n\n/** Matches Postgres `uuid` / Python `uuid.UUID` (128-bit, 8-4-4-4-12 hex). */\nconst STRUCTURED_UUID =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Structural check for a {@link DeltaChannel} without importing it (avoids an\n * import cycle: `delta.ts` imports `base.ts`).\n */\nexport function isDeltaChannel(channel: BaseChannel): boolean {\n return channel != null && channel.lc_graph_name === \"DeltaChannel\";\n}\n\nexport function deltaChannelWrites<W extends readonly [PropertyKey, unknown]>(\n channels: Record<string, BaseChannel>,\n writes: ReadonlyArray<W>\n): W[] {\n return writes.filter(([ch]) => isDeltaChannel(channels[String(ch)]));\n}\n\nexport function isBaseChannel(obj: unknown): obj is BaseChannel {\n return obj != null && (obj as BaseChannel).lg_is_channel === true;\n}\n\n/** @internal */\nexport abstract class BaseChannel<\n ValueType = unknown,\n UpdateType = unknown,\n CheckpointType = unknown,\n> {\n ValueType: ValueType;\n\n UpdateType: UpdateType;\n\n /**\n * The name of the channel.\n */\n abstract lc_graph_name: string;\n\n /** @ignore */\n lg_is_channel = true;\n\n /**\n * Return a new identical channel, optionally initialized from a checkpoint.\n * Can be thought of as a \"restoration\" from a checkpoint which is a \"snapshot\" of the channel's state.\n *\n * @param {CheckpointType | undefined} checkpoint\n * @param {CheckpointType | undefined} initialValue\n * @returns {this}\n */\n abstract fromCheckpoint(checkpoint?: CheckpointType): this;\n\n /**\n * Update the channel's value with the given sequence of updates.\n * The order of the updates in the sequence is arbitrary.\n * This method is called by Pregel for all channels at the end of each step.\n * If there are no updates, it is called with an empty sequence.\n *\n * Raises InvalidUpdateError if the sequence of updates is invalid.\n * Returns True if the channel was updated, False otherwise.\n *\n * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n * @param {Array<UpdateType>} values\n * @returns {void}\n */\n abstract update(values: UpdateType[]): boolean;\n\n /**\n * Return the current value of the channel.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet).\n * @returns {ValueType}\n */\n abstract get(): ValueType;\n\n /**\n * Return a string representation of the channel's current state.\n *\n * @throws {EmptyChannelError} if the channel is empty (never updated yet), or doesn't support checkpoints.\n * @returns {CheckpointType | undefined}\n */\n abstract checkpoint(): CheckpointType | undefined;\n\n /**\n * Mark the current value of the channel as consumed. By default, no-op.\n * A channel can use this method to modify its state, preventing the value\n * from being consumed again.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n consume(): boolean {\n return false;\n }\n\n /**\n * Notify the channel that the Pregel run is finishing. By default, no-op.\n * A channel can use this method to modify its state, preventing finish.\n *\n * Returns True if the channel was updated, False otherwise.\n */\n finish(): boolean {\n return false;\n }\n\n /**\n * Return True if the channel is available (not empty), False otherwise.\n * Subclasses should override this method to provide a more efficient\n * implementation than calling get() and catching EmptyChannelError.\n */\n isAvailable(): boolean {\n try {\n this.get();\n return true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Used to determine if two channels with the same key are semantically equivalent.\n * Subclasses should override this method to provide a meaningful comparison.\n *\n * @param {BaseChannel} other - The other channel to compare with.\n * @returns {boolean} True if the channels are equal, false otherwise.\n */\n equals(other: BaseChannel): boolean {\n return this === other;\n }\n}\n\nconst IS_ONLY_BASE_CHANNEL = Symbol.for(\"LG_IS_ONLY_BASE_CHANNEL\");\nexport function getOnlyChannels(\n channels: Record<string, BaseChannel>\n): Record<string, BaseChannel> {\n // @ts-expect-error - we know it's a record of base channels\n if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;\n\n const newChannels = {} as Record<string, BaseChannel>;\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const value = channels[k];\n if (isBaseChannel(value)) newChannels[k] = value;\n }\n\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\nexport function emptyChannels<Cc extends Record<string, BaseChannel>>(\n channels: Cc,\n checkpoint: ReadonlyCheckpoint\n): Cc {\n const filteredChannels = getOnlyChannels(channels) as Cc;\n\n const newChannels = {} as Cc;\n for (const k in filteredChannels) {\n if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;\n const channelValue = checkpoint.channel_values[k];\n newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);\n }\n Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n return newChannels;\n}\n\n/**\n * Minimal structural view of a {@link DeltaChannel}, used by helpers in this\n * module that must not import the concrete class (import-cycle avoidance).\n */\ninterface DeltaChannelLike extends BaseChannel {\n snapshotFrequency: number;\n replayWrites(writes: DeltaChannelHistory[\"writes\"]): void;\n}\n\n/**\n * Synthetic task id for exit-mode DeltaChannel writes.\n *\n * Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`\n * preserves chronological order while remaining a valid RFC UUID (required by\n * Postgres `checkpoint_writes.task_id uuid` columns). Never `NULL_TASK_ID`:\n * readers apply writes under it as the anchor checkpoint's own pending writes.\n */\nexport function exitDeltaTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepPart = String(step).padStart(8, \"0\");\n const synthetic = `${stepPart}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;\n return synthetic === NULL_TASK_ID ? EXIT_DELTA_NULL_TASK_ID : synthetic;\n}\n\n/**\n * Synthetic task id for exit-mode writes of a superstep after the anchor's\n * own.\n *\n * Sorts after every real task id, in step order, so replay keeps them after\n * the anchor's own superstep whether a saver orders writes by task path\n * (`writesSortKey`) or by task id. Mirrors Python's\n * `exit_delta_late_task_id` exactly, including the two hexadecimal step\n * groups (step is split across them).\n */\nexport function exitDeltaLateTaskId(step: number, taskId: string): string {\n if (!STRUCTURED_UUID.test(taskId)) {\n throw new TypeError(`Invalid task id for exit delta: ${taskId}`);\n }\n const parts = taskId.toLowerCase().split(\"-\");\n const stepHi = (step >> 16).toString(16).padStart(4, \"0\");\n const stepLo = (step & 0xffff).toString(16).padStart(4, \"0\");\n return `ffffffff-${stepHi}-${stepLo}-${parts[3]}-${parts[4]}`;\n}\n\n/**\n * DeltaChannels a branch starting from this checkpoint must snapshot.\n *\n * A checkpoint's pending writes belong to the child that consumed them, and\n * nothing records which child that was. A new branch snapshots every delta\n * channel they touch, so its ancestor walk never replays them (port of\n * Python #8548's `delta_channels_with_pending_writes`).\n */\nexport function deltaChannelsWithPendingWrites(\n channels: Record<string, BaseChannel>,\n pendingWrites: ReadonlyArray<[string, string, unknown]> | undefined\n): Set<string> {\n const result = new Set<string>();\n if (pendingWrites === undefined) return result;\n for (const [, ch] of pendingWrites) {\n const channel = channels[ch];\n if (channel != null && isDeltaChannel(channel)) result.add(ch);\n }\n return result;\n}\n\n/**\n * Return the set of {@link DeltaChannel} names that should snapshot now.\n *\n * A channel snapshots when EITHER its accumulated update count reaches\n * `snapshotFrequency` OR the total supersteps since its last snapshot reaches\n * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel\n * without a version is skipped: it was never written on this branch, so it\n * has nothing to snapshot. Pass `undefined` on a branch without\n * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes\n * and this snapshot is what bounds its walk. Pure predicate, no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>,\n channelVersions: Record<string, number | string> | undefined\n): Set<string> {\n const result = new Set<string>();\n for (const name in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;\n const ch = channels[name];\n if (\n !isDeltaChannel(ch) ||\n !ch.isAvailable() ||\n (channelVersions !== undefined && channelVersions[name] === undefined)\n ) {\n continue;\n }\n if (reachedSnapshotBound(ch, countersSinceDeltaSnapshot[name])) {\n result.add(name);\n }\n }\n return result;\n}\n\n/** Whether a delta channel's counters reached the bound where it snapshots. */\nexport function reachedSnapshotBound(\n channel: BaseChannel,\n counters: [number, number] | undefined\n): boolean {\n const [updates, supersteps] = counters ?? [0, 0];\n return (\n updates >= (channel as DeltaChannelLike).snapshotFrequency ||\n supersteps >= getDeltaMaxSuperstepsSinceSnapshot()\n );\n}\n\nexport function createCheckpoint<ValueType>(\n checkpoint: ReadonlyCheckpoint,\n channels: Record<string, BaseChannel<ValueType>> | undefined,\n step: number,\n options?: {\n id?: string;\n channelsToSnapshot?: Set<string>;\n /** The channel versions of the last checkpoint the saver stored. */\n storedVersions?: Record<string, number | string>;\n getNextVersion?: (current: number | string | undefined) => number | string;\n triggerToNodes?: Record<string, string[]>;\n }\n): Checkpoint {\n const channelsToSnapshot = options?.channelsToSnapshot ?? new Set<string>();\n const { storedVersions, getNextVersion } = options ?? {};\n const bumped: Record<string, [number | string | undefined, number | string]> =\n {};\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let values: Record<string, any>;\n let channelVersions: Record<string, number | string> =\n checkpoint.channel_versions;\n if (channels === undefined) {\n values = checkpoint.channel_values;\n } else {\n values = {};\n channelVersions = { ...checkpoint.channel_versions };\n for (const k in channels) {\n if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n const channel = channels[k];\n if (channelsToSnapshot.has(k)) {\n // `put` only stores a blob for a channel whose version moved, so\n // snapshotting a channel that didn't move since the last stored\n // checkpoint needs a bump: exit mode reaching the cadence on a\n // superstep that skipped the channel, and a fork's first checkpoint.\n if (\n getNextVersion !== undefined &&\n storedVersions !== undefined &&\n channelVersions[k] === storedVersions[k]\n ) {\n const old = channelVersions[k];\n channelVersions[k] = getNextVersion(old);\n bumped[k] = [old, channelVersions[k]];\n }\n values[k] = new DeltaSnapshot(channel.get());\n continue;\n }\n if (isDeltaChannel(channel)) {\n // Omitted from channel_values; reconstructed from ancestor writes.\n continue;\n }\n try {\n values[k] = channel.checkpoint();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.name === EmptyChannelError.unminifiable_name) {\n // no-op\n } else {\n throw error; // Rethrow unexpected errors\n }\n }\n }\n }\n\n return {\n v: 4,\n id: options?.id ?? uuid6(step),\n ts: new Date().toISOString(),\n channel_values: values,\n channel_versions: channelVersions,\n versions_seen: markBumpsSeen(\n checkpoint.versions_seen,\n bumped,\n options?.triggerToNodes\n ),\n };\n}\n\n/**\n * Advance whoever had seen a bumped channel's old version to the new one.\n *\n * A bump that only stores a snapshot is not a write. Left unseen, it would\n * re-fire `interruptBefore` and rerun the channel's subscribers. A channel\n * bumped from no version was never written, so it also goes to the\n * subscribers that never ran: they have no entry, and would start on the bump.\n * For each entry it advances, {@link SNAPSHOT_BUMPS} keeps the new version and\n * the one the node really read, so {@link versionsSeenWithoutBumps} can put\n * the read back.\n */\nfunction markBumpsSeen(\n versionsSeen: ReadonlyCheckpoint[\"versions_seen\"],\n bumped: Record<string, [number | string | undefined, number | string]>,\n triggerToNodes: Record<string, string[]> = {}\n): Checkpoint[\"versions_seen\"] {\n if (Object.keys(bumped).length === 0) return versionsSeen;\n const out = { ...versionsSeen };\n for (const [k, [old]] of Object.entries(bumped)) {\n if (old !== undefined) continue;\n for (const node of triggerToNodes[k] ?? []) out[node] ??= {};\n }\n const marks = { ...versionsSeen[SNAPSHOT_BUMPS] };\n for (const node in out) {\n if (\n !Object.prototype.hasOwnProperty.call(out, node) ||\n node === SNAPSHOT_BUMPS\n ) {\n continue;\n }\n for (const [k, [old, next]] of Object.entries(bumped)) {\n if (out[node][k] !== old) continue;\n const [advanced, read] = bumpKeys(node, k);\n // If an earlier bump set `old`, the real read is already recorded.\n if (old !== undefined && marks[advanced] !== old) marks[read] = old;\n marks[advanced] = next;\n out[node] = { ...out[node], [k]: next };\n }\n }\n if (Object.keys(marks).length > 0) out[SNAPSHOT_BUMPS] = marks;\n return out;\n}\n\n// Node names can't contain either separator, so the keys can't collide.\nconst bumpKeys = (node: string, channel: string): [string, string] => [\n `${node}${CHECKPOINT_NAMESPACE_SEPARATOR}${channel}`,\n `${node}${CHECKPOINT_NAMESPACE_END}${channel}`,\n];\n\n/**\n * `versions_seen` as the nodes read it: an entry a snapshot bump advanced goes\n * back to the version the node really read, or away if it never read one.\n */\nexport function versionsSeenWithoutBumps(\n versionsSeen: ReadonlyCheckpoint[\"versions_seen\"]\n): ReadonlyCheckpoint[\"versions_seen\"] {\n const marks = versionsSeen[SNAPSHOT_BUMPS];\n if (marks === undefined || Object.keys(marks).length === 0) {\n return versionsSeen;\n }\n const out: Checkpoint[\"versions_seen\"] = {};\n for (const node in versionsSeen) {\n if (\n !Object.prototype.hasOwnProperty.call(versionsSeen, node) ||\n node === SNAPSHOT_BUMPS\n ) {\n continue;\n }\n out[node] = {};\n for (const [k, v] of Object.entries(versionsSeen[node])) {\n const [advanced, read] = bumpKeys(node, k);\n if (marks[advanced] !== v) out[node][k] = v;\n else if (read in marks) out[node][k] = marks[read];\n }\n }\n return out;\n}\n\n/**\n * Checkpoint metadata key, `true` when every DeltaChannel write on the\n * checkpoint's branch carries a version. A delta channel with no version there\n * was never written, so it needs no walk. Branches started before this key\n * existed never get it: an older `updateState` stored a delta channel's first\n * write without a version, and only the walk finds such a write.\n */\nexport const DELTA_WRITES_VERSIONED = \"delta_writes_versioned\";\n\nexport function isDeltaWritesVersioned(\n metadata: CheckpointMetadata | undefined\n): boolean {\n return (\n (metadata as Record<string, unknown> | undefined)?.[\n DELTA_WRITES_VERSIONED\n ] === true\n );\n}\n\n/**\n * Channels whose value at `checkpoint` the ancestor walk rebuilds.\n *\n * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`\n * resolves directly, so only a channel with nothing stored there needs the\n * walk. With `versionedOnly`, a channel with no version was never written, so\n * it is empty without one; a walk for it would find no snapshot to stop at and\n * read every ancestor, every time the thread is loaded.\n *\n * A `BinaryOperatorAggregate` stores its value at every version, so one with a\n * version and nothing stored was written as a DeltaChannel before its\n * annotation changed, and is rebuilt the same way.\n */\nfunction deltaChannelsToReplay(\n specs: Record<string, BaseChannel>,\n checkpoint: ReadonlyCheckpoint,\n versionedOnly: boolean\n): string[] {\n const keys: string[] = [];\n for (const k in specs) {\n if (!Object.prototype.hasOwnProperty.call(specs, k)) continue;\n if (Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) {\n continue;\n }\n const versioned = checkpoint.channel_versions[k] !== undefined;\n if (\n (isDeltaChannel(specs[k]) && (versioned || !versionedOnly)) ||\n (specs[k].lc_graph_name === \"BinaryOperatorAggregate\" && versioned)\n ) {\n keys.push(k);\n }\n }\n return keys;\n}\n\n/**\n * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}\n * whose value is absent from `channel_values` by replaying ancestor writes.\n *\n * For most channels (and for delta channels with a {@link DeltaSnapshot} or a\n * migrated plain value in `channel_values`), {@link emptyChannels} is\n * sufficient and no saver access is required. When a delta channel is absent\n * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`\n * finds the nearest seed and accumulates the writes between it and the\n * target. All delta channels needing replay are batched into a single saver\n * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries\n * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are\n * left empty instead of walked.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: {\n saver?: BaseCheckpointSaver;\n config?: RunnableConfig;\n deltaWritesVersioned?: boolean;\n }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys = deltaChannelsToReplay(\n filteredSpecs,\n checkpoint,\n options?.deltaWritesVersioned ?? false\n );\n\n const written = deltaKeys.filter(\n (k) => checkpoint.channel_versions[k] !== undefined\n );\n if ((saver === undefined || config === undefined) && written.length > 0) {\n throw new Error(\n `DeltaChannel ${written.join(\", \")} has history to replay but no checkpointer or config was passed to read it`\n );\n }\n if (deltaKeys.length === 0 || saver === undefined || config === undefined) {\n return channels;\n }\n\n const histories = await saver.getDeltaChannelHistory({\n config,\n channels: deltaKeys,\n });\n for (const k of deltaKeys) {\n const history = histories[k];\n if (history === undefined) continue;\n const replayCh = filteredSpecs[k].fromCheckpoint(history.seed);\n if (isDeltaChannel(replayCh)) {\n (replayCh as DeltaChannelLike).replayWrites(history.writes);\n } else {\n // One update per write: the writes span many supersteps, and a single\n // call would reject a second Overwrite and drop the writes after one.\n for (const [, , value] of history.writes) replayCh.update([value]);\n }\n (channels as Record<string, BaseChannel>)[k] = replayCh;\n }\n return channels;\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  const require_constants = require("../constants.cjs");
2
2
  const require_errors = require("../errors.cjs");
3
3
  const require_base = require("./base.cjs");
4
+ let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
4
5
  //#region src/channels/binop.ts
5
6
  const isBinaryOperatorAggregate = (value) => {
6
7
  return value != null && value.lc_graph_name === "BinaryOperatorAggregate";
@@ -21,7 +22,7 @@ var BinaryOperatorAggregate = class BinaryOperatorAggregate extends require_base
21
22
  }
22
23
  fromCheckpoint(checkpoint) {
23
24
  const empty = new BinaryOperatorAggregate(this.operator, this.initialValueFactory);
24
- if (typeof checkpoint !== "undefined") empty.value = checkpoint;
25
+ if (typeof checkpoint !== "undefined") empty.value = (0, _langchain_langgraph_checkpoint.isDeltaSnapshot)(checkpoint) ? checkpoint.value : checkpoint;
25
26
  return empty;
26
27
  }
27
28
  update(values) {
@@ -1 +1 @@
1
- {"version":3,"file":"binop.cjs","names":["BaseChannel","_getOverwriteValue","_isOverwriteValue","InvalidUpdateError","EmptyChannelError"],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n empty.value = checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;AAiBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGHA,aAAAA,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YACxB,OAAM,QAAQ;AAEhB,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgBC,kBAAAA,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAIC,kBAAAA,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAIC,eAAAA,mBACR,iDACD;GAEH,MAAM,GAAG,OAAOF,kBAAAA,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIG,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIA,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
1
+ {"version":3,"file":"binop.cjs","names":["BaseChannel","_getOverwriteValue","_isOverwriteValue","InvalidUpdateError","EmptyChannelError"],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import { isDeltaSnapshot } from \"@langchain/langgraph-checkpoint\";\nimport {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n // A thread written while this field was a DeltaChannel stores snapshots.\n empty.value = isDeltaSnapshot(checkpoint)\n ? (checkpoint.value as ValueType)\n : checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGHA,aAAAA,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YAExB,OAAM,SAAA,GAAA,gCAAA,iBAAwB,WAAW,GACpC,WAAW,QACZ;AAEN,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgBC,kBAAAA,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAIC,kBAAAA,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAIC,eAAAA,mBACR,iDACD;GAEH,MAAM,GAAG,OAAOF,kBAAAA,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIG,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAIA,eAAAA,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"binop.d.cts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KAQK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAW5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAtGY;AAEd;;;;EA6GE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
1
+ {"version":3,"file":"binop.d.cts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KASK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAc5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAzGY;AAEd;;;;EAgHE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"binop.d.ts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KAQK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAW5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAtGY;AAEd;;;;EA6GE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
1
+ {"version":3,"file":"binop.d.ts","names":[],"sources":["../../src/channels/binop.ts"],"mappings":";;;;KASK,gBAAA,0BACD,cAAA,CAAe,SAAA,IACf,UAAA;AAAA,KAEQ,cAAA,2BACV,CAAA,EAAG,SAAA,EACH,CAAA,EAAG,UAAA,KACA,SAAA;AATmC;;;AAAA,cAoB3B,uBAAA,yBAEE,SAAA,UACL,WAAA,CACR,SAAA,EACA,gBAAA,CAAiB,SAAA,EAAW,UAAA,GAC5B,SAAA;EAEA,aAAA;EAEA,KAAA,EAAO,SAAA;EAEP,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA;EAEpC,mBAAA,SAA4B,SAAA;EAE5B,WAAA,CACE,QAAA,EAAU,cAAA,CAAe,SAAA,EAAW,UAAA,GACpC,mBAAA,SAA4B,SAAA;EASvB,cAAA,CAAe,UAAA,GAAa,SAAA;EAc5B,MAAA,CAAO,MAAA,EAAQ,gBAAA,CAAiB,SAAA,EAAW,UAAA;EAkC3C,GAAA,CAAA,GAAO,SAAA;EAOP,UAAA,CAAA,GAAc,SAAA;EAOrB,WAAA,CAAA;EAzGY;AAEd;;;;EAgHE,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA"}
@@ -1,6 +1,7 @@
1
1
  import { _getOverwriteValue, _isOverwriteValue } from "../constants.js";
2
2
  import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
3
3
  import { BaseChannel } from "./base.js";
4
+ import { isDeltaSnapshot } from "@langchain/langgraph-checkpoint";
4
5
  //#region src/channels/binop.ts
5
6
  const isBinaryOperatorAggregate = (value) => {
6
7
  return value != null && value.lc_graph_name === "BinaryOperatorAggregate";
@@ -21,7 +22,7 @@ var BinaryOperatorAggregate = class BinaryOperatorAggregate extends BaseChannel
21
22
  }
22
23
  fromCheckpoint(checkpoint) {
23
24
  const empty = new BinaryOperatorAggregate(this.operator, this.initialValueFactory);
24
- if (typeof checkpoint !== "undefined") empty.value = checkpoint;
25
+ if (typeof checkpoint !== "undefined") empty.value = isDeltaSnapshot(checkpoint) ? checkpoint.value : checkpoint;
25
26
  return empty;
26
27
  }
27
28
  update(values) {
@@ -1 +1 @@
1
- {"version":3,"file":"binop.js","names":[],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n empty.value = checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;AAiBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGH,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YACxB,OAAM,QAAQ;AAEhB,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgB,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAI,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAI,mBACR,iDACD;GAEH,MAAM,GAAG,OAAO,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
1
+ {"version":3,"file":"binop.js","names":[],"sources":["../../src/channels/binop.ts"],"sourcesContent":["import { isDeltaSnapshot } from \"@langchain/langgraph-checkpoint\";\nimport {\n _getOverwriteValue,\n _isOverwriteValue,\n type OverwriteValue,\n} from \"../constants.js\";\nimport { EmptyChannelError, InvalidUpdateError } from \"../errors.js\";\nimport { BaseChannel } from \"./base.js\";\n\ntype OverwriteOrValue<ValueType, UpdateType> =\n | OverwriteValue<ValueType>\n | UpdateType;\n\nexport type BinaryOperator<ValueType, UpdateType> = (\n a: ValueType,\n b: UpdateType\n) => ValueType;\n\nconst isBinaryOperatorAggregate = (\n value: BaseChannel\n): value is BinaryOperatorAggregate<unknown, unknown> => {\n return value != null && value.lc_graph_name === \"BinaryOperatorAggregate\";\n};\n\n/**\n * Stores the result of applying a binary operator to the current value and each new value.\n */\nexport class BinaryOperatorAggregate<\n ValueType,\n UpdateType = ValueType,\n> extends BaseChannel<\n ValueType,\n OverwriteOrValue<ValueType, UpdateType>,\n ValueType\n> {\n lc_graph_name = \"BinaryOperatorAggregate\";\n\n value: ValueType | undefined;\n\n operator: BinaryOperator<ValueType, UpdateType>;\n\n initialValueFactory?: () => ValueType;\n\n constructor(\n operator: BinaryOperator<ValueType, UpdateType>,\n initialValueFactory?: () => ValueType\n ) {\n super();\n\n this.operator = operator;\n this.initialValueFactory = initialValueFactory;\n this.value = initialValueFactory?.();\n }\n\n public fromCheckpoint(checkpoint?: ValueType) {\n const empty = new BinaryOperatorAggregate(\n this.operator,\n this.initialValueFactory\n );\n if (typeof checkpoint !== \"undefined\") {\n // A thread written while this field was a DeltaChannel stores snapshots.\n empty.value = isDeltaSnapshot(checkpoint)\n ? (checkpoint.value as ValueType)\n : checkpoint;\n }\n return empty as this;\n }\n\n public update(values: OverwriteOrValue<ValueType, UpdateType>[]): boolean {\n let newValues = values;\n if (!newValues.length) return false;\n\n if (this.value === undefined) {\n const first = newValues[0];\n const [isOverwrite, overwriteVal] = _getOverwriteValue<ValueType>(first);\n if (isOverwrite) {\n this.value = overwriteVal;\n } else {\n this.value = first as ValueType;\n }\n newValues = newValues.slice(1);\n }\n\n let seenOverwrite = false;\n for (const incoming of newValues) {\n if (_isOverwriteValue<ValueType>(incoming)) {\n if (seenOverwrite) {\n throw new InvalidUpdateError(\n \"Can receive only one Overwrite value per step.\"\n );\n }\n const [, val] = _getOverwriteValue<ValueType>(incoming);\n this.value = val;\n seenOverwrite = true;\n continue;\n } else if (!seenOverwrite && this.value !== undefined) {\n this.value = this.operator(this.value, incoming);\n }\n }\n return true;\n }\n\n public get(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n public checkpoint(): ValueType {\n if (this.value === undefined) {\n throw new EmptyChannelError();\n }\n return this.value;\n }\n\n isAvailable(): boolean {\n return this.value !== undefined;\n }\n\n /**\n * Compare this channel with another channel for equality.\n * Two BinaryOperatorAggregate channels are equal if they have the same operator function.\n * This follows the Python implementation which compares operator references.\n */\n equals(other: BaseChannel): boolean {\n if (this === other) return true;\n if (!isBinaryOperatorAggregate(other)) return false;\n return this.operator === other.operator;\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,6BACJ,UACuD;AACvD,QAAO,SAAS,QAAQ,MAAM,kBAAkB;;;;;AAMlD,IAAa,0BAAb,MAAa,gCAGH,YAIR;CACA,gBAAgB;CAEhB;CAEA;CAEA;CAEA,YACE,UACA,qBACA;AACA,SAAO;AAEP,OAAK,WAAW;AAChB,OAAK,sBAAsB;AAC3B,OAAK,QAAQ,uBAAuB;;CAGtC,eAAsB,YAAwB;EAC5C,MAAM,QAAQ,IAAI,wBAChB,KAAK,UACL,KAAK,oBACN;AACD,MAAI,OAAO,eAAe,YAExB,OAAM,QAAQ,gBAAgB,WAAW,GACpC,WAAW,QACZ;AAEN,SAAO;;CAGT,OAAc,QAA4D;EACxE,IAAI,YAAY;AAChB,MAAI,CAAC,UAAU,OAAQ,QAAO;AAE9B,MAAI,KAAK,UAAU,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;GACxB,MAAM,CAAC,aAAa,gBAAgB,mBAA8B,MAAM;AACxE,OAAI,YACF,MAAK,QAAQ;OAEb,MAAK,QAAQ;AAEf,eAAY,UAAU,MAAM,EAAE;;EAGhC,IAAI,gBAAgB;AACpB,OAAK,MAAM,YAAY,UACrB,KAAI,kBAA6B,SAAS,EAAE;AAC1C,OAAI,cACF,OAAM,IAAI,mBACR,iDACD;GAEH,MAAM,GAAG,OAAO,mBAA8B,SAAS;AACvD,QAAK,QAAQ;AACb,mBAAgB;AAChB;aACS,CAAC,iBAAiB,KAAK,UAAU,KAAA,EAC1C,MAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,SAAS;AAGpD,SAAO;;CAGT,MAAwB;AACtB,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,aAA+B;AAC7B,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,IAAI,mBAAmB;AAE/B,SAAO,KAAK;;CAGd,cAAuB;AACrB,SAAO,KAAK,UAAU,KAAA;;;;;;;CAQxB,OAAO,OAA6B;AAClC,MAAI,SAAS,MAAO,QAAO;AAC3B,MAAI,CAAC,0BAA0B,MAAM,CAAE,QAAO;AAC9C,SAAO,KAAK,aAAa,MAAM"}
@@ -24,6 +24,8 @@ exports.UntrackedValueChannel = require_untracked_value.UntrackedValueChannel;
24
24
  exports.channelsFromCheckpoint = require_base.channelsFromCheckpoint;
25
25
  exports.createCheckpoint = require_base.createCheckpoint;
26
26
  exports.deltaChannelsToSnapshot = require_base.deltaChannelsToSnapshot;
27
+ exports.deltaChannelsWithPendingWrites = require_base.deltaChannelsWithPendingWrites;
27
28
  exports.empty = require_base.emptyChannels;
29
+ exports.exitDeltaLateTaskId = require_base.exitDeltaLateTaskId;
28
30
  exports.exitDeltaTaskId = require_base.exitDeltaTaskId;
29
31
  exports.isDeltaChannel = require_base.isDeltaChannel;
@@ -1,4 +1,4 @@
1
- import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.cjs";
1
+ import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.cjs";
2
2
  import { DeltaChannel, DeltaReducer } from "./delta.cjs";
3
3
  import { BinaryOperator, BinaryOperatorAggregate } from "./binop.cjs";
4
4
  import { AnyValue } from "./any_value.cjs";
@@ -8,4 +8,4 @@ import { EphemeralValue } from "./ephemeral_value.cjs";
8
8
  import { NamedBarrierValue, NamedBarrierValueAfterFinish } from "./named_barrier_value.cjs";
9
9
  import { Topic } from "./topic.cjs";
10
10
  import { UntrackedValueChannel } from "./untracked_value.cjs";
11
- export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
11
+ export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
@@ -1,4 +1,4 @@
1
- import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.js";
1
+ import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.js";
2
2
  import { DeltaChannel, DeltaReducer } from "./delta.js";
3
3
  import { BinaryOperator, BinaryOperatorAggregate } from "./binop.js";
4
4
  import { AnyValue } from "./any_value.js";
@@ -8,4 +8,4 @@ import { EphemeralValue } from "./ephemeral_value.js";
8
8
  import { NamedBarrierValue, NamedBarrierValueAfterFinish } from "./named_barrier_value.js";
9
9
  import { Topic } from "./topic.js";
10
10
  import { UntrackedValueChannel } from "./untracked_value.js";
11
- export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
11
+ export { AnyValue, BaseChannel, type BinaryOperator, BinaryOperatorAggregate, DeltaChannel, type DeltaReducer, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, type WaitForNames, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
@@ -1,4 +1,4 @@
1
- import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel } from "./base.js";
1
+ import { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel } from "./base.js";
2
2
  import { BinaryOperatorAggregate } from "./binop.js";
3
3
  import { LastValue, LastValueAfterFinish } from "./last_value.js";
4
4
  import { Topic } from "./topic.js";
@@ -8,4 +8,4 @@ import { DeltaChannel } from "./delta.js";
8
8
  import { AnyValue } from "./any_value.js";
9
9
  import { DynamicBarrierValue } from "./dynamic_barrier_value.js";
10
10
  import { UntrackedValueChannel } from "./untracked_value.js";
11
- export { AnyValue, BaseChannel, BinaryOperatorAggregate, DeltaChannel, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels as empty, exitDeltaTaskId, isDeltaChannel };
11
+ export { AnyValue, BaseChannel, BinaryOperatorAggregate, DeltaChannel, DynamicBarrierValue, EphemeralValue, LastValue, LastValueAfterFinish, NamedBarrierValue, NamedBarrierValueAfterFinish, Topic, UntrackedValueChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, deltaChannelsWithPendingWrites, emptyChannels as empty, exitDeltaLateTaskId, exitDeltaTaskId, isDeltaChannel };
@@ -69,6 +69,11 @@ const INTERRUPT = "__interrupt__";
69
69
  const RESUME = "__resume__";
70
70
  /** Special channel reserved for cases when a task exits without any writes */
71
71
  const NO_WRITES = "__no_writes__";
72
+ /**
73
+ * `versions_seen` key recording which entries a snapshot-only version bump
74
+ * advanced, and the versions those nodes really read.
75
+ */
76
+ const SNAPSHOT_BUMPS = "__snapshot_bumps__";
72
77
  /** Special channel reserved for graph return */
73
78
  const RETURN = "__return__";
74
79
  /** Special channel reserved for graph previous state */
@@ -80,6 +85,12 @@ const TASKS = "__pregel_tasks";
80
85
  const PUSH = "__pregel_push";
81
86
  const PULL = "__pregel_pull";
82
87
  const NULL_TASK_ID = "00000000-0000-0000-0000-000000000000";
88
+ /**
89
+ * Stands in for `NULL_TASK_ID` as an exit-mode delta write's synthetic id (see
90
+ * `exitDeltaTaskId`). No task's synthetic id can equal it: those keep the task
91
+ * id's third group, and task ids are uuid5, so that group starts with 5.
92
+ */
93
+ const EXIT_DELTA_NULL_TASK_ID = "00000000-0000-0000-0000-000000000001";
83
94
  const RESERVED = [
84
95
  TAG_HIDDEN,
85
96
  INPUT,
@@ -88,6 +99,7 @@ const RESERVED = [
88
99
  ERROR,
89
100
  ERROR_SOURCE_NODE,
90
101
  NO_WRITES,
102
+ SNAPSHOT_BUMPS,
91
103
  CONFIG_KEY_SEND,
92
104
  CONFIG_KEY_READ,
93
105
  CONFIG_KEY_CHECKPOINTER,
@@ -527,6 +539,7 @@ exports.CommandInstance = CommandInstance;
527
539
  exports.END = END;
528
540
  exports.ERROR = ERROR;
529
541
  exports.ERROR_SOURCE_NODE = ERROR_SOURCE_NODE;
542
+ exports.EXIT_DELTA_NULL_TASK_ID = EXIT_DELTA_NULL_TASK_ID;
530
543
  exports.INPUT = INPUT;
531
544
  exports.INTERRUPT = INTERRUPT;
532
545
  exports.NO_WRITES = NO_WRITES;
@@ -540,6 +553,7 @@ exports.RESERVED = RESERVED;
540
553
  exports.RESUME = RESUME;
541
554
  exports.RETURN = RETURN;
542
555
  exports.SELF = SELF;
556
+ exports.SNAPSHOT_BUMPS = SNAPSHOT_BUMPS;
543
557
  exports.START = START;
544
558
  exports.Send = Send;
545
559
  exports.TAG_HIDDEN = TAG_HIDDEN;