@langchain/langgraph-checkpoint 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/base.cjs CHANGED
@@ -12,7 +12,7 @@ function deepCopy(obj) {
12
12
  function emptyCheckpoint() {
13
13
  return {
14
14
  v: 4,
15
- id: require_id.uuid6(-2),
15
+ id: require_id.uuid6(0),
16
16
  ts: (/* @__PURE__ */ new Date()).toISOString(),
17
17
  channel_values: {},
18
18
  channel_versions: {},
@@ -40,6 +40,65 @@ var BaseCheckpointSaver = class {
40
40
  return value ? value.checkpoint : void 0;
41
41
  }
42
42
  /**
43
+ * Walk the parent chain returning per-channel writes + seed, used to
44
+ * reconstruct `DeltaChannel` state from `checkpoint_writes`.
45
+ *
46
+ * For each requested channel, walks ancestors of the checkpoint identified
47
+ * by `config` (following `parentConfig`) and accumulates the pending writes
48
+ * for that channel. The walk terminates per-channel at the nearest ancestor
49
+ * whose `channel_values[ch]` is populated; that value is returned as `seed`.
50
+ * If the walk reaches the root without finding a stored value, `seed` is
51
+ * omitted from that channel's entry — the consumer treats the absence as
52
+ * "start empty".
53
+ *
54
+ * Walks the parent chain (not `list({ before })`): for forked threads, only
55
+ * on-path ancestors contribute.
56
+ *
57
+ * The default implementation walks `getTuple` + `parentConfig` once for all
58
+ * channels — each ancestor visited once, not once per channel. Savers with
59
+ * direct storage access (e.g. `MemorySaver`) override for performance; the
60
+ * return contract is fixed here.
61
+ *
62
+ * @remarks Beta. The signature, return shape, and interaction with
63
+ * `DeltaSnapshot` blobs may change. Override at your own risk; the default
64
+ * implementation will continue to work against the public
65
+ * `BaseCheckpointSaver` contract.
66
+ *
67
+ * @param options.config Configuration identifying the target checkpoint.
68
+ * @param options.channels Channel names to walk for. Empty → empty mapping.
69
+ * @returns Per-channel {@link DeltaChannelHistory} for every requested name.
70
+ */
71
+ async getDeltaChannelHistory(options) {
72
+ const { config, channels } = options;
73
+ if (channels.length === 0) return {};
74
+ const collectedByCh = {};
75
+ const seedByCh = {};
76
+ const remaining = new Set(channels);
77
+ for (const ch of channels) collectedByCh[ch] = [];
78
+ let cursorConfig = (await this.getTuple(config))?.parentConfig;
79
+ while (cursorConfig != null && remaining.size > 0) {
80
+ const tup = await this.getTuple(cursorConfig);
81
+ if (tup === void 0) break;
82
+ if (tup.pendingWrites && tup.pendingWrites.length > 0) for (let i = tup.pendingWrites.length - 1; i >= 0; i -= 1) {
83
+ const write = tup.pendingWrites[i];
84
+ const ch = write[1];
85
+ if (remaining.has(ch)) collectedByCh[ch].push(write);
86
+ }
87
+ for (const ch of Array.from(remaining)) if (Object.prototype.hasOwnProperty.call(tup.checkpoint.channel_values, ch)) {
88
+ seedByCh[ch] = tup.checkpoint.channel_values[ch];
89
+ remaining.delete(ch);
90
+ }
91
+ cursorConfig = tup.parentConfig;
92
+ }
93
+ const result = {};
94
+ for (const ch of channels) {
95
+ const entry = { writes: collectedByCh[ch].slice().reverse() };
96
+ if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) entry.seed = seedByCh[ch];
97
+ result[ch] = entry;
98
+ }
99
+ return result;
100
+ }
101
+ /**
43
102
  * Generate the next version ID for a channel.
44
103
  *
45
104
  * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,
package/dist/base.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"base.cjs","names":["uuid6","JsonPlusSerializer","ERROR","SCHEDULED","INTERRUPT","RESUME"],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(-2),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":";;;;AAsDA,SAAgB,SAAY,KAAW;AACrC,KAAI,OAAO,QAAQ,YAAY,QAAQ,KACrC,QAAO;CAGT,MAAM,SAAS,MAAM,QAAQ,IAAI,GAAG,EAAE,GAAG,EAAE;AAE3C,MAAK,MAAM,OAAO,IAChB,KAAI,OAAO,UAAU,eAAe,KAAK,KAAK,IAAI,CAC/C,QAAwC,OAAO,SAC7C,IAAgC,KAClC;AAIL,QAAO;;;AAIT,SAAgB,kBAA8B;AAC5C,QAAO;EACL,GAAG;EACH,IAAIA,WAAAA,MAAM,GAAG;EACb,qBAAI,IAAI,MAAM,EAAC,aAAa;EAC5B,gBAAgB,EAAE;EAClB,kBAAkB,EAAE;EACpB,eAAe,EAAE;EAClB;;;AAIH,SAAgB,eAAe,YAA4C;AACzE,QAAO;EACL,GAAG,WAAW;EACd,IAAI,WAAW;EACf,IAAI,WAAW;EACf,gBAAgB,EAAE,GAAG,WAAW,gBAAgB;EAChD,kBAAkB,EAAE,GAAG,WAAW,kBAAkB;EACpD,eAAe,SAAS,WAAW,cAAc;EAClD;;AAkBH,IAAsB,sBAAtB,MAA8E;CAC5E,QAA4B,IAAIC,iBAAAA,oBAAoB;CAEpD,YAAY,OAA4B;AACtC,OAAK,QAAQ,SAAS,KAAK;;CAG7B,MAAM,IAAI,QAAyD;EACjE,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO;AACzC,SAAO,QAAQ,MAAM,aAAa,KAAA;;;;;;;;CAwCpC,eAAe,SAA2B;AACxC,MAAI,OAAO,YAAY,SACrB,OAAM,IAAI,MAAM,sDAAsD;AAExE,SACE,YAAY,KAAA,KAAa,OAAO,YAAY,WAAW,UAAU,IAAI;;;AAK3E,SAAgB,uBACd,GACA,GACQ;AACR,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SACxC,QAAO,KAAK,KAAK,IAAI,EAAE;AAGzB,QAAO,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC;;AAG3C,SAAgB,kBACd,GAAG,UACa;AAChB,QAAO,SAAS,QAAQ,KAAK,SAAS,QAAQ;AAC5C,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO,uBAAuB,KAAK,QAAQ,IAAI,IAAI,MAAM;GACzD;;;;;;;;;AAUJ,MAAa,iBAAyC;EACnDC,cAAAA,QAAQ;EACRC,cAAAA,YAAY;EACZC,cAAAA,YAAY;EACZC,cAAAA,SAAS;CACX;AAED,SAAgB,gBAAgB,QAAgC;AAC9D,QACE,OAAO,cAAc,iBAAiB,OAAO,cAAc,aAAa"}
1
+ {"version":3,"file":"base.cjs","names":["uuid6","JsonPlusSerializer","ERROR","SCHEDULED","INTERRUPT","RESUME"],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n for (let i = tup.pendingWrites.length - 1; i >= 0; i -= 1) {\n const write = tup.pendingWrites[i];\n const ch = write[1];\n if (remaining.has(ch)) collectedByCh[ch].push(write);\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
package/dist/base.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SerializerProtocol } from "./serde/base.cjs";
2
- import { CheckpointMetadata, CheckpointPendingWrite, PendingWrite } from "./types.cjs";
2
+ import { CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, PendingWrite } from "./types.cjs";
3
3
  import { RunnableConfig } from "@langchain/core/runnables";
4
4
 
5
5
  //#region src/base.d.ts
@@ -70,6 +70,39 @@ declare abstract class BaseCheckpointSaver<V extends string | number = number> {
70
70
  * @param threadId The thread ID whose checkpoints should be deleted.
71
71
  */
72
72
  abstract deleteThread(threadId: string): Promise<void>;
73
+ /**
74
+ * Walk the parent chain returning per-channel writes + seed, used to
75
+ * reconstruct `DeltaChannel` state from `checkpoint_writes`.
76
+ *
77
+ * For each requested channel, walks ancestors of the checkpoint identified
78
+ * by `config` (following `parentConfig`) and accumulates the pending writes
79
+ * for that channel. The walk terminates per-channel at the nearest ancestor
80
+ * whose `channel_values[ch]` is populated; that value is returned as `seed`.
81
+ * If the walk reaches the root without finding a stored value, `seed` is
82
+ * omitted from that channel's entry — the consumer treats the absence as
83
+ * "start empty".
84
+ *
85
+ * Walks the parent chain (not `list({ before })`): for forked threads, only
86
+ * on-path ancestors contribute.
87
+ *
88
+ * The default implementation walks `getTuple` + `parentConfig` once for all
89
+ * channels — each ancestor visited once, not once per channel. Savers with
90
+ * direct storage access (e.g. `MemorySaver`) override for performance; the
91
+ * return contract is fixed here.
92
+ *
93
+ * @remarks Beta. The signature, return shape, and interaction with
94
+ * `DeltaSnapshot` blobs may change. Override at your own risk; the default
95
+ * implementation will continue to work against the public
96
+ * `BaseCheckpointSaver` contract.
97
+ *
98
+ * @param options.config Configuration identifying the target checkpoint.
99
+ * @param options.channels Channel names to walk for. Empty → empty mapping.
100
+ * @returns Per-channel {@link DeltaChannelHistory} for every requested name.
101
+ */
102
+ getDeltaChannelHistory(options: {
103
+ config: RunnableConfig;
104
+ channels: string[];
105
+ }): Promise<Record<string, DeltaChannelHistory>>;
73
106
  /**
74
107
  * Generate the next version ID for a channel.
75
108
  *
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.cts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAYK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAId,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAvFM;;;EAAA,SA4FR,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EApG+C;;;;EAAA,SA0GzC,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzGhB;;;;;;EAiHzB,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;AAjIH;;;cA+Ia,cAAA,EAAgB,MAAA;AAAA,iBAOb,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
1
+ {"version":3,"file":"base.d.cts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAId,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAvFM;;;EAAA,SA4FR,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EApG+C;;;;EAAA,SA0GzC,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzGhB;;;;;;;;;;;;;;AAO3B;;;;;;;;;;AAmBA;;;;;EA8GQ,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;EArGc;;;;;AAW3C;EAkJE,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;;;;cAcU,cAAA,EAAgB,MAAA;AAAA,iBAOb,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
package/dist/base.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SerializerProtocol } from "./serde/base.js";
2
- import { CheckpointMetadata, CheckpointPendingWrite, PendingWrite } from "./types.js";
2
+ import { CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, PendingWrite } from "./types.js";
3
3
  import { RunnableConfig } from "@langchain/core/runnables";
4
4
 
5
5
  //#region src/base.d.ts
@@ -70,6 +70,39 @@ declare abstract class BaseCheckpointSaver<V extends string | number = number> {
70
70
  * @param threadId The thread ID whose checkpoints should be deleted.
71
71
  */
72
72
  abstract deleteThread(threadId: string): Promise<void>;
73
+ /**
74
+ * Walk the parent chain returning per-channel writes + seed, used to
75
+ * reconstruct `DeltaChannel` state from `checkpoint_writes`.
76
+ *
77
+ * For each requested channel, walks ancestors of the checkpoint identified
78
+ * by `config` (following `parentConfig`) and accumulates the pending writes
79
+ * for that channel. The walk terminates per-channel at the nearest ancestor
80
+ * whose `channel_values[ch]` is populated; that value is returned as `seed`.
81
+ * If the walk reaches the root without finding a stored value, `seed` is
82
+ * omitted from that channel's entry — the consumer treats the absence as
83
+ * "start empty".
84
+ *
85
+ * Walks the parent chain (not `list({ before })`): for forked threads, only
86
+ * on-path ancestors contribute.
87
+ *
88
+ * The default implementation walks `getTuple` + `parentConfig` once for all
89
+ * channels — each ancestor visited once, not once per channel. Savers with
90
+ * direct storage access (e.g. `MemorySaver`) override for performance; the
91
+ * return contract is fixed here.
92
+ *
93
+ * @remarks Beta. The signature, return shape, and interaction with
94
+ * `DeltaSnapshot` blobs may change. Override at your own risk; the default
95
+ * implementation will continue to work against the public
96
+ * `BaseCheckpointSaver` contract.
97
+ *
98
+ * @param options.config Configuration identifying the target checkpoint.
99
+ * @param options.channels Channel names to walk for. Empty → empty mapping.
100
+ * @returns Per-channel {@link DeltaChannelHistory} for every requested name.
101
+ */
102
+ getDeltaChannelHistory(options: {
103
+ config: RunnableConfig;
104
+ channels: string[];
105
+ }): Promise<Record<string, DeltaChannelHistory>>;
73
106
  /**
74
107
  * Generate the next version ID for a channel.
75
108
  *
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAYK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAId,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAvFM;;;EAAA,SA4FR,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EApG+C;;;;EAAA,SA0GzC,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzGhB;;;;;;EAiHzB,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;AAjIH;;;cA+Ia,cAAA,EAAgB,MAAA;AAAA,iBAOb,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
1
+ {"version":3,"file":"base.d.ts","names":[],"sources":["../src/base.ts"],"mappings":";;;;;;KAaK,cAAA;AAAA,KAEO,eAAA,GAAkB,MAAA,SAAe,cAAA;AAAA,UAE5B,UAAA;;;;EAOf,CAAA;EATyB;;;EAazB,EAAA;EAXe;;;EAef,EAAA;EAIgB;;;EAAhB,cAAA,EAAgB,MAAA,CAAO,CAAA;EAQD;;;EAJtB,gBAAA,EAAkB,MAAA,CAAO,CAAA,EAAG,cAAA;EAIb;;;EAAf,aAAA,EAAe,MAAA,CAAO,CAAA,EAAG,MAAA,CAAO,CAAA,EAAG,cAAA;AAAA;AAAA,UAGpB,kBAAA,SAA2B,QAAA,CAAS,UAAA;EAAA,SAC1C,cAAA,EAAgB,QAAA,CAAS,MAAA;EAAA,SACzB,gBAAA,EAAkB,QAAA,CAAS,MAAA,SAAe,cAAA;EAAA,SAC1C,aAAA,EAAe,QAAA,CACtB,MAAA,SAAe,QAAA,CAAS,MAAA,SAAe,cAAA;AAAA;AAAA,iBAI3B,QAAA,GAAA,CAAY,GAAA,EAAK,CAAA,GAAI,CAAA;;iBAmBrB,eAAA,CAAA,GAAmB,UAAA;;iBAYnB,cAAA,CAAe,UAAA,EAAY,kBAAA,GAAqB,UAAA;AAAA,UAW/C,eAAA;EACf,MAAA,EAAQ,cAAA;EACR,UAAA,EAAY,UAAA;EACZ,QAAA,GAAW,kBAAA;EACX,YAAA,GAAe,cAAA;EACf,aAAA,GAAgB,sBAAA;AAAA;AAAA,KAGN,qBAAA;EACV,KAAA;EACA,MAAA,GAAS,cAAA;EAET,MAAA,GAAS,MAAA;AAAA;AAAA,uBAGW,mBAAA;EACpB,KAAA,EAAO,kBAAA;EAEP,WAAA,CAAY,KAAA,GAAQ,kBAAA;EAId,GAAA,CAAI,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,UAAA;EAAA,SAKlC,QAAA,CACP,MAAA,EAAQ,cAAA,GACP,OAAA,CAAQ,eAAA;EAAA,SAEF,IAAA,CACP,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAAA,SAET,GAAA,CACP,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,EACV,WAAA,EAAa,eAAA,GACZ,OAAA,CAAQ,cAAA;EAvFM;;;EAAA,SA4FR,SAAA,CACP,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EApG+C;;;;EAAA,SA0GzC,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzGhB;;;;;;;;;;;;;;AAO3B;;;;;;;;;;AAmBA;;;;;EA8GQ,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;EArGc;;;;;AAW3C;EAkJE,cAAA,CAAe,OAAA,EAAS,CAAA,eAAgB,CAAA;AAAA;AAAA,iBAU1B,sBAAA,CACd,CAAA,EAAG,cAAA,EACH,CAAA,EAAG,cAAA;AAAA,iBASW,iBAAA,CAAA,GACX,QAAA,EAAU,cAAA,KACZ,cAAA;;;;;;;;cAcU,cAAA,EAAgB,MAAA;AAAA,iBAOb,eAAA,CAAgB,MAAA,EAAQ,cAAA"}
package/dist/base.js CHANGED
@@ -12,7 +12,7 @@ function deepCopy(obj) {
12
12
  function emptyCheckpoint() {
13
13
  return {
14
14
  v: 4,
15
- id: uuid6(-2),
15
+ id: uuid6(0),
16
16
  ts: (/* @__PURE__ */ new Date()).toISOString(),
17
17
  channel_values: {},
18
18
  channel_versions: {},
@@ -40,6 +40,65 @@ var BaseCheckpointSaver = class {
40
40
  return value ? value.checkpoint : void 0;
41
41
  }
42
42
  /**
43
+ * Walk the parent chain returning per-channel writes + seed, used to
44
+ * reconstruct `DeltaChannel` state from `checkpoint_writes`.
45
+ *
46
+ * For each requested channel, walks ancestors of the checkpoint identified
47
+ * by `config` (following `parentConfig`) and accumulates the pending writes
48
+ * for that channel. The walk terminates per-channel at the nearest ancestor
49
+ * whose `channel_values[ch]` is populated; that value is returned as `seed`.
50
+ * If the walk reaches the root without finding a stored value, `seed` is
51
+ * omitted from that channel's entry — the consumer treats the absence as
52
+ * "start empty".
53
+ *
54
+ * Walks the parent chain (not `list({ before })`): for forked threads, only
55
+ * on-path ancestors contribute.
56
+ *
57
+ * The default implementation walks `getTuple` + `parentConfig` once for all
58
+ * channels — each ancestor visited once, not once per channel. Savers with
59
+ * direct storage access (e.g. `MemorySaver`) override for performance; the
60
+ * return contract is fixed here.
61
+ *
62
+ * @remarks Beta. The signature, return shape, and interaction with
63
+ * `DeltaSnapshot` blobs may change. Override at your own risk; the default
64
+ * implementation will continue to work against the public
65
+ * `BaseCheckpointSaver` contract.
66
+ *
67
+ * @param options.config Configuration identifying the target checkpoint.
68
+ * @param options.channels Channel names to walk for. Empty → empty mapping.
69
+ * @returns Per-channel {@link DeltaChannelHistory} for every requested name.
70
+ */
71
+ async getDeltaChannelHistory(options) {
72
+ const { config, channels } = options;
73
+ if (channels.length === 0) return {};
74
+ const collectedByCh = {};
75
+ const seedByCh = {};
76
+ const remaining = new Set(channels);
77
+ for (const ch of channels) collectedByCh[ch] = [];
78
+ let cursorConfig = (await this.getTuple(config))?.parentConfig;
79
+ while (cursorConfig != null && remaining.size > 0) {
80
+ const tup = await this.getTuple(cursorConfig);
81
+ if (tup === void 0) break;
82
+ if (tup.pendingWrites && tup.pendingWrites.length > 0) for (let i = tup.pendingWrites.length - 1; i >= 0; i -= 1) {
83
+ const write = tup.pendingWrites[i];
84
+ const ch = write[1];
85
+ if (remaining.has(ch)) collectedByCh[ch].push(write);
86
+ }
87
+ for (const ch of Array.from(remaining)) if (Object.prototype.hasOwnProperty.call(tup.checkpoint.channel_values, ch)) {
88
+ seedByCh[ch] = tup.checkpoint.channel_values[ch];
89
+ remaining.delete(ch);
90
+ }
91
+ cursorConfig = tup.parentConfig;
92
+ }
93
+ const result = {};
94
+ for (const ch of channels) {
95
+ const entry = { writes: collectedByCh[ch].slice().reverse() };
96
+ if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) entry.seed = seedByCh[ch];
97
+ result[ch] = entry;
98
+ }
99
+ return result;
100
+ }
101
+ /**
43
102
  * Generate the next version ID for a channel.
44
103
  *
45
104
  * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,
package/dist/base.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":[],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(-2),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":";;;;AAsDA,SAAgB,SAAY,KAAW;AACrC,KAAI,OAAO,QAAQ,YAAY,QAAQ,KACrC,QAAO;CAGT,MAAM,SAAS,MAAM,QAAQ,IAAI,GAAG,EAAE,GAAG,EAAE;AAE3C,MAAK,MAAM,OAAO,IAChB,KAAI,OAAO,UAAU,eAAe,KAAK,KAAK,IAAI,CAC/C,QAAwC,OAAO,SAC7C,IAAgC,KAClC;AAIL,QAAO;;;AAIT,SAAgB,kBAA8B;AAC5C,QAAO;EACL,GAAG;EACH,IAAI,MAAM,GAAG;EACb,qBAAI,IAAI,MAAM,EAAC,aAAa;EAC5B,gBAAgB,EAAE;EAClB,kBAAkB,EAAE;EACpB,eAAe,EAAE;EAClB;;;AAIH,SAAgB,eAAe,YAA4C;AACzE,QAAO;EACL,GAAG,WAAW;EACd,IAAI,WAAW;EACf,IAAI,WAAW;EACf,gBAAgB,EAAE,GAAG,WAAW,gBAAgB;EAChD,kBAAkB,EAAE,GAAG,WAAW,kBAAkB;EACpD,eAAe,SAAS,WAAW,cAAc;EAClD;;AAkBH,IAAsB,sBAAtB,MAA8E;CAC5E,QAA4B,IAAI,oBAAoB;CAEpD,YAAY,OAA4B;AACtC,OAAK,QAAQ,SAAS,KAAK;;CAG7B,MAAM,IAAI,QAAyD;EACjE,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO;AACzC,SAAO,QAAQ,MAAM,aAAa,KAAA;;;;;;;;CAwCpC,eAAe,SAA2B;AACxC,MAAI,OAAO,YAAY,SACrB,OAAM,IAAI,MAAM,sDAAsD;AAExE,SACE,YAAY,KAAA,KAAa,OAAO,YAAY,WAAW,UAAU,IAAI;;;AAK3E,SAAgB,uBACd,GACA,GACQ;AACR,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SACxC,QAAO,KAAK,KAAK,IAAI,EAAE;AAGzB,QAAO,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC;;AAG3C,SAAgB,kBACd,GAAG,UACa;AAChB,QAAO,SAAS,QAAQ,KAAK,SAAS,QAAQ;AAC5C,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO,uBAAuB,KAAK,QAAQ,IAAI,IAAI,MAAM;GACzD;;;;;;;;;AAUJ,MAAa,iBAAyC;EACnD,QAAQ;EACR,YAAY;EACZ,YAAY;EACZ,SAAS;CACX;AAED,SAAgB,gBAAgB,QAAgC;AAC9D,QACE,OAAO,cAAc,iBAAiB,OAAO,cAAc,aAAa"}
1
+ {"version":3,"file":"base.js","names":[],"sources":["../src/base.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport { uuid6 } from \"./id.js\";\nimport type {\n PendingWrite,\n CheckpointPendingWrite,\n CheckpointMetadata,\n DeltaChannelHistory,\n} from \"./types.js\";\nimport { ERROR, INTERRUPT, RESUME, SCHEDULED } from \"./serde/types.js\";\nimport { JsonPlusSerializer } from \"./serde/jsonplus.js\";\n\n/** @inline */\ntype ChannelVersion = number | string;\n\nexport type ChannelVersions = Record<string, ChannelVersion>;\n\nexport interface Checkpoint<\n N extends string = string,\n C extends string = string,\n> {\n /**\n * The version of the checkpoint format. Currently 4\n */\n v: number;\n /**\n * Checkpoint ID {uuid6}\n */\n id: string;\n /**\n * Timestamp {new Date().toISOString()}\n */\n ts: string;\n /**\n * @default {}\n */\n channel_values: Record<C, unknown>;\n /**\n * @default {}\n */\n channel_versions: Record<C, ChannelVersion>;\n /**\n * @default {}\n */\n versions_seen: Record<N, Record<C, ChannelVersion>>;\n}\n\nexport interface ReadonlyCheckpoint extends Readonly<Checkpoint> {\n readonly channel_values: Readonly<Record<string, unknown>>;\n readonly channel_versions: Readonly<Record<string, ChannelVersion>>;\n readonly versions_seen: Readonly<\n Record<string, Readonly<Record<string, ChannelVersion>>>\n >;\n}\n\nexport function deepCopy<T>(obj: T): T {\n if (typeof obj !== \"object\" || obj === null) {\n return obj;\n }\n\n const newObj = Array.isArray(obj) ? [] : {};\n\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n (newObj as Record<PropertyKey, unknown>)[key] = deepCopy(\n (obj as Record<string, unknown>)[key]\n );\n }\n }\n\n return newObj as T;\n}\n\n/** @hidden */\nexport function emptyCheckpoint(): Checkpoint {\n return {\n v: 4,\n id: uuid6(0),\n ts: new Date().toISOString(),\n channel_values: {},\n channel_versions: {},\n versions_seen: {},\n };\n}\n\n/** @hidden */\nexport function copyCheckpoint(checkpoint: ReadonlyCheckpoint): Checkpoint {\n return {\n v: checkpoint.v,\n id: checkpoint.id,\n ts: checkpoint.ts,\n channel_values: { ...checkpoint.channel_values },\n channel_versions: { ...checkpoint.channel_versions },\n versions_seen: deepCopy(checkpoint.versions_seen),\n };\n}\n\nexport interface CheckpointTuple {\n config: RunnableConfig;\n checkpoint: Checkpoint;\n metadata?: CheckpointMetadata;\n parentConfig?: RunnableConfig;\n pendingWrites?: CheckpointPendingWrite[];\n}\n\nexport type CheckpointListOptions = {\n limit?: number;\n before?: RunnableConfig;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter?: Record<string, any>;\n};\n\nexport abstract class BaseCheckpointSaver<V extends string | number = number> {\n serde: SerializerProtocol = new JsonPlusSerializer();\n\n constructor(serde?: SerializerProtocol) {\n this.serde = serde || this.serde;\n }\n\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const value = await this.getTuple(config);\n return value ? value.checkpoint : undefined;\n }\n\n abstract getTuple(\n config: RunnableConfig\n ): Promise<CheckpointTuple | undefined>;\n\n abstract list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple>;\n\n abstract put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n newVersions: ChannelVersions\n ): Promise<RunnableConfig>;\n\n /**\n * Store intermediate writes linked to a checkpoint.\n */\n abstract putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void>;\n\n /**\n * Delete all checkpoints and writes associated with a specific thread ID.\n * @param threadId The thread ID whose checkpoints should be deleted.\n */\n abstract deleteThread(threadId: string): Promise<void>;\n\n /**\n * Walk the parent chain returning per-channel writes + seed, used to\n * reconstruct `DeltaChannel` state from `checkpoint_writes`.\n *\n * For each requested channel, walks ancestors of the checkpoint identified\n * by `config` (following `parentConfig`) and accumulates the pending writes\n * for that channel. The walk terminates per-channel at the nearest ancestor\n * whose `channel_values[ch]` is populated; that value is returned as `seed`.\n * If the walk reaches the root without finding a stored value, `seed` is\n * omitted from that channel's entry — the consumer treats the absence as\n * \"start empty\".\n *\n * Walks the parent chain (not `list({ before })`): for forked threads, only\n * on-path ancestors contribute.\n *\n * The default implementation walks `getTuple` + `parentConfig` once for all\n * channels — each ancestor visited once, not once per channel. Savers with\n * direct storage access (e.g. `MemorySaver`) override for performance; the\n * return contract is fixed here.\n *\n * @remarks Beta. The signature, return shape, and interaction with\n * `DeltaSnapshot` blobs may change. Override at your own risk; the default\n * implementation will continue to work against the public\n * `BaseCheckpointSaver` contract.\n *\n * @param options.config Configuration identifying the target checkpoint.\n * @param options.channels Channel names to walk for. Empty → empty mapping.\n * @returns Per-channel {@link DeltaChannelHistory} for every requested name.\n */\n async getDeltaChannelHistory(options: {\n config: RunnableConfig;\n channels: string[];\n }): Promise<Record<string, DeltaChannelHistory>> {\n const { config, channels } = options;\n if (channels.length === 0) return {};\n\n const collectedByCh: Record<string, CheckpointPendingWrite[]> = {};\n const seedByCh: Record<string, unknown> = {};\n const remaining = new Set(channels);\n for (const ch of channels) collectedByCh[ch] = [];\n\n const targetTuple = await this.getTuple(config);\n let cursorConfig: RunnableConfig | undefined = targetTuple?.parentConfig;\n\n while (cursorConfig != null && remaining.size > 0) {\n const tup: CheckpointTuple | undefined =\n await this.getTuple(cursorConfig);\n if (tup === undefined) break;\n if (tup.pendingWrites && tup.pendingWrites.length > 0) {\n for (let i = tup.pendingWrites.length - 1; i >= 0; i -= 1) {\n const write = tup.pendingWrites[i];\n const ch = write[1];\n if (remaining.has(ch)) collectedByCh[ch].push(write);\n }\n }\n for (const ch of Array.from(remaining)) {\n if (\n Object.prototype.hasOwnProperty.call(\n tup.checkpoint.channel_values,\n ch\n )\n ) {\n seedByCh[ch] = tup.checkpoint.channel_values[ch];\n remaining.delete(ch);\n }\n }\n cursorConfig = tup.parentConfig;\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entry: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entry.seed = seedByCh[ch];\n }\n result[ch] = entry;\n }\n return result;\n }\n\n /**\n * Generate the next version ID for a channel.\n *\n * Default is to use integer versions, incrementing by 1. If you override, you can use str/int/float versions,\n * as long as they are monotonically increasing.\n */\n getNextVersion(current: V | undefined): V {\n if (typeof current === \"string\") {\n throw new Error(\"Please override this method to use string versions.\");\n }\n return (\n current !== undefined && typeof current === \"number\" ? current + 1 : 1\n ) as V;\n }\n}\n\nexport function compareChannelVersions(\n a: ChannelVersion,\n b: ChannelVersion\n): number {\n if (typeof a === \"number\" && typeof b === \"number\") {\n return Math.sign(a - b);\n }\n\n return String(a).localeCompare(String(b));\n}\n\nexport function maxChannelVersion(\n ...versions: ChannelVersion[]\n): ChannelVersion {\n return versions.reduce((max, version, idx) => {\n if (idx === 0) return version;\n return compareChannelVersions(max, version) >= 0 ? max : version;\n });\n}\n\n/**\n * Mapping from error type to error index.\n * Regular writes just map to their index in the list of writes being saved.\n * Special writes (e.g. errors) map to negative indices, to avoid those writes from\n * conflicting with regular writes.\n * Each Checkpointer implementation should use this mapping in put_writes.\n */\nexport const WRITES_IDX_MAP: Record<string, number> = {\n [ERROR]: -1,\n [SCHEDULED]: -2,\n [INTERRUPT]: -3,\n [RESUME]: -4,\n};\n\nexport function getCheckpointId(config: RunnableConfig): string {\n return (\n config.configurable?.checkpoint_id || config.configurable?.thread_ts || \"\"\n );\n}\n"],"mappings":"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"}
package/dist/id.cjs CHANGED
@@ -1,7 +1,22 @@
1
1
  let uuid = require("uuid");
