@langchain/langgraph 1.4.19 → 1.4.20

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.
@@ -105,15 +105,19 @@ function exitDeltaTaskId(step, taskId) {
105
105
  *
106
106
  * A channel snapshots when EITHER its accumulated update count reaches
107
107
  * `snapshotFrequency` OR the total supersteps since its last snapshot reaches
108
- * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.
108
+ * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel
109
+ * without a version is skipped: it was never written on this branch, so it
110
+ * has nothing to snapshot. Pass `undefined` on a branch without
111
+ * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes
112
+ * and this snapshot is what bounds its walk. Pure predicate, no mutation.
109
113
  */
110
- function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot) {
114
+ function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot, channelVersions) {
111
115
  const result = /* @__PURE__ */ new Set();
112
116
  const maxSupersteps = require_constants.getDeltaMaxSuperstepsSinceSnapshot();
113
117
  for (const name in channels) {
114
118
  if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;
115
119
  const ch = channels[name];
116
- if (!isDeltaChannel(ch) || !ch.isAvailable()) continue;
120
+ if (!isDeltaChannel(ch) || !ch.isAvailable() || channelVersions !== void 0 && channelVersions[name] === void 0) continue;
117
121
  const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];
118
122
  if (updates >= ch.snapshotFrequency || supersteps >= maxSupersteps) result.add(name);
119
123
  }
@@ -154,6 +158,34 @@ function createCheckpoint(checkpoint, channels, step, options) {
154
158
  };
155
159
  }
156
160
  /**
161
+ * Checkpoint metadata key, `true` when every DeltaChannel write on the
162
+ * checkpoint's branch carries a version. A delta channel with no version there
163
+ * was never written, so it needs no walk. Branches started before this key
164
+ * existed never get it: an older `updateState` stored a delta channel's first
165
+ * write without a version, and only the walk finds such a write.
166
+ */
167
+ const DELTA_WRITES_VERSIONED = "delta_writes_versioned";
168
+ function isDeltaWritesVersioned(metadata) {
169
+ return metadata?.[DELTA_WRITES_VERSIONED] === true;
170
+ }
171
+ /**
172
+ * Delta channels whose value at `checkpoint` the ancestor walk rebuilds.
173
+ *
174
+ * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`
175
+ * resolves directly, so only a channel with nothing stored there needs the
176
+ * walk. With `versionedOnly`, a channel with no version was never written, so
177
+ * it is empty without one; a walk for it would find no snapshot to stop at and
178
+ * read every ancestor, every time the thread is loaded.
179
+ */
180
+ function deltaChannelsToReplay(specs, checkpoint, versionedOnly) {
181
+ const keys = [];
182
+ for (const k in specs) {
183
+ 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);
185
+ }
186
+ return keys;
187
+ }
188
+ /**
157
189
  * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}
158
190
  * whose value is absent from `channel_values` by replaying ancestor writes.
159
191
  *
@@ -163,17 +195,15 @@ function createCheckpoint(checkpoint, channels, step, options) {
163
195
  * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`
164
196
  * finds the nearest seed and accumulates the writes between it and the
165
197
  * target. All delta channels needing replay are batched into a single saver
166
- * call.
198
+ * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries
199
+ * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are
200
+ * left empty instead of walked.
167
201
  */