2
2
  //#region src/id.ts
3
+ let lastMsecs = 0;
4
+ let lastNsecs = 0;
3
5
  function uuid6(clockseq) {
4
- return (0, uuid.v6)({ clockseq });
6
+ let msecs = Date.now();
7
+ if (msecs <= lastMsecs) {
8
+ lastNsecs += 1;
9
+ if (lastNsecs >= 1e4) {
10
+ lastNsecs = 0;
11
+ msecs = lastMsecs + 1;
12
+ }
13
+ } else lastNsecs = 0;
14
+ lastMsecs = msecs;
15
+ return (0, uuid.v6)({
16
+ clockseq,
17
+ msecs,
18
+ nsecs: lastNsecs
19
+ });
5
20
  }
6
21
  function uuid5(name, namespace) {
7
22
  const namespaceBytes = namespace.replace(/-/g, "").match(/.{2}/g).map((byte) => parseInt(byte, 16));
package/dist/id.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.cjs","names":[],"sources":["../src/id.ts"],"sourcesContent":["import { v5, v6 } from \"uuid\";\n\nexport function uuid6(clockseq: number): string {\n return v6({ clockseq });\n}\n\n// Skip UUID validation check, since UUID6s\n// generated with negative clockseq are not\n// technically compliant, but still work.\n// See: https://github.com/uuidjs/uuid/issues/511\nexport function uuid5(name: string, namespace: string): string {\n const namespaceBytes = namespace\n .replace(/-/g, \"\")\n .match(/.{2}/g)!\n .map((byte) => parseInt(byte, 16));\n return v5(name, new Uint8Array(namespaceBytes));\n}\n"],"mappings":";;AAEA,SAAgB,MAAM,UAA0B;AAC9C,SAAA,GAAA,KAAA,IAAU,EAAE,UAAU,CAAC;;AAOzB,SAAgB,MAAM,MAAc,WAA2B;CAC7D,MAAM,iBAAiB,UACpB,QAAQ,MAAM,GAAG,CACjB,MAAM,QAAQ,CACd,KAAK,SAAS,SAAS,MAAM,GAAG,CAAC;AACpC,SAAA,GAAA,KAAA,IAAU,MAAM,IAAI,WAAW,eAAe,CAAC"}
1
+ {"version":3,"file":"id.cjs","names":[],"sources":["../src/id.ts"],"sourcesContent":["import { v5, v6 } from \"uuid\";\n\n// Monotonic timestamp state, mirroring uuid@10's internal v1 clock handling.\n// uuid@11+ dropped the sub-millisecond `nsecs` counter when an explicit\n// `clockseq` is provided, so successive `v6({ clockseq })` calls within the\n// same millisecond produce identical time bits and would only be ordered by\n// `clockseq`. Checkpoint IDs are sorted lexicographically, so we must keep the\n// time component strictly increasing across calls to preserve checkpoint\n// ordering regardless of the `clockseq` value passed in.\nlet lastMsecs = 0;\nlet lastNsecs = 0;\n\nexport function uuid6(clockseq: number): string {\n let msecs = Date.now();\n if (msecs <= lastMsecs) {\n // Clock did not advance; bump the 100ns-resolution counter so the\n // generated time bits remain strictly monotonic.\n lastNsecs += 1;\n if (lastNsecs >= 10000) {\n lastNsecs = 0;\n msecs = lastMsecs + 1;\n }\n } else {\n lastNsecs = 0;\n }\n lastMsecs = msecs;\n return v6({ clockseq, msecs, nsecs: lastNsecs });\n}\n\nexport function uuid5(name: string, namespace: string): string {\n const namespaceBytes = namespace\n .replace(/-/g, \"\")\n .match(/.{2}/g)!\n .map((byte) => parseInt(byte, 16));\n return v5(name, new Uint8Array(namespaceBytes));\n}\n"],"mappings":";;AASA,IAAI,YAAY;AAChB,IAAI,YAAY;AAEhB,SAAgB,MAAM,UAA0B;CAC9C,IAAI,QAAQ,KAAK,KAAK;AACtB,KAAI,SAAS,WAAW;AAGtB,eAAa;AACb,MAAI,aAAa,KAAO;AACtB,eAAY;AACZ,WAAQ,YAAY;;OAGtB,aAAY;AAEd,aAAY;AACZ,SAAA,GAAA,KAAA,IAAU;EAAE;EAAU;EAAO,OAAO;EAAW,CAAC;;AAGlD,SAAgB,MAAM,MAAc,WAA2B;CAC7D,MAAM,iBAAiB,UACpB,QAAQ,MAAM,GAAG,CACjB,MAAM,QAAQ,CACd,KAAK,SAAS,SAAS,MAAM,GAAG,CAAC;AACpC,SAAA,GAAA,KAAA,IAAU,MAAM,IAAI,WAAW,eAAe,CAAC"}
package/dist/id.d.cts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.cts","names":[],"sources":["../src/id.ts"],"mappings":";iBAEgB,KAAA,CAAM,QAAA;AAAA,iBAQN,KAAA,CAAM,IAAA,UAAc,SAAA"}
1
+ {"version":3,"file":"id.d.cts","names":[],"sources":["../src/id.ts"],"mappings":";iBAYgB,KAAA,CAAM,QAAA;AAAA,iBAiBN,KAAA,CAAM,IAAA,UAAc,SAAA"}
package/dist/id.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.d.ts","names":[],"sources":["../src/id.ts"],"mappings":";iBAEgB,KAAA,CAAM,QAAA;AAAA,iBAQN,KAAA,CAAM,IAAA,UAAc,SAAA"}
1
+ {"version":3,"file":"id.d.ts","names":[],"sources":["../src/id.ts"],"mappings":";iBAYgB,KAAA,CAAM,QAAA;AAAA,iBAiBN,KAAA,CAAM,IAAA,UAAc,SAAA"}
package/dist/id.js CHANGED
@@ -1,7 +1,22 @@
1
1
  import { v5, v6 } from "uuid";