168
202
  async function channelsFromCheckpoint(specs, checkpoint, options) {
169
203
  const channels = emptyChannels(specs, checkpoint);
170
204
  const { saver, config } = options ?? {};
171
205
  const filteredSpecs = getOnlyChannels(specs);
172
- const deltaKeys = [];
173
- for (const k in filteredSpecs) {
174
- if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;
175
- if (isDeltaChannel(filteredSpecs[k]) && !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) deltaKeys.push(k);
176
- }
206
+ const deltaKeys = deltaChannelsToReplay(filteredSpecs, checkpoint, options?.deltaWritesVersioned ?? false);
177
207
  const written = deltaKeys.filter((k) => checkpoint.channel_versions[k] !== void 0);
178
208
  if ((saver === void 0 || config === void 0) && written.length > 0) throw new Error(`DeltaChannel ${written.join(", ")} has history to replay but no checkpointer or config was passed to read it`);
179
209
  if (deltaKeys.length === 0 || saver === void 0 || config === void 0) return channels;
@@ -192,6 +222,7 @@ async function channelsFromCheckpoint(specs, checkpoint, options) {
192
222
  }
193
223
  //#endregion
194
224
  exports.BaseChannel = BaseChannel;
225
+ exports.DELTA_WRITES_VERSIONED = DELTA_WRITES_VERSIONED;
195
226
  exports.channelsFromCheckpoint = channelsFromCheckpoint;
196
227
  exports.createCheckpoint = createCheckpoint;
197
228
  exports.deltaChannelsToSnapshot = deltaChannelsToSnapshot;
@@ -200,5 +231,6 @@ exports.exitDeltaTaskId = exitDeltaTaskId;
200
231
  exports.getOnlyChannels = getOnlyChannels;
201
232
  exports.isBaseChannel = isBaseChannel;
202
233
  exports.isDeltaChannel = isDeltaChannel;
234
+ exports.isDeltaWritesVersioned = isDeltaWritesVersioned;
203
235
 
204
236
  //# sourceMappingURL=base.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"base.cjs","names":["EmptyChannelError","getDeltaMaxSuperstepsSinceSnapshot","DeltaSnapshot"],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n ReadonlyCheckpoint,\n uuid6,\n Checkpoint,\n DeltaSnapshot,\n type BaseCheckpointSaver,\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`. Pure predicate — no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>\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 (!isDeltaChannel(ch) || !ch.isAvailable()) continue;\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 * 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.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: { saver?: BaseCheckpointSaver; config?: RunnableConfig }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys: string[] = [];\n for (const k in filteredSpecs) {\n if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;\n if (\n isDeltaChannel(filteredSpecs[k]) &&\n !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)\n ) {\n deltaKeys.push(k);\n }\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.cjs","names":["EmptyChannelError","getDeltaMaxSuperstepsSinceSnapshot","DeltaSnapshot"],"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,4 +1,4 @@
1
- import { BaseCheckpointSaver, Checkpoint, ReadonlyCheckpoint } from "@langchain/langgraph-checkpoint";
1
+ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata, ReadonlyCheckpoint } from "@langchain/langgraph-checkpoint";
2
2
  import { RunnableConfig } from "@langchain/core/runnables";
3
3
 
4
4
  //#region src/channels/base.d.ts
@@ -99,9 +99,13 @@ declare function exitDeltaTaskId(step: number, taskId: string): string;
99
99
  *
100
100
  * A channel snapshots when EITHER its accumulated update count reaches
101
101
  * `snapshotFrequency` OR the total supersteps since its last snapshot reaches
102
- * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.
102
+ * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel
103
+ * without a version is skipped: it was never written on this branch, so it
104
+ * has nothing to snapshot. Pass `undefined` on a branch without
105
+ * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes
106
+ * and this snapshot is what bounds its walk. Pure predicate, no mutation.
103
107
  */