2
2
  //#region src/id.ts
3
+ let lastMsecs = 0;
4
+ let lastNsecs = 0;
3
5
  function uuid6(clockseq) {
4
- return v6({ clockseq });
6
+ let msecs = Date.now();
7
+ if (msecs <= lastMsecs) {
8
+ lastNsecs += 1;
9
+ if (lastNsecs >= 1e4) {
10
+ lastNsecs = 0;
11
+ msecs = lastMsecs + 1;
12
+ }
13
+ } else lastNsecs = 0;
14
+ lastMsecs = msecs;
15
+ return v6({
16
+ clockseq,
17
+ msecs,
18
+ nsecs: lastNsecs
19
+ });
5
20
  }
6
21
  function uuid5(name, namespace) {
7
22
  const namespaceBytes = namespace.replace(/-/g, "").match(/.{2}/g).map((byte) => parseInt(byte, 16));
package/dist/id.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"id.js","names":[],"sources":["../src/id.ts"],"sourcesContent":["import { v5, v6 } from \"uuid\";\n\nexport function uuid6(clockseq: number): string {\n return v6({ clockseq });\n}\n\n// Skip UUID validation check, since UUID6s\n// generated with negative clockseq are not\n// technically compliant, but still work.\n// See: https://github.com/uuidjs/uuid/issues/511\nexport function uuid5(name: string, namespace: string): string {\n const namespaceBytes = namespace\n .replace(/-/g, \"\")\n .match(/.{2}/g)!\n .map((byte) => parseInt(byte, 16));\n return v5(name, new Uint8Array(namespaceBytes));\n}\n"],"mappings":";;AAEA,SAAgB,MAAM,UAA0B;AAC9C,QAAO,GAAG,EAAE,UAAU,CAAC;;AAOzB,SAAgB,MAAM,MAAc,WAA2B;CAC7D,MAAM,iBAAiB,UACpB,QAAQ,MAAM,GAAG,CACjB,MAAM,QAAQ,CACd,KAAK,SAAS,SAAS,MAAM,GAAG,CAAC;AACpC,QAAO,GAAG,MAAM,IAAI,WAAW,eAAe,CAAC"}
1
+ {"version":3,"file":"id.js","names":[],"sources":["../src/id.ts"],"sourcesContent":["import { v5, v6 } from \"uuid\";\n\n// Monotonic timestamp state, mirroring uuid@10's internal v1 clock handling.\n// uuid@11+ dropped the sub-millisecond `nsecs` counter when an explicit\n// `clockseq` is provided, so successive `v6({ clockseq })` calls within the\n// same millisecond produce identical time bits and would only be ordered by\n// `clockseq`. Checkpoint IDs are sorted lexicographically, so we must keep the\n// time component strictly increasing across calls to preserve checkpoint\n// ordering regardless of the `clockseq` value passed in.\nlet lastMsecs = 0;\nlet lastNsecs = 0;\n\nexport function uuid6(clockseq: number): string {\n let msecs = Date.now();\n if (msecs <= lastMsecs) {\n // Clock did not advance; bump the 100ns-resolution counter so the\n // generated time bits remain strictly monotonic.\n lastNsecs += 1;\n if (lastNsecs >= 10000) {\n lastNsecs = 0;\n msecs = lastMsecs + 1;\n }\n } else {\n lastNsecs = 0;\n }\n lastMsecs = msecs;\n return v6({ clockseq, msecs, nsecs: lastNsecs });\n}\n\nexport function uuid5(name: string, namespace: string): string {\n const namespaceBytes = namespace\n .replace(/-/g, \"\")\n .match(/.{2}/g)!\n .map((byte) => parseInt(byte, 16));\n return v5(name, new Uint8Array(namespaceBytes));\n}\n"],"mappings":";;AASA,IAAI,YAAY;AAChB,IAAI,YAAY;AAEhB,SAAgB,MAAM,UAA0B;CAC9C,IAAI,QAAQ,KAAK,KAAK;AACtB,KAAI,SAAS,WAAW;AAGtB,eAAa;AACb,MAAI,aAAa,KAAO;AACtB,eAAY;AACZ,WAAQ,YAAY;;OAGtB,aAAY;AAEd,aAAY;AACZ,QAAO,GAAG;EAAE;EAAU;EAAO,OAAO;EAAW,CAAC;;AAGlD,SAAgB,MAAM,MAAc,WAA2B;CAC7D,MAAM,iBAAiB,UACpB,QAAQ,MAAM,GAAG,CACjB,MAAM,QAAQ,CACd,KAAK,SAAS,SAAS,MAAM,GAAG,CAAC;AACpC,QAAO,GAAG,MAAM,IAAI,WAAW,eAAe,CAAC"}
package/dist/index.cjs CHANGED
@@ -13,6 +13,7 @@ exports.AsyncBatchedStore = require_batch.AsyncBatchedStore;
13
13
  exports.BaseCache = require_base$2.BaseCache;
14
14
  exports.BaseCheckpointSaver = require_base.BaseCheckpointSaver;
15
15
  exports.BaseStore = require_base$1.BaseStore;
16
+ exports.DeltaSnapshot = require_types.DeltaSnapshot;
16
17
  exports.ERROR = require_types.ERROR;
17
18
  exports.INTERRUPT = require_types.INTERRUPT;
18
19
  exports.InMemoryCache = require_memory$2.InMemoryCache;
@@ -30,6 +31,7 @@ exports.deepCopy = require_base.deepCopy;
30
31
  exports.emptyCheckpoint = require_base.emptyCheckpoint;
31
32
  exports.getCheckpointId = require_base.getCheckpointId;
32
33
  exports.getTextAtPath = require_base$1.getTextAtPath;
34
+ exports.isDeltaSnapshot = require_types.isDeltaSnapshot;
33
35
  exports.maxChannelVersion = require_base.maxChannelVersion;
34
36
  exports.tokenizePath = require_base$1.tokenizePath;
35
37
  exports.uuid5 = require_id.uuid5;
package/dist/index.d.cts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { SerializerProtocol } from "./serde/base.cjs";
2
- import { All, CheckpointMetadata, CheckpointPendingWrite, PendingWrite, PendingWriteValue } from "./types.cjs";
2
+ import { All, CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, PendingWrite, PendingWriteValue } from "./types.cjs";
3
3
  import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointTuple, ReadonlyCheckpoint, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, maxChannelVersion } from "./base.cjs";