104
- declare function deltaChannelsToSnapshot(channels: Record<string, BaseChannel>, countersSinceDeltaSnapshot: Record<string, [number, number]>): Set<string>;
108
+ declare function deltaChannelsToSnapshot(channels: Record<string, BaseChannel>, countersSinceDeltaSnapshot: Record<string, [number, number]>, channelVersions: Record<string, number | string> | undefined): Set<string>;
105
109
  declare function createCheckpoint<ValueType>(checkpoint: ReadonlyCheckpoint, channels: Record<string, BaseChannel<ValueType>> | undefined, step: number, options?: {
106
110
  id?: string;
107
111
  channelsToSnapshot?: Set<string>;
@@ -118,11 +122,14 @@ declare function createCheckpoint<ValueType>(checkpoint: ReadonlyCheckpoint, cha
118
122
  * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`
119
123
  * finds the nearest seed and accumulates the writes between it and the
120
124
  * target. All delta channels needing replay are batched into a single saver
121
- * call.
125
+ * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries
126
+ * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are
127
+ * left empty instead of walked.
122
128
  */
123
129
  declare function channelsFromCheckpoint<Cc extends Record<string, BaseChannel>>(specs: Cc, checkpoint: ReadonlyCheckpoint, options?: {
124
130
  saver?: BaseCheckpointSaver;
125
131
  config?: RunnableConfig;
132
+ deltaWritesVersioned?: boolean;
126
133
  }): Promise<Cc>;
127
134
  //#endregion
128
135
  export { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel };
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.cts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;AAoBA;;iBAAgB,cAAA,CAAe,OAAA,EAAS,WAAA;;uBASlB,WAAA;EAKpB,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAFD;;;EAAA,SAOF,aAAA;EAoCO;EAjChB,aAAA;EA0Fc;;;;;;;;EAAA,SAhFL,cAAA,CAAe,UAAA,GAAa,cAAA;EAlBzB;;;;;;;;;;;;;EAAA,SAiCH,MAAA,CAAO,MAAA,EAAQ,UAAA;EAmCxB;;;;;;EAAA,SA3BS,GAAA,CAAA,GAAO,SAAA;EAgFF;;;;;;EAAA,SAxEL,UAAA,CAAA,GAAc,cAAA;EA2EtB;;;;;;;EAlED,OAAA,CAAA;EAiEY;;;;;AA8Bd;EArFE,MAAA,CAAA;;;;AAqGF;;EA5FE,WAAA,CAAA;EA6FyB;;;;;;;;EAxEzB,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA;AAAA,iBAuBA,aAAA,YAAyB,MAAA,SAAe,WAAA,EAAA,CACtD,QAAA,EAAU,EAAA,EACV,UAAA,EAAY,kBAAA,GACX,EAAA;;;AAkEH;;;;;iBArCgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;;;iBAgB9B,uBAAA,CACd,QAAA,EAAU,MAAA,SAAe,WAAA,GACzB,0BAAA,EAA4B,MAAA,6BAC3B,GAAA;AAAA,iBAkBa,gBAAA,WAAA,CACd,UAAA,EAAY,kBAAA,EACZ,QAAA,EAAU,MAAA,SAAe,WAAA,CAAY,SAAA,gBACrC,IAAA,UACA,OAAA;EACE,EAAA;EACA,kBAAA,GAAqB,GAAA;EACrB,eAAA,GAAkB,GAAA;EAClB,cAAA,IAAkB,OAAA;AAAA,IAEnB,UAAA;;;;;;;;;;;;AAqEH;iBAAsB,sBAAA,YACT,MAAA,SAAe,WAAA,EAAA,CAE1B,KAAA,EAAO,EAAA,EACP,UAAA,EAAY,kBAAA,EACZ,OAAA;EAAY,KAAA,GAAQ,mBAAA;EAAqB,MAAA,GAAS,cAAA;AAAA,IACjD,OAAA,CAAQ,EAAA"}
1
+ {"version":3,"file":"base.d.cts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;AAqBA;;iBAAgB,cAAA,CAAe,OAAA,EAAS,WAAA;;uBASlB,WAAA;EAKpB,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAFD;;;EAAA,SAOF,aAAA;EAoCO;EAjChB,aAAA;EA0Fc;;;;;;;;EAAA,SAhFL,cAAA,CAAe,UAAA,GAAa,cAAA;EAlBzB;;;;;;;;;;;;;EAAA,SAiCH,MAAA,CAAO,MAAA,EAAQ,UAAA;EAmCxB;;;;;;EAAA,SA3BS,GAAA,CAAA,GAAO,SAAA;EAgFF;;;;;;EAAA,SAxEL,UAAA,CAAA,GAAc,cAAA;EA2EtB;;;;;;;EAlED,OAAA,CAAA;EAiEY;;;;;AA8Bd;EArFE,MAAA,CAAA;;;;AAyGF;;EAhGE,WAAA,CAAA;EAiGyB;;;;;;;;EA5EzB,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA;AAAA,iBAuBA,aAAA,YAAyB,MAAA,SAAe,WAAA,EAAA,CACtD,QAAA,EAAU,EAAA,EACV,UAAA,EAAY,kBAAA,GACX,EAAA;;;;;;AA6EH;;iBAhDgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;;;;;;;iBAoB9B,uBAAA,CACd,QAAA,EAAU,MAAA,SAAe,WAAA,GACzB,0BAAA,EAA4B,MAAA,4BAC5B,eAAA,EAAiB,MAAA,wCAChB,GAAA;AAAA,iBAwBa,gBAAA,WAAA,CACd,UAAA,EAAY,kBAAA,EACZ,QAAA,EAAU,MAAA,SAAe,WAAA,CAAY,SAAA,gBACrC,IAAA,UACA,OAAA;EACE,EAAA;EACA,kBAAA,GAAqB,GAAA;EACrB,eAAA,GAAkB,GAAA;EAClB,cAAA,IAAkB,OAAA;AAAA,IAEnB,UAAA;;AAsHH;;;;;;;;;;;;;iBAAsB,sBAAA,YACT,MAAA,SAAe,WAAA,EAAA,CAE1B,KAAA,EAAO,EAAA,EACP,UAAA,EAAY,kBAAA,EACZ,OAAA;EACE,KAAA,GAAQ,mBAAA;EACR,MAAA,GAAS,cAAA;EACT,oBAAA;AAAA,IAED,OAAA,CAAQ,EAAA"}
@@ -1,4 +1,4 @@
1
- import { BaseCheckpointSaver, Checkpoint, ReadonlyCheckpoint } from "@langchain/langgraph-checkpoint";
1
+ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata, ReadonlyCheckpoint } from "@langchain/langgraph-checkpoint";
2
2
  import { RunnableConfig } from "@langchain/core/runnables";
3
3
 
4
4
  //#region src/channels/base.d.ts
@@ -99,9 +99,13 @@ declare function exitDeltaTaskId(step: number, taskId: string): string;
99
99
  *
100
100
  * A channel snapshots when EITHER its accumulated update count reaches
101
101
  * `snapshotFrequency` OR the total supersteps since its last snapshot reaches
102
- * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.
102
+ * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel
103
+ * without a version is skipped: it was never written on this branch, so it
104
+ * has nothing to snapshot. Pass `undefined` on a branch without
105
+ * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes
106
+ * and this snapshot is what bounds its walk. Pure predicate, no mutation.
103
107
  */
104
- declare function deltaChannelsToSnapshot(channels: Record<string, BaseChannel>, countersSinceDeltaSnapshot: Record<string, [number, number]>): Set<string>;
108
+ declare function deltaChannelsToSnapshot(channels: Record<string, BaseChannel>, countersSinceDeltaSnapshot: Record<string, [number, number]>, channelVersions: Record<string, number | string> | undefined): Set<string>;
105
109
  declare function createCheckpoint<ValueType>(checkpoint: ReadonlyCheckpoint, channels: Record<string, BaseChannel<ValueType>> | undefined, step: number, options?: {
106
110
  id?: string;
107
111
  channelsToSnapshot?: Set<string>;
@@ -118,11 +122,14 @@ declare function createCheckpoint<ValueType>(checkpoint: ReadonlyCheckpoint, cha
118
122
  * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`
119
123
  * finds the nearest seed and accumulates the writes between it and the
120
124
  * target. All delta channels needing replay are batched into a single saver
121
- * call.
125
+ * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries
126
+ * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are
127
+ * left empty instead of walked.
122
128
  */
123
129
  declare function channelsFromCheckpoint<Cc extends Record<string, BaseChannel>>(specs: Cc, checkpoint: ReadonlyCheckpoint, options?: {
124
130
  saver?: BaseCheckpointSaver;
125
131
  config?: RunnableConfig;
132
+ deltaWritesVersioned?: boolean;
126
133
  }): Promise<Cc>;
127
134
  //#endregion
128
135
  export { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, isDeltaChannel };
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;AAoBA;;iBAAgB,cAAA,CAAe,OAAA,EAAS,WAAA;;uBASlB,WAAA;EAKpB,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAFD;;;EAAA,SAOF,aAAA;EAoCO;EAjChB,aAAA;EA0Fc;;;;;;;;EAAA,SAhFL,cAAA,CAAe,UAAA,GAAa,cAAA;EAlBzB;;;;;;;;;;;;;EAAA,SAiCH,MAAA,CAAO,MAAA,EAAQ,UAAA;EAmCxB;;;;;;EAAA,SA3BS,GAAA,CAAA,GAAO,SAAA;EAgFF;;;;;;EAAA,SAxEL,UAAA,CAAA,GAAc,cAAA;EA2EtB;;;;;;;EAlED,OAAA,CAAA;EAiEY;;;;;AA8Bd;EArFE,MAAA,CAAA;;;;AAqGF;;EA5FE,WAAA,CAAA;EA6FyB;;;;;;;;EAxEzB,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA;AAAA,iBAuBA,aAAA,YAAyB,MAAA,SAAe,WAAA,EAAA,CACtD,QAAA,EAAU,EAAA,EACV,UAAA,EAAY,kBAAA,GACX,EAAA;;;AAkEH;;;;;iBArCgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;;;iBAgB9B,uBAAA,CACd,QAAA,EAAU,MAAA,SAAe,WAAA,GACzB,0BAAA,EAA4B,MAAA,6BAC3B,GAAA;AAAA,iBAkBa,gBAAA,WAAA,CACd,UAAA,EAAY,kBAAA,EACZ,QAAA,EAAU,MAAA,SAAe,WAAA,CAAY,SAAA,gBACrC,IAAA,UACA,OAAA;EACE,EAAA;EACA,kBAAA,GAAqB,GAAA;EACrB,eAAA,GAAkB,GAAA;EAClB,cAAA,IAAkB,OAAA;AAAA,IAEnB,UAAA;;;;;;;;;;;;AAqEH;iBAAsB,sBAAA,YACT,MAAA,SAAe,WAAA,EAAA,CAE1B,KAAA,EAAO,EAAA,EACP,UAAA,EAAY,kBAAA,EACZ,OAAA;EAAY,KAAA,GAAQ,mBAAA;EAAqB,MAAA,GAAS,cAAA;AAAA,IACjD,OAAA,CAAQ,EAAA"}
1
+ {"version":3,"file":"base.d.ts","names":[],"sources":["../../src/channels/base.ts"],"mappings":";;;;;;AAqBA;;iBAAgB,cAAA,CAAe,OAAA,EAAS,WAAA;;uBASlB,WAAA;EAKpB,SAAA,EAAW,SAAA;EAEX,UAAA,EAAY,UAAA;EAFD;;;EAAA,SAOF,aAAA;EAoCO;EAjChB,aAAA;EA0Fc;;;;;;;;EAAA,SAhFL,cAAA,CAAe,UAAA,GAAa,cAAA;EAlBzB;;;;;;;;;;;;;EAAA,SAiCH,MAAA,CAAO,MAAA,EAAQ,UAAA;EAmCxB;;;;;;EAAA,SA3BS,GAAA,CAAA,GAAO,SAAA;EAgFF;;;;;;EAAA,SAxEL,UAAA,CAAA,GAAc,cAAA;EA2EtB;;;;;;;EAlED,OAAA,CAAA;EAiEY;;;;;AA8Bd;EArFE,MAAA,CAAA;;;;AAyGF;;EAhGE,WAAA,CAAA;EAiGyB;;;;;;;;EA5EzB,MAAA,CAAO,KAAA,EAAO,WAAA;AAAA;AAAA,iBAuBA,aAAA,YAAyB,MAAA,SAAe,WAAA,EAAA,CACtD,QAAA,EAAU,EAAA,EACV,UAAA,EAAY,kBAAA,GACX,EAAA;;;;;;AA6EH;;iBAhDgB,eAAA,CAAgB,IAAA,UAAc,MAAA;;;;;;;;;;;;iBAoB9B,uBAAA,CACd,QAAA,EAAU,MAAA,SAAe,WAAA,GACzB,0BAAA,EAA4B,MAAA,4BAC5B,eAAA,EAAiB,MAAA,wCAChB,GAAA;AAAA,iBAwBa,gBAAA,WAAA,CACd,UAAA,EAAY,kBAAA,EACZ,QAAA,EAAU,MAAA,SAAe,WAAA,CAAY,SAAA,gBACrC,IAAA,UACA,OAAA;EACE,EAAA;EACA,kBAAA,GAAqB,GAAA;EACrB,eAAA,GAAkB,GAAA;EAClB,cAAA,IAAkB,OAAA;AAAA,IAEnB,UAAA;;AAsHH;;;;;;;;;;;;;iBAAsB,sBAAA,YACT,MAAA,SAAe,WAAA,EAAA,CAE1B,KAAA,EAAO,EAAA,EACP,UAAA,EAAY,kBAAA,EACZ,OAAA;EACE,KAAA,GAAQ,mBAAA;EACR,MAAA,GAAS,cAAA;EACT,oBAAA;AAAA,IAED,OAAA,CAAQ,EAAA"}
@@ -105,15 +105,19 @@ function exitDeltaTaskId(step, taskId) {
105
105
  *
106
106
  * A channel snapshots when EITHER its accumulated update count reaches
107
107
  * `snapshotFrequency` OR the total supersteps since its last snapshot reaches
108
- * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.
108
+ * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Given `channelVersions`, a channel
109
+ * without a version is skipped: it was never written on this branch, so it
110
+ * has nothing to snapshot. Pass `undefined` on a branch without
111
+ * {@link DELTA_WRITES_VERSIONED}, where such a channel can still hold writes
112
+ * and this snapshot is what bounds its walk. Pure predicate, no mutation.
109
113
  */
110
- function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot) {
114
+ function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot, channelVersions) {
111
115
  const result = /* @__PURE__ */ new Set();
112
116
  const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();
113
117
  for (const name in channels) {
114
118
  if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;
115
119
  const ch = channels[name];
116
- if (!isDeltaChannel(ch) || !ch.isAvailable()) continue;
120
+ if (!isDeltaChannel(ch) || !ch.isAvailable() || channelVersions !== void 0 && channelVersions[name] === void 0) continue;
117
121
  const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];
118
122
  if (updates >= ch.snapshotFrequency || supersteps >= maxSupersteps) result.add(name);
119
123
  }
@@ -154,6 +158,34 @@ function createCheckpoint(checkpoint, channels, step, options) {
154
158
  };
155
159
  }
156
160
  /**
161
+ * Checkpoint metadata key, `true` when every DeltaChannel write on the
162
+ * checkpoint's branch carries a version. A delta channel with no version there
163
+ * was never written, so it needs no walk. Branches started before this key
164
+ * existed never get it: an older `updateState` stored a delta channel's first
165
+ * write without a version, and only the walk finds such a write.
166
+ */
167
+ const DELTA_WRITES_VERSIONED = "delta_writes_versioned";
168
+ function isDeltaWritesVersioned(metadata) {
169
+ return metadata?.[DELTA_WRITES_VERSIONED] === true;
170
+ }
171
+ /**
172
+ * Delta channels whose value at `checkpoint` the ancestor walk rebuilds.
173
+ *
174
+ * A {@link DeltaSnapshot} or a migrated plain value in `channel_values`
175
+ * resolves directly, so only a channel with nothing stored there needs the
176
+ * walk. With `versionedOnly`, a channel with no version was never written, so
177
+ * it is empty without one; a walk for it would find no snapshot to stop at and
178
+ * read every ancestor, every time the thread is loaded.
179
+ */
180
+ function deltaChannelsToReplay(specs, checkpoint, versionedOnly) {
181
+ const keys = [];
182
+ for (const k in specs) {
183
+ 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);
185
+ }
186
+ return keys;
187
+ }
188
+ /**
157
189
  * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}
158
190
  * whose value is absent from `channel_values` by replaying ancestor writes.
159
191
  *
@@ -163,17 +195,15 @@ function createCheckpoint(checkpoint, channels, step, options) {
163
195
  * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`
164
196
  * finds the nearest seed and accumulates the writes between it and the
165
197
  * target. All delta channels needing replay are batched into a single saver
166
- * call.
198
+ * call. Pass `deltaWritesVersioned` for a checkpoint whose metadata carries
199
+ * {@link DELTA_WRITES_VERSIONED}, so delta channels without a version are
200
+ * left empty instead of walked.
167
201
  */
168
202
  async function channelsFromCheckpoint(specs, checkpoint, options) {
169
203
  const channels = emptyChannels(specs, checkpoint);
170
204
  const { saver, config } = options ?? {};
171
205
  const filteredSpecs = getOnlyChannels(specs);
172
- const deltaKeys = [];
173
- for (const k in filteredSpecs) {
174
- if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;
175
- if (isDeltaChannel(filteredSpecs[k]) && !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) deltaKeys.push(k);
176
- }
206
+ const deltaKeys = deltaChannelsToReplay(filteredSpecs, checkpoint, options?.deltaWritesVersioned ?? false);
177
207
  const written = deltaKeys.filter((k) => checkpoint.channel_versions[k] !== void 0);
178
208
  if ((saver === void 0 || config === void 0) && written.length > 0) throw new Error(`DeltaChannel ${written.join(", ")} has history to replay but no checkpointer or config was passed to read it`);
179
209
  if (deltaKeys.length === 0 || saver === void 0 || config === void 0) return channels;
@@ -191,6 +221,6 @@ async function channelsFromCheckpoint(specs, checkpoint, options) {
191
221
  return channels;
192
222
  }
193
223
  //#endregion
194
- export { BaseChannel, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel };
224
+ export { BaseChannel, DELTA_WRITES_VERSIONED, channelsFromCheckpoint, createCheckpoint, deltaChannelsToSnapshot, emptyChannels, exitDeltaTaskId, getOnlyChannels, isBaseChannel, isDeltaChannel, isDeltaWritesVersioned };
195
225
 
196
226
  //# 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 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`. Pure predicate — no mutation.\n */\nexport function deltaChannelsToSnapshot(\n channels: Record<string, BaseChannel>,\n countersSinceDeltaSnapshot: Record<string, [number, number]>\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 (!isDeltaChannel(ch) || !ch.isAvailable()) continue;\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 * 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.\n */\nexport async function channelsFromCheckpoint<\n Cc extends Record<string, BaseChannel>,\n>(\n specs: Cc,\n checkpoint: ReadonlyCheckpoint,\n options?: { saver?: BaseCheckpointSaver; config?: RunnableConfig }\n): Promise<Cc> {\n const channels = emptyChannels(specs, checkpoint);\n const { saver, config } = options ?? {};\n\n const filteredSpecs = getOnlyChannels(specs);\n const deltaKeys: string[] = [];\n for (const k in filteredSpecs) {\n if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;\n if (\n isDeltaChannel(filteredSpecs[k]) &&\n !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)\n ) {\n deltaKeys.push(k);\n }\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 { 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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"}
@@ -488,7 +488,8 @@ var Pregel = class extends PartialRunnable {
488
488
  };
489
489
  const channels = await require_base.channelsFromCheckpoint(this.channels, saved.checkpoint, {
490
490
  saver,
491
- config: saved.config ?? config
491
+ config: saved.config ?? config,
492
+ deltaWritesVersioned: require_base.isDeltaWritesVersioned(saved.metadata)
492
493
  });
493
494
  if (saved.pendingWrites?.length) {
494
495
  const nullWrites = saved.pendingWrites.filter(([taskId, _]) => taskId === require_constants.NULL_TASK_ID).map(([_, channel, value]) => [String(channel), value]);
@@ -653,18 +654,22 @@ var Pregel = class extends PartialRunnable {
653
654
  ...checkpointMetadata
654
655
  };
655
656
  }
657
+ const deltaWritesVersioned = Object.values(this.channels).some(require_base.isDeltaChannel) && (saved === void 0 || require_base.isDeltaWritesVersioned(saved.metadata));
658
+ const versionedMetadata = deltaWritesVersioned ? { [require_base.DELTA_WRITES_VERSIONED]: true } : {};
656
659
  const { values, asNode } = updates[0];
657
660
  if (values == null && asNode === void 0) {
658
661
  if (updates.length > 1) throw new require_errors.InvalidUpdateError(`Cannot create empty checkpoint with multiple updates`);
659
662
  return require_index.patchCheckpointMap(await checkpointer.put(checkpointConfig, require_base.createCheckpoint(checkpoint, void 0, step), {
660
663
  source: "update",
661
664
  step: step + 1,
662
- parents: saved?.metadata?.parents ?? {}
665
+ parents: saved?.metadata?.parents ?? {},
666
+ ...versionedMetadata
663
667
  }, {}), saved ? saved.metadata : void 0);
664
668
  }
665
669
  const channels = await require_base.channelsFromCheckpoint(this.channels, checkpoint, {
666
670
  saver: checkpointer,
667
- config: saved?.config ?? checkpointConfig
671
+ config: saved?.config ?? checkpointConfig,
672
+ deltaWritesVersioned
668
673
  });
669
674
  if (values === null && asNode === "__end__") {
670
675
  if (updates.length > 1) throw new require_errors.InvalidUpdateError(`Cannot apply multiple updates when clearing state`);
@@ -691,11 +696,13 @@ var Pregel = class extends PartialRunnable {
691
696
  }
692
697
  require_algo._applyWrites(checkpoint, channels, Object.values(nextTasks), checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
693
698
  }
699
+ const { [require_base.DELTA_WRITES_VERSIONED]: _carried, ...endMetadata } = checkpointMetadata;
694
700
  return require_index.patchCheckpointMap(await checkpointer.put(checkpointConfig, require_base.createCheckpoint(checkpoint, channels, step), {
695
- ...checkpointMetadata,
701
+ ...endMetadata,
696
702
  source: "update",
697
703
  step: step + 1,
698
- parents: saved?.metadata?.parents ?? {}
704
+ parents: saved?.metadata?.parents ?? {},
705
+ ...versionedMetadata
699
706
  }, require_index.getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions)), saved ? saved.metadata : void 0);
700
707
  }
701
708
  if (asNode === "__copy__") {
@@ -710,7 +717,8 @@ var Pregel = class extends PartialRunnable {
710
717
  const nextConfig = await checkpointer.put(saved.parentConfig ?? require_utils.patchConfigurable(saved.config, { checkpoint_id: void 0 }), nextCheckpoint, {
711
718
  source: "fork",
712
719
  step: step + 1,
713
- parents: saved.metadata?.parents ?? {}
720
+ parents: saved.metadata?.parents ?? {},
721
+ ...versionedMetadata
714
722
  }, {});
715
723
  if (isCopyWithUpdates(values)) {
716
724
  const nextTasks = require_algo._prepareNextTasks(nextCheckpoint, saved.pendingWrites, this.nodes, channels, nextConfig, false, { step: step + 2 });
@@ -748,7 +756,8 @@ var Pregel = class extends PartialRunnable {
748
756
  const nextConfig = await checkpointer.put(checkpointConfig, require_base.createCheckpoint(checkpoint, channels, nextStep), {
749
757
  source: "input",
750
758
  step: nextStep,
751
- parents: saved?.metadata?.parents ?? {}
759
+ parents: saved?.metadata?.parents ?? {},
760
+ ...versionedMetadata
752
761
  }, require_index.getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions));
753
762
  await checkpointer.putWrites(nextConfig, inputWrites, (0, _langchain_langgraph_checkpoint.uuid5)(require_constants.INPUT, checkpoint.id));
754
763
  return require_index.patchCheckpointMap(nextConfig, saved ? saved.metadata : void 0);
@@ -760,11 +769,11 @@ var Pregel = class extends PartialRunnable {
760
769
  step: (saved.metadata?.step ?? -1) + 1
761
770
  });
762
771
  const nullWrites = (saved.pendingWrites ?? []).filter((w) => w[0] === require_constants.NULL_TASK_ID).map((w) => w.slice(1));
763
- if (nullWrites.length > 0) require_algo._applyWrites(saved.checkpoint, channels, [{
772
+ if (nullWrites.length > 0) require_algo._applyWrites(checkpoint, channels, [{
764
773
  name: require_constants.INPUT,
765
774
  writes: nullWrites,
766
775
  triggers: []
767
- }], void 0, this.triggerToNodes);
776
+ }], checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
768
777
  for (const [tid, k, v] of saved.pendingWrites) {
769
778
  if ([
770
779
  "__error__",
@@ -776,7 +785,7 @@ var Pregel = class extends PartialRunnable {
776
785
  const tasks = Object.values(nextTasks).filter((task) => {
777
786
  return task.writes.length > 0;
778
787
  });
779
- if (tasks.length > 0) require_algo._applyWrites(checkpoint, channels, tasks, void 0, this.triggerToNodes);
788
+ if (tasks.length > 0) require_algo._applyWrites(checkpoint, channels, tasks, checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
780
789
  }
781
790
  const nonNullVersion = Object.values(checkpoint.versions_seen).map((seenVersions) => {
782
791
  return Object.values(seenVersions);
@@ -813,7 +822,7 @@ var Pregel = class extends PartialRunnable {
813
822
  });
814
823
  }
815
824
  const tasks = [];
816
- for (const { asNode, values, taskId } of validUpdates) {
825
+ for (const [i, { asNode, values, taskId }] of validUpdates.entries()) {
817
826
  if (this.nodes[asNode] === void 0) throw new require_errors.InvalidUpdateError(`Node "${asNode.toString()}" does not exist`);
818
827
  const writers = this.nodes[asNode].getWriters();
819
828
  if (!writers.length) throw new require_errors.InvalidUpdateError(`No writers found for node "${asNode.toString()}"`);
@@ -823,7 +832,7 @@ var Pregel = class extends PartialRunnable {
823
832
  proc: writers.length > 1 ? _langchain_core_runnables.RunnableSequence.from(writers, { omitSequenceTags: true }) : writers[0],
824
833
  writes: [],
825
834
  triggers: [require_constants.INTERRUPT],
826
- id: taskId ?? (0, _langchain_langgraph_checkpoint.uuid5)("__interrupt__", checkpoint.id),
835
+ id: taskId ?? (0, _langchain_langgraph_checkpoint.uuid5)(i === 0 ? "__interrupt__" : `__interrupt__:${i}`, checkpoint.id),
827
836
  writers: []
828
837
  });
829
838
  }
@@ -842,11 +851,16 @@ var Pregel = class extends PartialRunnable {
842
851
  if (saved !== void 0 && channelWrites.length > 0) await checkpointer.putWrites(checkpointConfig, channelWrites, task.id);
843
852
  }
844
853
  require_algo._applyWrites(checkpoint, channels, tasks, checkpointer.getNextVersion.bind(checkpointer), this.triggerToNodes);
854
+ const channelsToSnapshot = /* @__PURE__ */ new Set();
855
+ if (saved === void 0) {
856
+ for (const [k, ch] of Object.entries(channels)) if (require_base.isDeltaChannel(ch) && checkpoint.channel_versions[k] !== void 0) channelsToSnapshot.add(k);
857
+ }
845
858
  const newVersions = require_index.getNewChannelVersions(checkpointPreviousVersions, checkpoint.channel_versions);
846
- const nextConfig = await checkpointer.put(checkpointConfig, require_base.createCheckpoint(checkpoint, channels, step + 1), {
859
+ const nextConfig = await checkpointer.put(checkpointConfig, require_base.createCheckpoint(checkpoint, channels, step + 1, { channelsToSnapshot }), {
847
860
  source: "update",
848
861
  step: step + 1,
849
- parents: saved?.metadata?.parents ?? {}
862
+ parents: saved?.metadata?.parents ?? {},
863
+ ...versionedMetadata
850
864
  }, newVersions);
851
865
  for (const task of tasks) {
852
866
  const pushWrites = task.writes.filter((w) => w[0] === require_constants.PUSH);