4
4
  import { MemorySaver } from "./memory.cjs";
5
5
  import { uuid5, uuid6 } from "./id.cjs";
6
- import { ChannelProtocol, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS } from "./serde/types.cjs";
6
+ import { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.cjs";
7
7
  import { BaseStore, GetOperation, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PutOperation, SearchItem, SearchOperation, getTextAtPath, tokenizePath } from "./store/base.cjs";
8
8
  import { AsyncBatchedStore } from "./store/batch.cjs";
9
9
  import { InMemoryStore, MemoryStore } from "./store/memory.cjs";
10
10
  import { BaseCache, CacheFullKey, CacheNamespace } from "./cache/base.cjs";
11
11
  import { InMemoryCache } from "./cache/memory.cjs";
12
- export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, ERROR, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, maxChannelVersion, tokenizePath, uuid5, uuid6 };
12
+ export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, DeltaChannelHistory, DeltaSnapshot, ERROR, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { SerializerProtocol } from "./serde/base.js";
2
- import { All, CheckpointMetadata, CheckpointPendingWrite, PendingWrite, PendingWriteValue } from "./types.js";
2
+ import { All, CheckpointMetadata, CheckpointPendingWrite, DeltaChannelHistory, PendingWrite, PendingWriteValue } from "./types.js";
3
3
  import { BaseCheckpointSaver, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointTuple, ReadonlyCheckpoint, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, maxChannelVersion } from "./base.js";
4
4
  import { MemorySaver } from "./memory.js";
5
5
  import { uuid5, uuid6 } from "./id.js";
6
- import { ChannelProtocol, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS } from "./serde/types.js";
6
+ import { ChannelProtocol, DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, SendProtocol, TASKS, isDeltaSnapshot } from "./serde/types.js";
7
7
  import { BaseStore, GetOperation, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PutOperation, SearchItem, SearchOperation, getTextAtPath, tokenizePath } from "./store/base.js";
8
8
  import { AsyncBatchedStore } from "./store/batch.js";
9
9
  import { InMemoryStore, MemoryStore } from "./store/memory.js";
10
10
  import { BaseCache, CacheFullKey, CacheNamespace } from "./cache/base.js";
11
11
  import { InMemoryCache } from "./cache/memory.js";
12
- export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, ERROR, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, maxChannelVersion, tokenizePath, uuid5, uuid6 };
12
+ export { All, AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, CacheFullKey, CacheNamespace, ChannelProtocol, ChannelVersions, Checkpoint, CheckpointListOptions, CheckpointMetadata, CheckpointPendingWrite, CheckpointTuple, DeltaChannelHistory, DeltaSnapshot, ERROR, GetOperation, INTERRUPT, InMemoryCache, InMemoryStore, IndexConfig, InvalidNamespaceError, Item, ListNamespacesOperation, MatchCondition, MemorySaver, MemoryStore, NameSpacePath, NamespaceMatchType, Operation, OperationResults, PendingWrite, PendingWriteValue, PutOperation, RESUME, ReadonlyCheckpoint, SCHEDULED, SearchItem, SearchOperation, SendProtocol, SerializerProtocol, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { uuid5, uuid6 } from "./id.js";
2
- import { ERROR, INTERRUPT, RESUME, SCHEDULED, TASKS } from "./serde/types.js";
2
+ import { DeltaSnapshot, ERROR, INTERRUPT, RESUME, SCHEDULED, TASKS, isDeltaSnapshot } from "./serde/types.js";
3
3
  import { BaseCheckpointSaver, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, maxChannelVersion } from "./base.js";
4
4
  import { MemorySaver } from "./memory.js";
5
5
  import { BaseStore, InvalidNamespaceError, getTextAtPath, tokenizePath } from "./store/base.js";
@@ -8,4 +8,4 @@ import { InMemoryStore, MemoryStore } from "./store/memory.js";
8
8
  import { BaseCache } from "./cache/base.js";
9
9
  import { InMemoryCache } from "./cache/memory.js";
10
10
  import "./cache/index.js";
11
- export { AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, ERROR, INTERRUPT, InMemoryCache, InMemoryStore, InvalidNamespaceError, MemorySaver, MemoryStore, RESUME, SCHEDULED, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, maxChannelVersion, tokenizePath, uuid5, uuid6 };
11
+ export { AsyncBatchedStore, BaseCache, BaseCheckpointSaver, BaseStore, DeltaSnapshot, ERROR, INTERRUPT, InMemoryCache, InMemoryStore, InvalidNamespaceError, MemorySaver, MemoryStore, RESUME, SCHEDULED, TASKS, WRITES_IDX_MAP, compareChannelVersions, copyCheckpoint, deepCopy, emptyCheckpoint, getCheckpointId, getTextAtPath, isDeltaSnapshot, maxChannelVersion, tokenizePath, uuid5, uuid6 };
package/dist/memory.cjs CHANGED
@@ -255,6 +255,85 @@ var MemorySaver = class extends require_base.BaseCheckpointSaver {
255
255
  delete this.storage[threadId];
256
256
  for (const key of Object.keys(this.writes)) if (_parseKey(key).threadId === threadId) delete this.writes[key];
257
257
  }
258
+ /**
259
+ * Override: walk the parent chain ONCE for all requested channels using
260
+ * direct storage access.
261
+ *
262
+ * Each channel terminates independently at the nearest ancestor whose
263
+ * stored `channel_values[ch]` is populated. Other channels keep walking
264
+ * until they find their own terminator or hit the root.
265
+ *
266
+ * The seed value (whether a `DeltaSnapshot` or a plain pre-delta migration
267
+ * blob) is the value AT that ancestor, prior to its own pending writes that
268
+ * produce the child. Those on-path writes — including the ones stored on the
269
+ * terminating ancestor — are always collected and replayed on top of the
270
+ * seed, so a thread migrated from a pre-delta channel does not drop the
271
+ * writes saved under the migration boundary checkpoint.
272
+ *
273
+ * @remarks Beta. See {@link BaseCheckpointSaver.getDeltaChannelHistory}.
274
+ */
275
+ async getDeltaChannelHistory(options) {
276
+ const { config, channels } = options;
277
+ if (channels.length === 0) return {};
278
+ const threadId = config.configurable?.thread_id;
279
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
280
+ const checkpointId = require_base.getCheckpointId(config);
281
+ if (threadId !== void 0) assertSafeStorageKey("thread_id", threadId);
282
+ assertSafeStorageKey("checkpoint_ns", checkpointNs, { allowEmpty: true });
283
+ const nsStorage = this.storage[threadId]?.[checkpointNs] ?? {};
284
+ const chain = [];
285
+ let current = (checkpointId ? nsStorage[checkpointId] : void 0)?.[2];
286
+ while (current !== void 0) {
287
+ const entry = nsStorage[current];
288
+ if (entry === void 0) break;
289
+ chain.push(current);
290
+ current = entry[2];
291
+ }
292
+ const collectedByCh = {};
293
+ const seedByCh = {};
294
+ const remaining = new Set(channels);
295
+ for (const ch of channels) collectedByCh[ch] = [];
296
+ for (const cpId of chain) {
297
+ if (remaining.size === 0) break;
298
+ const entry = nsStorage[cpId];
299
+ const ckpt = entry !== void 0 ? await this.serde.loadsTyped("json", entry[0]) : void 0;
300
+ const blobValueByCh = {};
301
+ const terminatedHere = /* @__PURE__ */ new Set();
302
+ if (ckpt !== void 0) {
303
+ for (const ch of remaining) if (Object.prototype.hasOwnProperty.call(ckpt.channel_values, ch) && ckpt.channel_values[ch] !== void 0) {
304
+ blobValueByCh[ch] = ckpt.channel_values[ch];
305
+ terminatedHere.add(ch);
306
+ }
307
+ }
308
+ const stepWritesKey = _generateKey(threadId, checkpointNs, cpId);
309
+ const stepWrites = Object.entries(this.writes[stepWritesKey] ?? {});
310
+ stepWrites.sort(([a], [b]) => {
311
+ const [aTask, aIdx] = a.split(",");
312
+ const [bTask, bIdx] = b.split(",");
313
+ if (aTask !== bTask) return aTask < bTask ? 1 : -1;
314
+ return Number(bIdx) - Number(aIdx);
315
+ });
316
+ for (const [, [tid, ch, serialized]] of stepWrites) {
317
+ if (!remaining.has(ch)) continue;
318
+ collectedByCh[ch].push([
319
+ tid,
320
+ ch,
321
+ await this.serde.loadsTyped("json", serialized)
322
+ ]);
323
+ }
324
+ for (const ch of terminatedHere) {
325
+ seedByCh[ch] = blobValueByCh[ch];
326
+ remaining.delete(ch);
327
+ }
328
+ }
329
+ const result = {};
330
+ for (const ch of channels) {
331
+ const entryH = { writes: collectedByCh[ch].slice().reverse() };
332
+ if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) entryH.seed = seedByCh[ch];
333
+ result[ch] = entryH;
334
+ }
335
+ return result;
336
+ }
258
337
  };
259
338
  //#endregion
260
339
  exports.MemorySaver = MemorySaver;