@langchain/langgraph-checkpoint 1.0.4 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"memory.cjs","names":["BaseCheckpointSaver","TASKS","maxChannelVersion","getCheckpointId","copyCheckpoint","WRITES_IDX_MAP"],"sources":["../src/memory.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n Checkpoint,\n CheckpointListOptions,\n CheckpointTuple,\n copyCheckpoint,\n getCheckpointId,\n maxChannelVersion,\n WRITES_IDX_MAP,\n} from \"./base.js\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport {\n CheckpointMetadata,\n CheckpointPendingWrite,\n PendingWrite,\n} from \"./types.js\";\nimport { TASKS } from \"./serde/types.js\";\n\n/**\n * Keys that, when written into a plain JavaScript object via bracket\n * notation, traverse the prototype chain and mutate `Object.prototype`\n * (or the constructor) instead of creating a new own property. Any of\n * the three reaches `Object.prototype` and pollutes every object in\n * the running process. CWE-1321 (Prototype Pollution).\n */\nconst POLLUTION_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/**\n * Asserts that a value sourced from {@link RunnableConfig.configurable} (or\n * any other caller-influenced position) is safe to use as a property key\n * on the in-memory checkpoint store.\n *\n * `MemorySaver` keeps state in two nested plain objects (`storage` and\n * `writes`) and writes to them with bracket notation:\n *\n * this.storage[threadId][checkpointNamespace][checkpoint.id] = ...\n *\n * Without this guard a `threadId` of `\"__proto__\"` (or `\"constructor\"`)\n * resolves through the prototype chain, and the subsequent assignment\n * mutates `Object.prototype`. From that point every plain object in the\n * process inherits the injected property: `for...in` loops over unrelated\n * objects iterate it, framework code that does `if (obj[x])` short-circuits\n * unexpectedly, and downstream serializers may emit it. In a Node.js\n * server this is a stepping stone to remote code execution.\n *\n * `MemorySaver` is the default saver used by every quickstart, every\n * tutorial, and most test fixtures, so this guard runs in the hot path\n * for the most common LangGraph configuration.\n *\n * @param field Name of the configurable field, used in the error message.\n * @param value Value to validate. Must be a non-empty string that is not\n * one of the three prototype-pollution keys.\n * @param options.allowEmpty When true the empty string is accepted, used\n * for the documented empty `checkpoint_ns`\n * default; otherwise an empty string is\n * rejected the same way as a non-string.\n */\nfunction assertSafeStorageKey(\n field: string,\n value: unknown,\n options: { allowEmpty?: boolean } = {}\n): asserts value is string {\n const { allowEmpty = false } = options;\n if (typeof value !== \"string\") {\n const observed =\n value === null\n ? \"null\"\n : value === undefined\n ? \"undefined\"\n : Array.isArray(value)\n ? \"array\"\n : typeof value;\n throw new Error(\n `Invalid configurable value for key \"${field}\": expected a string identifier (got ${observed}). This guard protects MemorySaver from prototype pollution.`\n );\n }\n if (!allowEmpty && value === \"\") {\n throw new Error(\n `Invalid configurable value for key \"${field}\": empty string is not permitted as an in-memory storage key.`\n );\n }\n if (POLLUTION_KEYS.has(value)) {\n throw new Error(\n `Invalid configurable value for key \"${field}\": value \"${value}\" is reserved (would mutate Object.prototype). This guard protects MemorySaver from prototype pollution.`\n );\n }\n}\n\nfunction _generateKey(\n threadId: string,\n checkpointNamespace: string,\n checkpointId: string\n) {\n return JSON.stringify([threadId, checkpointNamespace, checkpointId]);\n}\n\nfunction _parseKey(key: string) {\n const [threadId, checkpointNamespace, checkpointId] = JSON.parse(key);\n return { threadId, checkpointNamespace, checkpointId };\n}\n\nexport class MemorySaver extends BaseCheckpointSaver {\n // thread ID -> checkpoint namespace -> checkpoint ID -> checkpoint mapping\n //\n // Defense in depth against prototype pollution: the backing\n // objects (and every nested level created below) use a null prototype, so\n // even if a malicious key bypassed `assertSafeStorageKey` it could not reach\n // `Object.prototype`. The guard remains the primary control; this is the\n // structural safety net.\n storage: Record<\n string,\n Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>\n > = Object.create(null);\n\n writes: Record<string, Record<string, [string, string, Uint8Array]>> =\n Object.create(null);\n\n constructor(serde?: SerializerProtocol) {\n super(serde);\n }\n\n /** @internal */\n async _migratePendingSends(\n mutableCheckpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ) {\n const deseriablizableCheckpoint = mutableCheckpoint;\n const parentKey = _generateKey(threadId, checkpointNs, parentCheckpointId);\n\n const pendingSends = await Promise.all(\n Object.values(this.writes[parentKey] ?? {})\n .filter(([_taskId, channel]) => channel === TASKS)\n .map(\n async ([_taskId, _channel, writes]) =>\n await this.serde.loadsTyped(\"json\", writes)\n )\n );\n\n deseriablizableCheckpoint.channel_values ??= {};\n deseriablizableCheckpoint.channel_values[TASKS] = pendingSends;\n\n deseriablizableCheckpoint.channel_versions ??= {};\n deseriablizableCheckpoint.channel_versions[TASKS] =\n Object.keys(deseriablizableCheckpoint.channel_versions).length > 0\n ? maxChannelVersion(\n ...Object.values(deseriablizableCheckpoint.channel_versions)\n )\n : this.getNextVersion(undefined);\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n let checkpoint_id = getCheckpointId(config);\n\n // Defense in depth: every public entry that mutates state already\n // validates these, but read paths must not return data sourced from\n // prototype-chain lookups when an attacker passes the magic keys.\n // `checkpoint_id` is intentionally allowed to be empty / undefined\n // here because the downstream `if (checkpoint_id)` branch treats\n // both as \"fetch the latest checkpoint\" rather than as a lookup key.\n if (thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", thread_id);\n }\n assertSafeStorageKey(\"checkpoint_ns\", checkpoint_ns, { allowEmpty: true });\n if (checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", checkpoint_id);\n }\n\n if (checkpoint_id) {\n const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];\n if (saved !== undefined) {\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config,\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n } else {\n const checkpoints = this.storage[thread_id]?.[checkpoint_ns];\n if (checkpoints !== undefined) {\n // eslint-disable-next-line prefer-destructuring\n checkpoint_id = Object.keys(checkpoints).sort((a, b) =>\n b.localeCompare(a)\n )[0];\n const saved = checkpoints[checkpoint_id];\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id,\n checkpoint_id,\n checkpoint_ns,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n }\n\n return undefined;\n }\n\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n // eslint-disable-next-line prefer-const\n let { before, limit, filter } = options ?? {};\n if (config.configurable?.thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", config.configurable.thread_id);\n }\n if (config.configurable?.checkpoint_ns !== undefined) {\n assertSafeStorageKey(\"checkpoint_ns\", config.configurable.checkpoint_ns, {\n allowEmpty: true,\n });\n }\n if (config.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", config.configurable.checkpoint_id);\n }\n if (before?.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", before.configurable.checkpoint_id);\n }\n const threadIds = config.configurable?.thread_id\n ? [config.configurable?.thread_id]\n : Object.keys(this.storage);\n const configCheckpointNamespace = config.configurable?.checkpoint_ns;\n const configCheckpointId = config.configurable?.checkpoint_id;\n\n for (const threadId of threadIds) {\n for (const checkpointNamespace of Object.keys(\n this.storage[threadId] ?? {}\n )) {\n if (\n configCheckpointNamespace !== undefined &&\n checkpointNamespace !== configCheckpointNamespace\n ) {\n continue;\n }\n const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};\n const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) =>\n b[0].localeCompare(a[0])\n );\n\n for (const [\n checkpointId,\n [checkpoint, metadataStr, parentCheckpointId],\n ] of sortedCheckpoints) {\n // Filter by checkpoint ID from config\n if (configCheckpointId && checkpointId !== configCheckpointId) {\n continue;\n }\n\n // Filter by checkpoint ID from before config\n if (\n before &&\n before.configurable?.checkpoint_id &&\n checkpointId >= before.configurable.checkpoint_id\n ) {\n continue;\n }\n\n // Parse metadata\n const metadata = (await this.serde.loadsTyped(\n \"json\",\n metadataStr\n )) as CheckpointMetadata;\n\n if (\n filter &&\n !Object.entries(filter).every(\n ([key, value]) =>\n (metadata as unknown as Record<string, unknown>)[key] === value\n )\n ) {\n continue;\n }\n\n // Limit search results\n if (limit !== undefined) {\n if (limit <= 0) break;\n limit -= 1;\n }\n\n const key = _generateKey(threadId, checkpointNamespace, checkpointId);\n const writes = Object.values(this.writes[key] || {});\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n writes.map(async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n })\n );\n\n const deserializedCheckpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (\n deserializedCheckpoint.v < 4 &&\n parentCheckpointId !== undefined\n ) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n threadId,\n checkpointNamespace,\n parentCheckpointId\n );\n }\n\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpointId,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n yield checkpointTuple;\n }\n }\n }\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns ?? \"\";\n if (threadId === undefined) {\n throw new Error(\n `Failed to put checkpoint. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpoint.id);\n\n if (!this.storage[threadId]) {\n this.storage[threadId] = Object.create(null);\n }\n if (!this.storage[threadId][checkpointNamespace]) {\n this.storage[threadId][checkpointNamespace] = Object.create(null);\n }\n\n const [[, serializedCheckpoint], [, serializedMetadata]] =\n await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n this.storage[threadId][checkpointNamespace][checkpoint.id] = [\n serializedCheckpoint,\n serializedMetadata,\n config.configurable?.checkpoint_id, // parent\n ];\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpoint.id,\n },\n };\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns;\n const checkpointId = config.configurable?.checkpoint_id;\n if (threadId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n if (checkpointId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"checkpoint_id\" field in its \"configurable\" property.`\n );\n }\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpointId);\n assertSafeStorageKey(\"task_id\", taskId);\n const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);\n const outerWrites_ = this.writes[outerKey];\n if (this.writes[outerKey] === undefined) {\n this.writes[outerKey] = Object.create(null);\n }\n\n await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const [, serializedValue] = await this.serde.dumpsTyped(value);\n const innerKey: [string, number] = [\n taskId,\n WRITES_IDX_MAP[channel] || idx,\n ];\n const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;\n if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {\n return;\n }\n this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];\n })\n );\n }\n\n async deleteThread(threadId: string): Promise<void> {\n assertSafeStorageKey(\"thread_id\", threadId);\n delete this.storage[threadId];\n for (const key of Object.keys(this.writes)) {\n if (_parseKey(key).threadId === threadId) delete this.writes[key];\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory.cjs","names":["BaseCheckpointSaver","TASKS","maxChannelVersion","getCheckpointId","copyCheckpoint","WRITES_IDX_MAP"],"sources":["../src/memory.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n Checkpoint,\n CheckpointListOptions,\n CheckpointTuple,\n copyCheckpoint,\n getCheckpointId,\n maxChannelVersion,\n WRITES_IDX_MAP,\n} from \"./base.js\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport {\n CheckpointMetadata,\n CheckpointPendingWrite,\n DeltaChannelHistory,\n PendingWrite,\n} from \"./types.js\";\nimport { TASKS } from \"./serde/types.js\";\n\n/**\n * Keys that, when written into a plain JavaScript object via bracket\n * notation, traverse the prototype chain and mutate `Object.prototype`\n * (or the constructor) instead of creating a new own property. Any of\n * the three reaches `Object.prototype` and pollutes every object in\n * the running process. CWE-1321 (Prototype Pollution).\n */\nconst POLLUTION_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/**\n * Asserts that a value sourced from {@link RunnableConfig.configurable} (or\n * any other caller-influenced position) is safe to use as a property key\n * on the in-memory checkpoint store.\n *\n * `MemorySaver` keeps state in two nested plain objects (`storage` and\n * `writes`) and writes to them with bracket notation:\n *\n * this.storage[threadId][checkpointNamespace][checkpoint.id] = ...\n *\n * Without this guard a `threadId` of `\"__proto__\"` (or `\"constructor\"`)\n * resolves through the prototype chain, and the subsequent assignment\n * mutates `Object.prototype`. From that point every plain object in the\n * process inherits the injected property: `for...in` loops over unrelated\n * objects iterate it, framework code that does `if (obj[x])` short-circuits\n * unexpectedly, and downstream serializers may emit it. In a Node.js\n * server this is a stepping stone to remote code execution.\n *\n * `MemorySaver` is the default saver used by every quickstart, every\n * tutorial, and most test fixtures, so this guard runs in the hot path\n * for the most common LangGraph configuration.\n *\n * @param field Name of the configurable field, used in the error message.\n * @param value Value to validate. Must be a non-empty string that is not\n * one of the three prototype-pollution keys.\n * @param options.allowEmpty When true the empty string is accepted, used\n * for the documented empty `checkpoint_ns`\n * default; otherwise an empty string is\n * rejected the same way as a non-string.\n */\nfunction assertSafeStorageKey(\n field: string,\n value: unknown,\n options: { allowEmpty?: boolean } = {}\n): asserts value is string {\n const { allowEmpty = false } = options;\n if (typeof value !== \"string\") {\n const observed =\n value === null\n ? \"null\"\n : value === undefined\n ? \"undefined\"\n : Array.isArray(value)\n ? \"array\"\n : typeof value;\n throw new Error(\n `Invalid configurable value for key \"${field}\": expected a string identifier (got ${observed}). This guard protects MemorySaver from prototype pollution.`\n );\n }\n if (!allowEmpty && value === \"\") {\n throw new Error(\n `Invalid configurable value for key \"${field}\": empty string is not permitted as an in-memory storage key.`\n );\n }\n if (POLLUTION_KEYS.has(value)) {\n throw new Error(\n `Invalid configurable value for key \"${field}\": value \"${value}\" is reserved (would mutate Object.prototype). This guard protects MemorySaver from prototype pollution.`\n );\n }\n}\n\nfunction _generateKey(\n threadId: string,\n checkpointNamespace: string,\n checkpointId: string\n) {\n return JSON.stringify([threadId, checkpointNamespace, checkpointId]);\n}\n\nfunction _parseKey(key: string) {\n const [threadId, checkpointNamespace, checkpointId] = JSON.parse(key);\n return { threadId, checkpointNamespace, checkpointId };\n}\n\nexport class MemorySaver extends BaseCheckpointSaver {\n // thread ID -> checkpoint namespace -> checkpoint ID -> checkpoint mapping\n //\n // Defense in depth against prototype pollution: the backing\n // objects (and every nested level created below) use a null prototype, so\n // even if a malicious key bypassed `assertSafeStorageKey` it could not reach\n // `Object.prototype`. The guard remains the primary control; this is the\n // structural safety net.\n storage: Record<\n string,\n Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>\n > = Object.create(null);\n\n writes: Record<string, Record<string, [string, string, Uint8Array]>> =\n Object.create(null);\n\n constructor(serde?: SerializerProtocol) {\n super(serde);\n }\n\n /** @internal */\n async _migratePendingSends(\n mutableCheckpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ) {\n const deseriablizableCheckpoint = mutableCheckpoint;\n const parentKey = _generateKey(threadId, checkpointNs, parentCheckpointId);\n\n const pendingSends = await Promise.all(\n Object.values(this.writes[parentKey] ?? {})\n .filter(([_taskId, channel]) => channel === TASKS)\n .map(\n async ([_taskId, _channel, writes]) =>\n await this.serde.loadsTyped(\"json\", writes)\n )\n );\n\n deseriablizableCheckpoint.channel_values ??= {};\n deseriablizableCheckpoint.channel_values[TASKS] = pendingSends;\n\n deseriablizableCheckpoint.channel_versions ??= {};\n deseriablizableCheckpoint.channel_versions[TASKS] =\n Object.keys(deseriablizableCheckpoint.channel_versions).length > 0\n ? maxChannelVersion(\n ...Object.values(deseriablizableCheckpoint.channel_versions)\n )\n : this.getNextVersion(undefined);\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n let checkpoint_id = getCheckpointId(config);\n\n // Defense in depth: every public entry that mutates state already\n // validates these, but read paths must not return data sourced from\n // prototype-chain lookups when an attacker passes the magic keys.\n // `checkpoint_id` is intentionally allowed to be empty / undefined\n // here because the downstream `if (checkpoint_id)` branch treats\n // both as \"fetch the latest checkpoint\" rather than as a lookup key.\n if (thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", thread_id);\n }\n assertSafeStorageKey(\"checkpoint_ns\", checkpoint_ns, { allowEmpty: true });\n if (checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", checkpoint_id);\n }\n\n if (checkpoint_id) {\n const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];\n if (saved !== undefined) {\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config,\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n } else {\n const checkpoints = this.storage[thread_id]?.[checkpoint_ns];\n if (checkpoints !== undefined) {\n // eslint-disable-next-line prefer-destructuring\n checkpoint_id = Object.keys(checkpoints).sort((a, b) =>\n b.localeCompare(a)\n )[0];\n const saved = checkpoints[checkpoint_id];\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id,\n checkpoint_id,\n checkpoint_ns,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n }\n\n return undefined;\n }\n\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n // eslint-disable-next-line prefer-const\n let { before, limit, filter } = options ?? {};\n if (config.configurable?.thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", config.configurable.thread_id);\n }\n if (config.configurable?.checkpoint_ns !== undefined) {\n assertSafeStorageKey(\"checkpoint_ns\", config.configurable.checkpoint_ns, {\n allowEmpty: true,\n });\n }\n if (config.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", config.configurable.checkpoint_id);\n }\n if (before?.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", before.configurable.checkpoint_id);\n }\n const threadIds = config.configurable?.thread_id\n ? [config.configurable?.thread_id]\n : Object.keys(this.storage);\n const configCheckpointNamespace = config.configurable?.checkpoint_ns;\n const configCheckpointId = config.configurable?.checkpoint_id;\n\n for (const threadId of threadIds) {\n for (const checkpointNamespace of Object.keys(\n this.storage[threadId] ?? {}\n )) {\n if (\n configCheckpointNamespace !== undefined &&\n checkpointNamespace !== configCheckpointNamespace\n ) {\n continue;\n }\n const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};\n const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) =>\n b[0].localeCompare(a[0])\n );\n\n for (const [\n checkpointId,\n [checkpoint, metadataStr, parentCheckpointId],\n ] of sortedCheckpoints) {\n // Filter by checkpoint ID from config\n if (configCheckpointId && checkpointId !== configCheckpointId) {\n continue;\n }\n\n // Filter by checkpoint ID from before config\n if (\n before &&\n before.configurable?.checkpoint_id &&\n checkpointId >= before.configurable.checkpoint_id\n ) {\n continue;\n }\n\n // Parse metadata\n const metadata = (await this.serde.loadsTyped(\n \"json\",\n metadataStr\n )) as CheckpointMetadata;\n\n if (\n filter &&\n !Object.entries(filter).every(\n ([key, value]) =>\n (metadata as unknown as Record<string, unknown>)[key] === value\n )\n ) {\n continue;\n }\n\n // Limit search results\n if (limit !== undefined) {\n if (limit <= 0) break;\n limit -= 1;\n }\n\n const key = _generateKey(threadId, checkpointNamespace, checkpointId);\n const writes = Object.values(this.writes[key] || {});\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n writes.map(async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n })\n );\n\n const deserializedCheckpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (\n deserializedCheckpoint.v < 4 &&\n parentCheckpointId !== undefined\n ) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n threadId,\n checkpointNamespace,\n parentCheckpointId\n );\n }\n\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpointId,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n yield checkpointTuple;\n }\n }\n }\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns ?? \"\";\n if (threadId === undefined) {\n throw new Error(\n `Failed to put checkpoint. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpoint.id);\n\n if (!this.storage[threadId]) {\n this.storage[threadId] = Object.create(null);\n }\n if (!this.storage[threadId][checkpointNamespace]) {\n this.storage[threadId][checkpointNamespace] = Object.create(null);\n }\n\n const [[, serializedCheckpoint], [, serializedMetadata]] =\n await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n this.storage[threadId][checkpointNamespace][checkpoint.id] = [\n serializedCheckpoint,\n serializedMetadata,\n config.configurable?.checkpoint_id, // parent\n ];\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpoint.id,\n },\n };\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns;\n const checkpointId = config.configurable?.checkpoint_id;\n if (threadId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n if (checkpointId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"checkpoint_id\" field in its \"configurable\" property.`\n );\n }\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpointId);\n assertSafeStorageKey(\"task_id\", taskId);\n const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);\n const outerWrites_ = this.writes[outerKey];\n if (this.writes[outerKey] === undefined) {\n this.writes[outerKey] = Object.create(null);\n }\n\n await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const [, serializedValue] = await this.serde.dumpsTyped(value);\n const innerKey: [string, number] = [\n taskId,\n WRITES_IDX_MAP[channel] || idx,\n ];\n const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;\n if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {\n return;\n }\n this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];\n })\n );\n }\n\n async deleteThread(threadId: string): Promise<void> {\n assertSafeStorageKey(\"thread_id\", threadId);\n delete this.storage[threadId];\n for (const key of Object.keys(this.writes)) {\n if (_parseKey(key).threadId === threadId) delete this.writes[key];\n }\n }\n\n /**\n * Override: walk the parent chain ONCE for all requested channels using\n * direct storage access.\n *\n * Each channel terminates independently at the nearest ancestor whose\n * stored `channel_values[ch]` is populated. Other channels keep walking\n * until they find their own terminator or hit the root.\n *\n * The seed value (whether a `DeltaSnapshot` or a plain pre-delta migration\n * blob) is the value AT that ancestor, prior to its own pending writes that\n * produce the child. Those on-path writes — including the ones stored on the\n * terminating ancestor — are always collected and replayed on top of the\n * seed, so a thread migrated from a pre-delta channel does not drop the\n * writes saved under the migration boundary checkpoint.\n *\n * @remarks Beta. See {@link BaseCheckpointSaver.getDeltaChannelHistory}.\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 threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = getCheckpointId(config);\n\n if (threadId !== undefined) assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNs, { allowEmpty: true });\n\n const nsStorage = this.storage[threadId]?.[checkpointNs] ?? {};\n\n // Build the parent chain starting at the target's parent (the target's\n // own pending writes are for the next super-step and excluded).\n const chain: string[] = [];\n const targetEntry = checkpointId ? nsStorage[checkpointId] : undefined;\n let current: string | undefined = targetEntry?.[2];\n while (current !== undefined) {\n const entry = nsStorage[current];\n if (entry === undefined) break;\n chain.push(current);\n current = entry[2];\n }\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 for (const cpId of chain) {\n if (remaining.size === 0) break;\n const entry = nsStorage[cpId];\n const ckpt: Checkpoint | undefined =\n entry !== undefined\n ? await this.serde.loadsTyped(\"json\", entry[0])\n : undefined;\n\n const blobValueByCh: Record<string, unknown> = {};\n const terminatedHere = new Set<string>();\n if (ckpt !== undefined) {\n for (const ch of remaining) {\n if (\n Object.prototype.hasOwnProperty.call(ckpt.channel_values, ch) &&\n ckpt.channel_values[ch] !== undefined\n ) {\n blobValueByCh[ch] = ckpt.channel_values[ch];\n terminatedHere.add(ch);\n }\n }\n }\n\n const stepWritesKey = _generateKey(threadId, checkpointNs, cpId);\n const stepWrites = Object.entries(this.writes[stepWritesKey] ?? {});\n // Sort by [taskId, idx] descending to mirror the Python walk order;\n // the full list is reversed once at the end to get oldest→newest.\n stepWrites.sort(([a], [b]) => {\n const [aTask, aIdx] = a.split(\",\");\n const [bTask, bIdx] = b.split(\",\");\n if (aTask !== bTask) return aTask < bTask ? 1 : -1;\n return Number(bIdx) - Number(aIdx);\n });\n for (const [, [tid, ch, serialized]] of stepWrites) {\n if (!remaining.has(ch)) continue;\n // Collect on-path writes regardless of seed type. A plain (pre-delta\n // migration) blob is the settled value AT that ancestor; its own\n // pending writes produce the child and must still be replayed, just\n // like a `DeltaSnapshot` seed. Skipping them would drop post-migration\n // writes saved under the migration boundary checkpoint.\n collectedByCh[ch].push([\n tid,\n ch,\n await this.serde.loadsTyped(\"json\", serialized),\n ]);\n }\n\n for (const ch of terminatedHere) {\n seedByCh[ch] = blobValueByCh[ch];\n remaining.delete(ch);\n }\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entryH: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entryH.seed = seedByCh[ch];\n }\n result[ch] = entryH;\n }\n return result;\n }\n}\n"],"mappings":"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"}
package/dist/memory.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SerializerProtocol } from "./serde/base.cjs";
2
- import { CheckpointMetadata, PendingWrite } from "./types.cjs";
2
+ import { CheckpointMetadata, DeltaChannelHistory, PendingWrite } from "./types.cjs";
3
3
  import { BaseCheckpointSaver, Checkpoint, CheckpointListOptions, CheckpointTuple } from "./base.cjs";
4
4
  import { RunnableConfig } from "@langchain/core/runnables";
5
5
 
@@ -15,6 +15,27 @@ declare class MemorySaver extends BaseCheckpointSaver {
15
15
  put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
16
16
  putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
17
17
  deleteThread(threadId: string): Promise<void>;
18
+ /**
19
+ * Override: walk the parent chain ONCE for all requested channels using
20
+ * direct storage access.
21
+ *
22
+ * Each channel terminates independently at the nearest ancestor whose
23
+ * stored `channel_values[ch]` is populated. Other channels keep walking
24
+ * until they find their own terminator or hit the root.
25
+ *
26
+ * The seed value (whether a `DeltaSnapshot` or a plain pre-delta migration
27
+ * blob) is the value AT that ancestor, prior to its own pending writes that
28
+ * produce the child. Those on-path writes — including the ones stored on the
29
+ * terminating ancestor — are always collected and replayed on top of the
30
+ * seed, so a thread migrated from a pre-delta channel does not drop the
31
+ * writes saved under the migration boundary checkpoint.
32
+ *
33
+ * @remarks Beta. See {@link BaseCheckpointSaver.getDeltaChannelHistory}.
34
+ */
35
+ getDeltaChannelHistory(options: {
36
+ config: RunnableConfig;
37
+ channels: string[];
38
+ }): Promise<Record<string, DeltaChannelHistory>>;
18
39
  }
19
40
  //#endregion
20
41
  export { MemorySaver };
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.cts","names":[],"sources":["../src/memory.ts"],"mappings":";;;;;;cAsGa,WAAA,SAAoB,mBAAA;EAQ/B,OAAA,EAAS,MAAA,SAEP,MAAA,SAAe,MAAA,UAAgB,UAAA,EAAY,UAAA;EAG7C,MAAA,EAAQ,MAAA,SAAe,MAAA,0BAAgC,UAAA;EAGvD,WAAA,CAAY,KAAA,GAAQ,kBAAA;;EAKd,oBAAA,CACJ,iBAAA,EAAmB,UAAA,EACnB,QAAA,UACA,YAAA,UACA,kBAAA,WAA0B,OAAA;EA0BtB,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EAuIzC,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAuIZ,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EA8CL,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAmB,OAAA;AAAA"}
1
+ {"version":3,"file":"memory.d.cts","names":[],"sources":["../src/memory.ts"],"mappings":";;;;;;cAuGa,WAAA,SAAoB,mBAAA;EAQ/B,OAAA,EAAS,MAAA,SAEP,MAAA,SAAe,MAAA,UAAgB,UAAA,EAAY,UAAA;EAG7C,MAAA,EAAQ,MAAA,SAAe,MAAA,0BAAgC,UAAA;EAGvD,WAAA,CAAY,KAAA,GAAQ,kBAAA;;EAKd,oBAAA,CACJ,iBAAA,EAAmB,UAAA,EACnB,QAAA,UACA,YAAA,UACA,kBAAA,WAA0B,OAAA;EA0BtB,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EAuIzC,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAuIZ,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EA8CL,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzZf;;;;;;;;;;;;;;;;;EAkbjB,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;AAAA"}
package/dist/memory.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SerializerProtocol } from "./serde/base.js";
2
- import { CheckpointMetadata, PendingWrite } from "./types.js";
2
+ import { CheckpointMetadata, DeltaChannelHistory, PendingWrite } from "./types.js";
3
3
  import { BaseCheckpointSaver, Checkpoint, CheckpointListOptions, CheckpointTuple } from "./base.js";
4
4
  import { RunnableConfig } from "@langchain/core/runnables";
5
5
 
@@ -15,6 +15,27 @@ declare class MemorySaver extends BaseCheckpointSaver {
15
15
  put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
16
16
  putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
17
17
  deleteThread(threadId: string): Promise<void>;
18
+ /**
19
+ * Override: walk the parent chain ONCE for all requested channels using
20
+ * direct storage access.
21
+ *
22
+ * Each channel terminates independently at the nearest ancestor whose
23
+ * stored `channel_values[ch]` is populated. Other channels keep walking
24
+ * until they find their own terminator or hit the root.
25
+ *
26
+ * The seed value (whether a `DeltaSnapshot` or a plain pre-delta migration
27
+ * blob) is the value AT that ancestor, prior to its own pending writes that
28
+ * produce the child. Those on-path writes — including the ones stored on the
29
+ * terminating ancestor — are always collected and replayed on top of the
30
+ * seed, so a thread migrated from a pre-delta channel does not drop the
31
+ * writes saved under the migration boundary checkpoint.
32
+ *
33
+ * @remarks Beta. See {@link BaseCheckpointSaver.getDeltaChannelHistory}.
34
+ */
35
+ getDeltaChannelHistory(options: {
36
+ config: RunnableConfig;
37
+ channels: string[];
38
+ }): Promise<Record<string, DeltaChannelHistory>>;
18
39
  }
19
40
  //#endregion
20
41
  export { MemorySaver };
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","names":[],"sources":["../src/memory.ts"],"mappings":";;;;;;cAsGa,WAAA,SAAoB,mBAAA;EAQ/B,OAAA,EAAS,MAAA,SAEP,MAAA,SAAe,MAAA,UAAgB,UAAA,EAAY,UAAA;EAG7C,MAAA,EAAQ,MAAA,SAAe,MAAA,0BAAgC,UAAA;EAGvD,WAAA,CAAY,KAAA,GAAQ,kBAAA;;EAKd,oBAAA,CACJ,iBAAA,EAAmB,UAAA,EACnB,QAAA,UACA,YAAA,UACA,kBAAA,WAA0B,OAAA;EA0BtB,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EAuIzC,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAuIZ,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EA8CL,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAmB,OAAA;AAAA"}
1
+ {"version":3,"file":"memory.d.ts","names":[],"sources":["../src/memory.ts"],"mappings":";;;;;;cAuGa,WAAA,SAAoB,mBAAA;EAQ/B,OAAA,EAAS,MAAA,SAEP,MAAA,SAAe,MAAA,UAAgB,UAAA,EAAY,UAAA;EAG7C,MAAA,EAAQ,MAAA,SAAe,MAAA,0BAAgC,UAAA;EAGvD,WAAA,CAAY,KAAA,GAAQ,kBAAA;;EAKd,oBAAA,CACJ,iBAAA,EAAmB,UAAA,EACnB,QAAA,UACA,YAAA,UACA,kBAAA,WAA0B,OAAA;EA0BtB,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EAuIzC,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAuIZ,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EA8CL,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAmB,OAAA;EAzZf;;;;;;;;;;;;;;;;;EAkbjB,sBAAA,CAAuB,OAAA;IAC3B,MAAA,EAAQ,cAAA;IACR,QAAA;EAAA,IACE,OAAA,CAAQ,MAAA,SAAe,mBAAA;AAAA"}
package/dist/memory.js CHANGED
@@ -255,6 +255,85 @@ var MemorySaver = class extends 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 = 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
  export { MemorySaver };
@@ -1 +1 @@
1
- {"version":3,"file":"memory.js","names":[],"sources":["../src/memory.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n Checkpoint,\n CheckpointListOptions,\n CheckpointTuple,\n copyCheckpoint,\n getCheckpointId,\n maxChannelVersion,\n WRITES_IDX_MAP,\n} from \"./base.js\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport {\n CheckpointMetadata,\n CheckpointPendingWrite,\n PendingWrite,\n} from \"./types.js\";\nimport { TASKS } from \"./serde/types.js\";\n\n/**\n * Keys that, when written into a plain JavaScript object via bracket\n * notation, traverse the prototype chain and mutate `Object.prototype`\n * (or the constructor) instead of creating a new own property. Any of\n * the three reaches `Object.prototype` and pollutes every object in\n * the running process. CWE-1321 (Prototype Pollution).\n */\nconst POLLUTION_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/**\n * Asserts that a value sourced from {@link RunnableConfig.configurable} (or\n * any other caller-influenced position) is safe to use as a property key\n * on the in-memory checkpoint store.\n *\n * `MemorySaver` keeps state in two nested plain objects (`storage` and\n * `writes`) and writes to them with bracket notation:\n *\n * this.storage[threadId][checkpointNamespace][checkpoint.id] = ...\n *\n * Without this guard a `threadId` of `\"__proto__\"` (or `\"constructor\"`)\n * resolves through the prototype chain, and the subsequent assignment\n * mutates `Object.prototype`. From that point every plain object in the\n * process inherits the injected property: `for...in` loops over unrelated\n * objects iterate it, framework code that does `if (obj[x])` short-circuits\n * unexpectedly, and downstream serializers may emit it. In a Node.js\n * server this is a stepping stone to remote code execution.\n *\n * `MemorySaver` is the default saver used by every quickstart, every\n * tutorial, and most test fixtures, so this guard runs in the hot path\n * for the most common LangGraph configuration.\n *\n * @param field Name of the configurable field, used in the error message.\n * @param value Value to validate. Must be a non-empty string that is not\n * one of the three prototype-pollution keys.\n * @param options.allowEmpty When true the empty string is accepted, used\n * for the documented empty `checkpoint_ns`\n * default; otherwise an empty string is\n * rejected the same way as a non-string.\n */\nfunction assertSafeStorageKey(\n field: string,\n value: unknown,\n options: { allowEmpty?: boolean } = {}\n): asserts value is string {\n const { allowEmpty = false } = options;\n if (typeof value !== \"string\") {\n const observed =\n value === null\n ? \"null\"\n : value === undefined\n ? \"undefined\"\n : Array.isArray(value)\n ? \"array\"\n : typeof value;\n throw new Error(\n `Invalid configurable value for key \"${field}\": expected a string identifier (got ${observed}). This guard protects MemorySaver from prototype pollution.`\n );\n }\n if (!allowEmpty && value === \"\") {\n throw new Error(\n `Invalid configurable value for key \"${field}\": empty string is not permitted as an in-memory storage key.`\n );\n }\n if (POLLUTION_KEYS.has(value)) {\n throw new Error(\n `Invalid configurable value for key \"${field}\": value \"${value}\" is reserved (would mutate Object.prototype). This guard protects MemorySaver from prototype pollution.`\n );\n }\n}\n\nfunction _generateKey(\n threadId: string,\n checkpointNamespace: string,\n checkpointId: string\n) {\n return JSON.stringify([threadId, checkpointNamespace, checkpointId]);\n}\n\nfunction _parseKey(key: string) {\n const [threadId, checkpointNamespace, checkpointId] = JSON.parse(key);\n return { threadId, checkpointNamespace, checkpointId };\n}\n\nexport class MemorySaver extends BaseCheckpointSaver {\n // thread ID -> checkpoint namespace -> checkpoint ID -> checkpoint mapping\n //\n // Defense in depth against prototype pollution: the backing\n // objects (and every nested level created below) use a null prototype, so\n // even if a malicious key bypassed `assertSafeStorageKey` it could not reach\n // `Object.prototype`. The guard remains the primary control; this is the\n // structural safety net.\n storage: Record<\n string,\n Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>\n > = Object.create(null);\n\n writes: Record<string, Record<string, [string, string, Uint8Array]>> =\n Object.create(null);\n\n constructor(serde?: SerializerProtocol) {\n super(serde);\n }\n\n /** @internal */\n async _migratePendingSends(\n mutableCheckpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ) {\n const deseriablizableCheckpoint = mutableCheckpoint;\n const parentKey = _generateKey(threadId, checkpointNs, parentCheckpointId);\n\n const pendingSends = await Promise.all(\n Object.values(this.writes[parentKey] ?? {})\n .filter(([_taskId, channel]) => channel === TASKS)\n .map(\n async ([_taskId, _channel, writes]) =>\n await this.serde.loadsTyped(\"json\", writes)\n )\n );\n\n deseriablizableCheckpoint.channel_values ??= {};\n deseriablizableCheckpoint.channel_values[TASKS] = pendingSends;\n\n deseriablizableCheckpoint.channel_versions ??= {};\n deseriablizableCheckpoint.channel_versions[TASKS] =\n Object.keys(deseriablizableCheckpoint.channel_versions).length > 0\n ? maxChannelVersion(\n ...Object.values(deseriablizableCheckpoint.channel_versions)\n )\n : this.getNextVersion(undefined);\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n let checkpoint_id = getCheckpointId(config);\n\n // Defense in depth: every public entry that mutates state already\n // validates these, but read paths must not return data sourced from\n // prototype-chain lookups when an attacker passes the magic keys.\n // `checkpoint_id` is intentionally allowed to be empty / undefined\n // here because the downstream `if (checkpoint_id)` branch treats\n // both as \"fetch the latest checkpoint\" rather than as a lookup key.\n if (thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", thread_id);\n }\n assertSafeStorageKey(\"checkpoint_ns\", checkpoint_ns, { allowEmpty: true });\n if (checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", checkpoint_id);\n }\n\n if (checkpoint_id) {\n const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];\n if (saved !== undefined) {\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config,\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n } else {\n const checkpoints = this.storage[thread_id]?.[checkpoint_ns];\n if (checkpoints !== undefined) {\n // eslint-disable-next-line prefer-destructuring\n checkpoint_id = Object.keys(checkpoints).sort((a, b) =>\n b.localeCompare(a)\n )[0];\n const saved = checkpoints[checkpoint_id];\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id,\n checkpoint_id,\n checkpoint_ns,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n }\n\n return undefined;\n }\n\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n // eslint-disable-next-line prefer-const\n let { before, limit, filter } = options ?? {};\n if (config.configurable?.thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", config.configurable.thread_id);\n }\n if (config.configurable?.checkpoint_ns !== undefined) {\n assertSafeStorageKey(\"checkpoint_ns\", config.configurable.checkpoint_ns, {\n allowEmpty: true,\n });\n }\n if (config.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", config.configurable.checkpoint_id);\n }\n if (before?.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", before.configurable.checkpoint_id);\n }\n const threadIds = config.configurable?.thread_id\n ? [config.configurable?.thread_id]\n : Object.keys(this.storage);\n const configCheckpointNamespace = config.configurable?.checkpoint_ns;\n const configCheckpointId = config.configurable?.checkpoint_id;\n\n for (const threadId of threadIds) {\n for (const checkpointNamespace of Object.keys(\n this.storage[threadId] ?? {}\n )) {\n if (\n configCheckpointNamespace !== undefined &&\n checkpointNamespace !== configCheckpointNamespace\n ) {\n continue;\n }\n const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};\n const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) =>\n b[0].localeCompare(a[0])\n );\n\n for (const [\n checkpointId,\n [checkpoint, metadataStr, parentCheckpointId],\n ] of sortedCheckpoints) {\n // Filter by checkpoint ID from config\n if (configCheckpointId && checkpointId !== configCheckpointId) {\n continue;\n }\n\n // Filter by checkpoint ID from before config\n if (\n before &&\n before.configurable?.checkpoint_id &&\n checkpointId >= before.configurable.checkpoint_id\n ) {\n continue;\n }\n\n // Parse metadata\n const metadata = (await this.serde.loadsTyped(\n \"json\",\n metadataStr\n )) as CheckpointMetadata;\n\n if (\n filter &&\n !Object.entries(filter).every(\n ([key, value]) =>\n (metadata as unknown as Record<string, unknown>)[key] === value\n )\n ) {\n continue;\n }\n\n // Limit search results\n if (limit !== undefined) {\n if (limit <= 0) break;\n limit -= 1;\n }\n\n const key = _generateKey(threadId, checkpointNamespace, checkpointId);\n const writes = Object.values(this.writes[key] || {});\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n writes.map(async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n })\n );\n\n const deserializedCheckpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (\n deserializedCheckpoint.v < 4 &&\n parentCheckpointId !== undefined\n ) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n threadId,\n checkpointNamespace,\n parentCheckpointId\n );\n }\n\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpointId,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n yield checkpointTuple;\n }\n }\n }\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns ?? \"\";\n if (threadId === undefined) {\n throw new Error(\n `Failed to put checkpoint. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpoint.id);\n\n if (!this.storage[threadId]) {\n this.storage[threadId] = Object.create(null);\n }\n if (!this.storage[threadId][checkpointNamespace]) {\n this.storage[threadId][checkpointNamespace] = Object.create(null);\n }\n\n const [[, serializedCheckpoint], [, serializedMetadata]] =\n await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n this.storage[threadId][checkpointNamespace][checkpoint.id] = [\n serializedCheckpoint,\n serializedMetadata,\n config.configurable?.checkpoint_id, // parent\n ];\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpoint.id,\n },\n };\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns;\n const checkpointId = config.configurable?.checkpoint_id;\n if (threadId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n if (checkpointId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"checkpoint_id\" field in its \"configurable\" property.`\n );\n }\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpointId);\n assertSafeStorageKey(\"task_id\", taskId);\n const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);\n const outerWrites_ = this.writes[outerKey];\n if (this.writes[outerKey] === undefined) {\n this.writes[outerKey] = Object.create(null);\n }\n\n await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const [, serializedValue] = await this.serde.dumpsTyped(value);\n const innerKey: [string, number] = [\n taskId,\n WRITES_IDX_MAP[channel] || idx,\n ];\n const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;\n if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {\n return;\n }\n this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];\n })\n );\n }\n\n async deleteThread(threadId: string): Promise<void> {\n assertSafeStorageKey(\"thread_id\", threadId);\n delete this.storage[threadId];\n for (const key of Object.keys(this.writes)) {\n if (_parseKey(key).threadId === threadId) delete this.writes[key];\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory.js","names":[],"sources":["../src/memory.ts"],"sourcesContent":["import type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n Checkpoint,\n CheckpointListOptions,\n CheckpointTuple,\n copyCheckpoint,\n getCheckpointId,\n maxChannelVersion,\n WRITES_IDX_MAP,\n} from \"./base.js\";\nimport { SerializerProtocol } from \"./serde/base.js\";\nimport {\n CheckpointMetadata,\n CheckpointPendingWrite,\n DeltaChannelHistory,\n PendingWrite,\n} from \"./types.js\";\nimport { TASKS } from \"./serde/types.js\";\n\n/**\n * Keys that, when written into a plain JavaScript object via bracket\n * notation, traverse the prototype chain and mutate `Object.prototype`\n * (or the constructor) instead of creating a new own property. Any of\n * the three reaches `Object.prototype` and pollutes every object in\n * the running process. CWE-1321 (Prototype Pollution).\n */\nconst POLLUTION_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/**\n * Asserts that a value sourced from {@link RunnableConfig.configurable} (or\n * any other caller-influenced position) is safe to use as a property key\n * on the in-memory checkpoint store.\n *\n * `MemorySaver` keeps state in two nested plain objects (`storage` and\n * `writes`) and writes to them with bracket notation:\n *\n * this.storage[threadId][checkpointNamespace][checkpoint.id] = ...\n *\n * Without this guard a `threadId` of `\"__proto__\"` (or `\"constructor\"`)\n * resolves through the prototype chain, and the subsequent assignment\n * mutates `Object.prototype`. From that point every plain object in the\n * process inherits the injected property: `for...in` loops over unrelated\n * objects iterate it, framework code that does `if (obj[x])` short-circuits\n * unexpectedly, and downstream serializers may emit it. In a Node.js\n * server this is a stepping stone to remote code execution.\n *\n * `MemorySaver` is the default saver used by every quickstart, every\n * tutorial, and most test fixtures, so this guard runs in the hot path\n * for the most common LangGraph configuration.\n *\n * @param field Name of the configurable field, used in the error message.\n * @param value Value to validate. Must be a non-empty string that is not\n * one of the three prototype-pollution keys.\n * @param options.allowEmpty When true the empty string is accepted, used\n * for the documented empty `checkpoint_ns`\n * default; otherwise an empty string is\n * rejected the same way as a non-string.\n */\nfunction assertSafeStorageKey(\n field: string,\n value: unknown,\n options: { allowEmpty?: boolean } = {}\n): asserts value is string {\n const { allowEmpty = false } = options;\n if (typeof value !== \"string\") {\n const observed =\n value === null\n ? \"null\"\n : value === undefined\n ? \"undefined\"\n : Array.isArray(value)\n ? \"array\"\n : typeof value;\n throw new Error(\n `Invalid configurable value for key \"${field}\": expected a string identifier (got ${observed}). This guard protects MemorySaver from prototype pollution.`\n );\n }\n if (!allowEmpty && value === \"\") {\n throw new Error(\n `Invalid configurable value for key \"${field}\": empty string is not permitted as an in-memory storage key.`\n );\n }\n if (POLLUTION_KEYS.has(value)) {\n throw new Error(\n `Invalid configurable value for key \"${field}\": value \"${value}\" is reserved (would mutate Object.prototype). This guard protects MemorySaver from prototype pollution.`\n );\n }\n}\n\nfunction _generateKey(\n threadId: string,\n checkpointNamespace: string,\n checkpointId: string\n) {\n return JSON.stringify([threadId, checkpointNamespace, checkpointId]);\n}\n\nfunction _parseKey(key: string) {\n const [threadId, checkpointNamespace, checkpointId] = JSON.parse(key);\n return { threadId, checkpointNamespace, checkpointId };\n}\n\nexport class MemorySaver extends BaseCheckpointSaver {\n // thread ID -> checkpoint namespace -> checkpoint ID -> checkpoint mapping\n //\n // Defense in depth against prototype pollution: the backing\n // objects (and every nested level created below) use a null prototype, so\n // even if a malicious key bypassed `assertSafeStorageKey` it could not reach\n // `Object.prototype`. The guard remains the primary control; this is the\n // structural safety net.\n storage: Record<\n string,\n Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>\n > = Object.create(null);\n\n writes: Record<string, Record<string, [string, string, Uint8Array]>> =\n Object.create(null);\n\n constructor(serde?: SerializerProtocol) {\n super(serde);\n }\n\n /** @internal */\n async _migratePendingSends(\n mutableCheckpoint: Checkpoint,\n threadId: string,\n checkpointNs: string,\n parentCheckpointId: string\n ) {\n const deseriablizableCheckpoint = mutableCheckpoint;\n const parentKey = _generateKey(threadId, checkpointNs, parentCheckpointId);\n\n const pendingSends = await Promise.all(\n Object.values(this.writes[parentKey] ?? {})\n .filter(([_taskId, channel]) => channel === TASKS)\n .map(\n async ([_taskId, _channel, writes]) =>\n await this.serde.loadsTyped(\"json\", writes)\n )\n );\n\n deseriablizableCheckpoint.channel_values ??= {};\n deseriablizableCheckpoint.channel_values[TASKS] = pendingSends;\n\n deseriablizableCheckpoint.channel_versions ??= {};\n deseriablizableCheckpoint.channel_versions[TASKS] =\n Object.keys(deseriablizableCheckpoint.channel_versions).length > 0\n ? maxChannelVersion(\n ...Object.values(deseriablizableCheckpoint.channel_versions)\n )\n : this.getNextVersion(undefined);\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n let checkpoint_id = getCheckpointId(config);\n\n // Defense in depth: every public entry that mutates state already\n // validates these, but read paths must not return data sourced from\n // prototype-chain lookups when an attacker passes the magic keys.\n // `checkpoint_id` is intentionally allowed to be empty / undefined\n // here because the downstream `if (checkpoint_id)` branch treats\n // both as \"fetch the latest checkpoint\" rather than as a lookup key.\n if (thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", thread_id);\n }\n assertSafeStorageKey(\"checkpoint_ns\", checkpoint_ns, { allowEmpty: true });\n if (checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", checkpoint_id);\n }\n\n if (checkpoint_id) {\n const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];\n if (saved !== undefined) {\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config,\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n } else {\n const checkpoints = this.storage[thread_id]?.[checkpoint_ns];\n if (checkpoints !== undefined) {\n // eslint-disable-next-line prefer-destructuring\n checkpoint_id = Object.keys(checkpoints).sort((a, b) =>\n b.localeCompare(a)\n )[0];\n const saved = checkpoints[checkpoint_id];\n const [checkpoint, metadata, parentCheckpointId] = saved;\n const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);\n const deserializedCheckpoint: Checkpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (deserializedCheckpoint.v < 4 && parentCheckpointId !== undefined) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n thread_id,\n checkpoint_ns,\n parentCheckpointId\n );\n }\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n Object.values(this.writes[key] || {}).map(\n async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n }\n )\n );\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id,\n checkpoint_id,\n checkpoint_ns,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata: (await this.serde.loadsTyped(\n \"json\",\n metadata\n )) as CheckpointMetadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n return checkpointTuple;\n }\n }\n\n return undefined;\n }\n\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n // eslint-disable-next-line prefer-const\n let { before, limit, filter } = options ?? {};\n if (config.configurable?.thread_id !== undefined) {\n assertSafeStorageKey(\"thread_id\", config.configurable.thread_id);\n }\n if (config.configurable?.checkpoint_ns !== undefined) {\n assertSafeStorageKey(\"checkpoint_ns\", config.configurable.checkpoint_ns, {\n allowEmpty: true,\n });\n }\n if (config.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", config.configurable.checkpoint_id);\n }\n if (before?.configurable?.checkpoint_id) {\n assertSafeStorageKey(\"checkpoint_id\", before.configurable.checkpoint_id);\n }\n const threadIds = config.configurable?.thread_id\n ? [config.configurable?.thread_id]\n : Object.keys(this.storage);\n const configCheckpointNamespace = config.configurable?.checkpoint_ns;\n const configCheckpointId = config.configurable?.checkpoint_id;\n\n for (const threadId of threadIds) {\n for (const checkpointNamespace of Object.keys(\n this.storage[threadId] ?? {}\n )) {\n if (\n configCheckpointNamespace !== undefined &&\n checkpointNamespace !== configCheckpointNamespace\n ) {\n continue;\n }\n const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};\n const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) =>\n b[0].localeCompare(a[0])\n );\n\n for (const [\n checkpointId,\n [checkpoint, metadataStr, parentCheckpointId],\n ] of sortedCheckpoints) {\n // Filter by checkpoint ID from config\n if (configCheckpointId && checkpointId !== configCheckpointId) {\n continue;\n }\n\n // Filter by checkpoint ID from before config\n if (\n before &&\n before.configurable?.checkpoint_id &&\n checkpointId >= before.configurable.checkpoint_id\n ) {\n continue;\n }\n\n // Parse metadata\n const metadata = (await this.serde.loadsTyped(\n \"json\",\n metadataStr\n )) as CheckpointMetadata;\n\n if (\n filter &&\n !Object.entries(filter).every(\n ([key, value]) =>\n (metadata as unknown as Record<string, unknown>)[key] === value\n )\n ) {\n continue;\n }\n\n // Limit search results\n if (limit !== undefined) {\n if (limit <= 0) break;\n limit -= 1;\n }\n\n const key = _generateKey(threadId, checkpointNamespace, checkpointId);\n const writes = Object.values(this.writes[key] || {});\n\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n writes.map(async ([taskId, channel, value]) => {\n return [\n taskId,\n channel,\n await this.serde.loadsTyped(\"json\", value),\n ];\n })\n );\n\n const deserializedCheckpoint = await this.serde.loadsTyped(\n \"json\",\n checkpoint\n );\n\n if (\n deserializedCheckpoint.v < 4 &&\n parentCheckpointId !== undefined\n ) {\n await this._migratePendingSends(\n deserializedCheckpoint,\n threadId,\n checkpointNamespace,\n parentCheckpointId\n );\n }\n\n const checkpointTuple: CheckpointTuple = {\n config: {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpointId,\n },\n },\n checkpoint: deserializedCheckpoint,\n metadata,\n pendingWrites,\n };\n if (parentCheckpointId !== undefined) {\n checkpointTuple.parentConfig = {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: parentCheckpointId,\n },\n };\n }\n yield checkpointTuple;\n }\n }\n }\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns ?? \"\";\n if (threadId === undefined) {\n throw new Error(\n `Failed to put checkpoint. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpoint.id);\n\n if (!this.storage[threadId]) {\n this.storage[threadId] = Object.create(null);\n }\n if (!this.storage[threadId][checkpointNamespace]) {\n this.storage[threadId][checkpointNamespace] = Object.create(null);\n }\n\n const [[, serializedCheckpoint], [, serializedMetadata]] =\n await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n this.storage[threadId][checkpointNamespace][checkpoint.id] = [\n serializedCheckpoint,\n serializedMetadata,\n config.configurable?.checkpoint_id, // parent\n ];\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNamespace,\n checkpoint_id: checkpoint.id,\n },\n };\n }\n\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const threadId = config.configurable?.thread_id;\n const checkpointNamespace = config.configurable?.checkpoint_ns;\n const checkpointId = config.configurable?.checkpoint_id;\n if (threadId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"thread_id\" field in its \"configurable\" property. ` +\n `When using a checkpointer, you must pass a \"thread_id\" so the checkpointer knows which conversation thread to persist state for. ` +\n `Example: graph.stream(input, { configurable: { thread_id: \"my-thread-id\" } })`\n );\n }\n if (checkpointId === undefined) {\n throw new Error(\n `Failed to put writes. The passed RunnableConfig is missing a required \"checkpoint_id\" field in its \"configurable\" property.`\n );\n }\n assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNamespace, {\n allowEmpty: true,\n });\n assertSafeStorageKey(\"checkpoint_id\", checkpointId);\n assertSafeStorageKey(\"task_id\", taskId);\n const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);\n const outerWrites_ = this.writes[outerKey];\n if (this.writes[outerKey] === undefined) {\n this.writes[outerKey] = Object.create(null);\n }\n\n await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const [, serializedValue] = await this.serde.dumpsTyped(value);\n const innerKey: [string, number] = [\n taskId,\n WRITES_IDX_MAP[channel] || idx,\n ];\n const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;\n if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {\n return;\n }\n this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];\n })\n );\n }\n\n async deleteThread(threadId: string): Promise<void> {\n assertSafeStorageKey(\"thread_id\", threadId);\n delete this.storage[threadId];\n for (const key of Object.keys(this.writes)) {\n if (_parseKey(key).threadId === threadId) delete this.writes[key];\n }\n }\n\n /**\n * Override: walk the parent chain ONCE for all requested channels using\n * direct storage access.\n *\n * Each channel terminates independently at the nearest ancestor whose\n * stored `channel_values[ch]` is populated. Other channels keep walking\n * until they find their own terminator or hit the root.\n *\n * The seed value (whether a `DeltaSnapshot` or a plain pre-delta migration\n * blob) is the value AT that ancestor, prior to its own pending writes that\n * produce the child. Those on-path writes — including the ones stored on the\n * terminating ancestor — are always collected and replayed on top of the\n * seed, so a thread migrated from a pre-delta channel does not drop the\n * writes saved under the migration boundary checkpoint.\n *\n * @remarks Beta. See {@link BaseCheckpointSaver.getDeltaChannelHistory}.\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 threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? \"\";\n const checkpointId = getCheckpointId(config);\n\n if (threadId !== undefined) assertSafeStorageKey(\"thread_id\", threadId);\n assertSafeStorageKey(\"checkpoint_ns\", checkpointNs, { allowEmpty: true });\n\n const nsStorage = this.storage[threadId]?.[checkpointNs] ?? {};\n\n // Build the parent chain starting at the target's parent (the target's\n // own pending writes are for the next super-step and excluded).\n const chain: string[] = [];\n const targetEntry = checkpointId ? nsStorage[checkpointId] : undefined;\n let current: string | undefined = targetEntry?.[2];\n while (current !== undefined) {\n const entry = nsStorage[current];\n if (entry === undefined) break;\n chain.push(current);\n current = entry[2];\n }\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 for (const cpId of chain) {\n if (remaining.size === 0) break;\n const entry = nsStorage[cpId];\n const ckpt: Checkpoint | undefined =\n entry !== undefined\n ? await this.serde.loadsTyped(\"json\", entry[0])\n : undefined;\n\n const blobValueByCh: Record<string, unknown> = {};\n const terminatedHere = new Set<string>();\n if (ckpt !== undefined) {\n for (const ch of remaining) {\n if (\n Object.prototype.hasOwnProperty.call(ckpt.channel_values, ch) &&\n ckpt.channel_values[ch] !== undefined\n ) {\n blobValueByCh[ch] = ckpt.channel_values[ch];\n terminatedHere.add(ch);\n }\n }\n }\n\n const stepWritesKey = _generateKey(threadId, checkpointNs, cpId);\n const stepWrites = Object.entries(this.writes[stepWritesKey] ?? {});\n // Sort by [taskId, idx] descending to mirror the Python walk order;\n // the full list is reversed once at the end to get oldest→newest.\n stepWrites.sort(([a], [b]) => {\n const [aTask, aIdx] = a.split(\",\");\n const [bTask, bIdx] = b.split(\",\");\n if (aTask !== bTask) return aTask < bTask ? 1 : -1;\n return Number(bIdx) - Number(aIdx);\n });\n for (const [, [tid, ch, serialized]] of stepWrites) {\n if (!remaining.has(ch)) continue;\n // Collect on-path writes regardless of seed type. A plain (pre-delta\n // migration) blob is the settled value AT that ancestor; its own\n // pending writes produce the child and must still be replayed, just\n // like a `DeltaSnapshot` seed. Skipping them would drop post-migration\n // writes saved under the migration boundary checkpoint.\n collectedByCh[ch].push([\n tid,\n ch,\n await this.serde.loadsTyped(\"json\", serialized),\n ]);\n }\n\n for (const ch of terminatedHere) {\n seedByCh[ch] = blobValueByCh[ch];\n remaining.delete(ch);\n }\n }\n\n const result: Record<string, DeltaChannelHistory> = {};\n for (const ch of channels) {\n const entryH: DeltaChannelHistory = {\n writes: collectedByCh[ch].slice().reverse(),\n };\n if (Object.prototype.hasOwnProperty.call(seedByCh, ch)) {\n entryH.seed = seedByCh[ch];\n }\n result[ch] = entryH;\n }\n return result;\n }\n}\n"],"mappings":";;;;;;;;;;AA2BA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAe;CAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCzE,SAAS,qBACP,OACA,OACA,UAAoC,EAAE,EACb;CACzB,MAAM,EAAE,aAAa,UAAU;AAC/B,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,WACJ,UAAU,OACN,SACA,UAAU,KAAA,IACR,cACA,MAAM,QAAQ,MAAM,GAClB,UACA,OAAO;AACjB,QAAM,IAAI,MACR,uCAAuC,MAAM,uCAAuC,SAAS,8DAC9F;;AAEH,KAAI,CAAC,cAAc,UAAU,GAC3B,OAAM,IAAI,MACR,uCAAuC,MAAM,+DAC9C;AAEH,KAAI,eAAe,IAAI,MAAM,CAC3B,OAAM,IAAI,MACR,uCAAuC,MAAM,YAAY,MAAM,0GAChE;;AAIL,SAAS,aACP,UACA,qBACA,cACA;AACA,QAAO,KAAK,UAAU;EAAC;EAAU;EAAqB;EAAa,CAAC;;AAGtE,SAAS,UAAU,KAAa;CAC9B,MAAM,CAAC,UAAU,qBAAqB,gBAAgB,KAAK,MAAM,IAAI;AACrE,QAAO;EAAE;EAAU;EAAqB;EAAc;;AAGxD,IAAa,cAAb,cAAiC,oBAAoB;CAQnD,UAGI,OAAO,OAAO,KAAK;CAEvB,SACE,OAAO,OAAO,KAAK;CAErB,YAAY,OAA4B;AACtC,QAAM,MAAM;;;CAId,MAAM,qBACJ,mBACA,UACA,cACA,oBACA;EACA,MAAM,4BAA4B;EAClC,MAAM,YAAY,aAAa,UAAU,cAAc,mBAAmB;EAE1E,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,OAAO,KAAK,OAAO,cAAc,EAAE,CAAC,CACxC,QAAQ,CAAC,SAAS,aAAa,YAAY,MAAM,CACjD,IACC,OAAO,CAAC,SAAS,UAAU,YACzB,MAAM,KAAK,MAAM,WAAW,QAAQ,OAAO,CAC9C,CACJ;AAED,4BAA0B,mBAAmB,EAAE;AAC/C,4BAA0B,eAAe,SAAS;AAElD,4BAA0B,qBAAqB,EAAE;AACjD,4BAA0B,iBAAiB,SACzC,OAAO,KAAK,0BAA0B,iBAAiB,CAAC,SAAS,IAC7D,kBACE,GAAG,OAAO,OAAO,0BAA0B,iBAAiB,CAC7D,GACD,KAAK,eAAe,KAAA,EAAU;;CAGtC,MAAM,SAAS,QAA8D;EAC3E,MAAM,YAAY,OAAO,cAAc;EACvC,MAAM,gBAAgB,OAAO,cAAc,iBAAiB;EAC5D,IAAI,gBAAgB,gBAAgB,OAAO;AAQ3C,MAAI,cAAc,KAAA,EAChB,sBAAqB,aAAa,UAAU;AAE9C,uBAAqB,iBAAiB,eAAe,EAAE,YAAY,MAAM,CAAC;AAC1E,MAAI,cACF,sBAAqB,iBAAiB,cAAc;AAGtD,MAAI,eAAe;GACjB,MAAM,QAAQ,KAAK,QAAQ,aAAa,iBAAiB;AACzD,OAAI,UAAU,KAAA,GAAW;IACvB,MAAM,CAAC,YAAY,UAAU,sBAAsB;IACnD,MAAM,MAAM,aAAa,WAAW,eAAe,cAAc;IACjE,MAAM,yBAAqC,MAAM,KAAK,MAAM,WAC1D,QACA,WACD;AAED,QAAI,uBAAuB,IAAI,KAAK,uBAAuB,KAAA,EACzD,OAAM,KAAK,qBACT,wBACA,WACA,eACA,mBACD;IAGH,MAAM,gBAA0C,MAAM,QAAQ,IAC5D,OAAO,OAAO,KAAK,OAAO,QAAQ,EAAE,CAAC,CAAC,IACpC,OAAO,CAAC,QAAQ,SAAS,WAAW;AAClC,YAAO;MACL;MACA;MACA,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM;MAC3C;MAEJ,CACF;IACD,MAAM,kBAAmC;KACvC;KACA,YAAY;KACZ,UAAW,MAAM,KAAK,MAAM,WAC1B,QACA,SACD;KACD;KACD;AACD,QAAI,uBAAuB,KAAA,EACzB,iBAAgB,eAAe,EAC7B,cAAc;KACZ;KACA;KACA,eAAe;KAChB,EACF;AAEH,WAAO;;SAEJ;GACL,MAAM,cAAc,KAAK,QAAQ,aAAa;AAC9C,OAAI,gBAAgB,KAAA,GAAW;AAE7B,oBAAgB,OAAO,KAAK,YAAY,CAAC,MAAM,GAAG,MAChD,EAAE,cAAc,EAAE,CACnB,CAAC;IAEF,MAAM,CAAC,YAAY,UAAU,sBADf,YAAY;IAE1B,MAAM,MAAM,aAAa,WAAW,eAAe,cAAc;IACjE,MAAM,yBAAqC,MAAM,KAAK,MAAM,WAC1D,QACA,WACD;AAED,QAAI,uBAAuB,IAAI,KAAK,uBAAuB,KAAA,EACzD,OAAM,KAAK,qBACT,wBACA,WACA,eACA,mBACD;IAGH,MAAM,gBAA0C,MAAM,QAAQ,IAC5D,OAAO,OAAO,KAAK,OAAO,QAAQ,EAAE,CAAC,CAAC,IACpC,OAAO,CAAC,QAAQ,SAAS,WAAW;AAClC,YAAO;MACL;MACA;MACA,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM;MAC3C;MAEJ,CACF;IACD,MAAM,kBAAmC;KACvC,QAAQ,EACN,cAAc;MACZ;MACA;MACA;MACD,EACF;KACD,YAAY;KACZ,UAAW,MAAM,KAAK,MAAM,WAC1B,QACA,SACD;KACD;KACD;AACD,QAAI,uBAAuB,KAAA,EACzB,iBAAgB,eAAe,EAC7B,cAAc;KACZ;KACA;KACA,eAAe;KAChB,EACF;AAEH,WAAO;;;;CAOb,OAAO,KACL,QACA,SACiC;EAEjC,IAAI,EAAE,QAAQ,OAAO,WAAW,WAAW,EAAE;AAC7C,MAAI,OAAO,cAAc,cAAc,KAAA,EACrC,sBAAqB,aAAa,OAAO,aAAa,UAAU;AAElE,MAAI,OAAO,cAAc,kBAAkB,KAAA,EACzC,sBAAqB,iBAAiB,OAAO,aAAa,eAAe,EACvE,YAAY,MACb,CAAC;AAEJ,MAAI,OAAO,cAAc,cACvB,sBAAqB,iBAAiB,OAAO,aAAa,cAAc;AAE1E,MAAI,QAAQ,cAAc,cACxB,sBAAqB,iBAAiB,OAAO,aAAa,cAAc;EAE1E,MAAM,YAAY,OAAO,cAAc,YACnC,CAAC,OAAO,cAAc,UAAU,GAChC,OAAO,KAAK,KAAK,QAAQ;EAC7B,MAAM,4BAA4B,OAAO,cAAc;EACvD,MAAM,qBAAqB,OAAO,cAAc;AAEhD,OAAK,MAAM,YAAY,UACrB,MAAK,MAAM,uBAAuB,OAAO,KACvC,KAAK,QAAQ,aAAa,EAAE,CAC7B,EAAE;AACD,OACE,8BAA8B,KAAA,KAC9B,wBAAwB,0BAExB;GAEF,MAAM,cAAc,KAAK,QAAQ,YAAY,wBAAwB,EAAE;GACvE,MAAM,oBAAoB,OAAO,QAAQ,YAAY,CAAC,MAAM,GAAG,MAC7D,EAAE,GAAG,cAAc,EAAE,GAAG,CACzB;AAED,QAAK,MAAM,CACT,cACA,CAAC,YAAY,aAAa,wBACvB,mBAAmB;AAEtB,QAAI,sBAAsB,iBAAiB,mBACzC;AAIF,QACE,UACA,OAAO,cAAc,iBACrB,gBAAgB,OAAO,aAAa,cAEpC;IAIF,MAAM,WAAY,MAAM,KAAK,MAAM,WACjC,QACA,YACD;AAED,QACE,UACA,CAAC,OAAO,QAAQ,OAAO,CAAC,OACrB,CAAC,KAAK,WACJ,SAAgD,SAAS,MAC7D,CAED;AAIF,QAAI,UAAU,KAAA,GAAW;AACvB,SAAI,SAAS,EAAG;AAChB,cAAS;;IAGX,MAAM,MAAM,aAAa,UAAU,qBAAqB,aAAa;IACrE,MAAM,SAAS,OAAO,OAAO,KAAK,OAAO,QAAQ,EAAE,CAAC;IAEpD,MAAM,gBAA0C,MAAM,QAAQ,IAC5D,OAAO,IAAI,OAAO,CAAC,QAAQ,SAAS,WAAW;AAC7C,YAAO;MACL;MACA;MACA,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM;MAC3C;MACD,CACH;IAED,MAAM,yBAAyB,MAAM,KAAK,MAAM,WAC9C,QACA,WACD;AAED,QACE,uBAAuB,IAAI,KAC3B,uBAAuB,KAAA,EAEvB,OAAM,KAAK,qBACT,wBACA,UACA,qBACA,mBACD;IAGH,MAAM,kBAAmC;KACvC,QAAQ,EACN,cAAc;MACZ,WAAW;MACX,eAAe;MACf,eAAe;MAChB,EACF;KACD,YAAY;KACZ;KACA;KACD;AACD,QAAI,uBAAuB,KAAA,EACzB,iBAAgB,eAAe,EAC7B,cAAc;KACZ,WAAW;KACX,eAAe;KACf,eAAe;KAChB,EACF;AAEH,UAAM;;;;CAMd,MAAM,IACJ,QACA,YACA,UACyB;EACzB,MAAM,qBAA0C,eAAe,WAAW;EAC1E,MAAM,WAAW,OAAO,cAAc;EACtC,MAAM,sBAAsB,OAAO,cAAc,iBAAiB;AAClE,MAAI,aAAa,KAAA,EACf,OAAM,IAAI,MACR,qVAGD;AAGH,uBAAqB,aAAa,SAAS;AAC3C,uBAAqB,iBAAiB,qBAAqB,EACzD,YAAY,MACb,CAAC;AACF,uBAAqB,iBAAiB,WAAW,GAAG;AAEpD,MAAI,CAAC,KAAK,QAAQ,UAChB,MAAK,QAAQ,YAAY,OAAO,OAAO,KAAK;AAE9C,MAAI,CAAC,KAAK,QAAQ,UAAU,qBAC1B,MAAK,QAAQ,UAAU,uBAAuB,OAAO,OAAO,KAAK;EAGnE,MAAM,CAAC,GAAG,uBAAuB,GAAG,uBAClC,MAAM,QAAQ,IAAI,CAChB,KAAK,MAAM,WAAW,mBAAmB,EACzC,KAAK,MAAM,WAAW,SAAS,CAChC,CAAC;AAEJ,OAAK,QAAQ,UAAU,qBAAqB,WAAW,MAAM;GAC3D;GACA;GACA,OAAO,cAAc;GACtB;AAED,SAAO,EACL,cAAc;GACZ,WAAW;GACX,eAAe;GACf,eAAe,WAAW;GAC3B,EACF;;CAGH,MAAM,UACJ,QACA,QACA,QACe;EACf,MAAM,WAAW,OAAO,cAAc;EACtC,MAAM,sBAAsB,OAAO,cAAc;EACjD,MAAM,eAAe,OAAO,cAAc;AAC1C,MAAI,aAAa,KAAA,EACf,OAAM,IAAI,MACR,iVAGD;AAEH,MAAI,iBAAiB,KAAA,EACnB,OAAM,IAAI,MACR,8HACD;AAEH,uBAAqB,aAAa,SAAS;AAC3C,uBAAqB,iBAAiB,qBAAqB,EACzD,YAAY,MACb,CAAC;AACF,uBAAqB,iBAAiB,aAAa;AACnD,uBAAqB,WAAW,OAAO;EACvC,MAAM,WAAW,aAAa,UAAU,qBAAqB,aAAa;EAC1E,MAAM,eAAe,KAAK,OAAO;AACjC,MAAI,KAAK,OAAO,cAAc,KAAA,EAC5B,MAAK,OAAO,YAAY,OAAO,OAAO,KAAK;AAG7C,QAAM,QAAQ,IACZ,OAAO,IAAI,OAAO,CAAC,SAAS,QAAQ,QAAQ;GAC1C,MAAM,GAAG,mBAAmB,MAAM,KAAK,MAAM,WAAW,MAAM;GAC9D,MAAM,WAA6B,CACjC,QACA,eAAe,YAAY,IAC5B;GACD,MAAM,cAAc,GAAG,SAAS,GAAG,GAAG,SAAS;AAC/C,OAAI,SAAS,MAAM,KAAK,gBAAgB,eAAe,aACrD;AAEF,QAAK,OAAO,UAAU,eAAe;IAAC;IAAQ;IAAS;IAAgB;IACvE,CACH;;CAGH,MAAM,aAAa,UAAiC;AAClD,uBAAqB,aAAa,SAAS;AAC3C,SAAO,KAAK,QAAQ;AACpB,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,OAAO,CACxC,KAAI,UAAU,IAAI,CAAC,aAAa,SAAU,QAAO,KAAK,OAAO;;;;;;;;;;;;;;;;;;;CAqBjE,MAAM,uBAAuB,SAGoB;EAC/C,MAAM,EAAE,QAAQ,aAAa;AAC7B,MAAI,SAAS,WAAW,EAAG,QAAO,EAAE;EAEpC,MAAM,WAAW,OAAO,cAAc;EACtC,MAAM,eAAe,OAAO,cAAc,iBAAiB;EAC3D,MAAM,eAAe,gBAAgB,OAAO;AAE5C,MAAI,aAAa,KAAA,EAAW,sBAAqB,aAAa,SAAS;AACvE,uBAAqB,iBAAiB,cAAc,EAAE,YAAY,MAAM,CAAC;EAEzE,MAAM,YAAY,KAAK,QAAQ,YAAY,iBAAiB,EAAE;EAI9D,MAAM,QAAkB,EAAE;EAE1B,IAAI,WADgB,eAAe,UAAU,gBAAgB,KAAA,KACb;AAChD,SAAO,YAAY,KAAA,GAAW;GAC5B,MAAM,QAAQ,UAAU;AACxB,OAAI,UAAU,KAAA,EAAW;AACzB,SAAM,KAAK,QAAQ;AACnB,aAAU,MAAM;;EAGlB,MAAM,gBAA0D,EAAE;EAClE,MAAM,WAAoC,EAAE;EAC5C,MAAM,YAAY,IAAI,IAAI,SAAS;AACnC,OAAK,MAAM,MAAM,SAAU,eAAc,MAAM,EAAE;AAEjD,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,UAAU,SAAS,EAAG;GAC1B,MAAM,QAAQ,UAAU;GACxB,MAAM,OACJ,UAAU,KAAA,IACN,MAAM,KAAK,MAAM,WAAW,QAAQ,MAAM,GAAG,GAC7C,KAAA;GAEN,MAAM,gBAAyC,EAAE;GACjD,MAAM,iCAAiB,IAAI,KAAa;AACxC,OAAI,SAAS,KAAA;SACN,MAAM,MAAM,UACf,KACE,OAAO,UAAU,eAAe,KAAK,KAAK,gBAAgB,GAAG,IAC7D,KAAK,eAAe,QAAQ,KAAA,GAC5B;AACA,mBAAc,MAAM,KAAK,eAAe;AACxC,oBAAe,IAAI,GAAG;;;GAK5B,MAAM,gBAAgB,aAAa,UAAU,cAAc,KAAK;GAChE,MAAM,aAAa,OAAO,QAAQ,KAAK,OAAO,kBAAkB,EAAE,CAAC;AAGnE,cAAW,MAAM,CAAC,IAAI,CAAC,OAAO;IAC5B,MAAM,CAAC,OAAO,QAAQ,EAAE,MAAM,IAAI;IAClC,MAAM,CAAC,OAAO,QAAQ,EAAE,MAAM,IAAI;AAClC,QAAI,UAAU,MAAO,QAAO,QAAQ,QAAQ,IAAI;AAChD,WAAO,OAAO,KAAK,GAAG,OAAO,KAAK;KAClC;AACF,QAAK,MAAM,GAAG,CAAC,KAAK,IAAI,gBAAgB,YAAY;AAClD,QAAI,CAAC,UAAU,IAAI,GAAG,CAAE;AAMxB,kBAAc,IAAI,KAAK;KACrB;KACA;KACA,MAAM,KAAK,MAAM,WAAW,QAAQ,WAAW;KAChD,CAAC;;AAGJ,QAAK,MAAM,MAAM,gBAAgB;AAC/B,aAAS,MAAM,cAAc;AAC7B,cAAU,OAAO,GAAG;;;EAIxB,MAAM,SAA8C,EAAE;AACtD,OAAK,MAAM,MAAM,UAAU;GACzB,MAAM,SAA8B,EAClC,QAAQ,cAAc,IAAI,OAAO,CAAC,SAAS,EAC5C;AACD,OAAI,OAAO,UAAU,eAAe,KAAK,UAAU,GAAG,CACpD,QAAO,OAAO,SAAS;AAEzB,UAAO,MAAM;;AAEf,SAAO"}
@@ -1,3 +1,4 @@
1
+ const require_types = require("./types.cjs");
1
2
  const require_index = require("./utils/fast-safe-stringify/index.cjs");
2
3
  let _langchain_core_load = require("@langchain/core/load");
3
4
  //#region src/serde/jsonplus.ts
@@ -18,6 +19,7 @@ async function _reviver(value) {
18
19
  const revivedObj = {};
19
20
  for (const [k, v] of Object.entries(value)) revivedObj[k] = await _reviver(v);
20
21
  if (revivedObj.lc === 2 && revivedObj.type === "undefined") return;
22
+ else if (revivedObj.lc === 2 && revivedObj.type === "delta_snapshot") return new require_types.DeltaSnapshot(revivedObj.value);
21
23
  else if (revivedObj.lc === 2 && revivedObj.type === "constructor" && Array.isArray(revivedObj.id)) try {
22
24
  const constructorName = revivedObj.id[revivedObj.id.length - 1];
23
25
  let constructor;
@@ -64,12 +66,18 @@ function _default(obj) {
64
66
  lc: 2,
65
67
  type: "undefined"
66
68
  };
69
+ else if (obj instanceof require_types.DeltaSnapshot) return {
70
+ lc: 2,
71
+ type: "delta_snapshot",
72
+ value: obj.value
73
+ };
67
74
  else if (obj instanceof Set || obj instanceof Map) return _encodeConstructorArgs(obj.constructor, void 0, [Array.from(obj)]);
68
75
  else if (obj instanceof RegExp) return _encodeConstructorArgs(RegExp, void 0, [obj.source, obj.flags]);
69
76
  else if (obj instanceof Error) return _encodeConstructorArgs(obj.constructor, void 0, [obj.message]);
70
77
  else if (obj?.lg_name === "Send") return {
71
78
  node: obj.node,
72
- args: obj.args
79
+ args: obj.args,
80
+ ...obj.timeout !== void 0 ? { timeout: obj.timeout } : {}
73
81
  };
74
82
  else if (obj instanceof Uint8Array) return _encodeConstructorArgs(Uint8Array, "from", [Array.from(obj)]);
75
83
  else return obj;
@@ -1 +1 @@
1
- {"version":3,"file":"jsonplus.cjs","names":["stringify"],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"jsonplus.cjs","names":["DeltaSnapshot","stringify"],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (revivedObj.lc === 2 && revivedObj.type === \"delta_snapshot\") {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { DeltaSnapshot } from "./types.js";
1
2
  import { stringify } from "./utils/fast-safe-stringify/index.js";
2
3
  import { load } from "@langchain/core/load";
3
4
  //#region src/serde/jsonplus.ts
@@ -18,6 +19,7 @@ async function _reviver(value) {
18
19
  const revivedObj = {};
19
20
  for (const [k, v] of Object.entries(value)) revivedObj[k] = await _reviver(v);
20
21
  if (revivedObj.lc === 2 && revivedObj.type === "undefined") return;
22
+ else if (revivedObj.lc === 2 && revivedObj.type === "delta_snapshot") return new DeltaSnapshot(revivedObj.value);
21
23
  else if (revivedObj.lc === 2 && revivedObj.type === "constructor" && Array.isArray(revivedObj.id)) try {
22
24
  const constructorName = revivedObj.id[revivedObj.id.length - 1];
23
25
  let constructor;
@@ -64,12 +66,18 @@ function _default(obj) {
64
66
  lc: 2,
65
67
  type: "undefined"
66
68
  };
69
+ else if (obj instanceof DeltaSnapshot) return {
70
+ lc: 2,
71
+ type: "delta_snapshot",
72
+ value: obj.value
73
+ };
67
74
  else if (obj instanceof Set || obj instanceof Map) return _encodeConstructorArgs(obj.constructor, void 0, [Array.from(obj)]);
68
75
  else if (obj instanceof RegExp) return _encodeConstructorArgs(RegExp, void 0, [obj.source, obj.flags]);
69
76
  else if (obj instanceof Error) return _encodeConstructorArgs(obj.constructor, void 0, [obj.message]);
70
77
  else if (obj?.lg_name === "Send") return {
71
78
  node: obj.node,
72
- args: obj.args
79
+ args: obj.args,
80
+ ...obj.timeout !== void 0 ? { timeout: obj.timeout } : {}
73
81
  };
74
82
  else if (obj instanceof Uint8Array) return _encodeConstructorArgs(Uint8Array, "from", [Array.from(obj)]);
75
83
  else return obj;
@@ -1 +1 @@
1
- {"version":3,"file":"jsonplus.js","names":[],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":";;;AAMA,SAAS,4BAA4B,OAAgC;AACnE,QACE,UAAU,QACV,MAAM,OAAO,KACb,MAAM,SAAS,iBACf,MAAM,QAAQ,MAAM,GAAG;;;;;;;;;;AAY3B,eAAe,SAAS,OAA0B;AAChD,KAAI,SAAS,OAAO,UAAU,SAC5B,KAAI,MAAM,QAAQ,MAAM,CAItB,QAHqB,MAAM,QAAQ,IACjC,MAAM,KAAK,SAAS,SAAS,KAAK,CAAC,CACpC;MAEI;EACL,MAAM,aAAkB,EAAE;AAC1B,OAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,CACxC,YAAW,KAAK,MAAM,SAAS,EAAE;AAGnC,MAAI,WAAW,OAAO,KAAK,WAAW,SAAS,YAC7C;WAEA,WAAW,OAAO,KAClB,WAAW,SAAS,iBACpB,MAAM,QAAQ,WAAW,GAAG,CAE5B,KAAI;GACF,MAAM,kBAAkB,WAAW,GAAG,WAAW,GAAG,SAAS;GAC7D,IAAI;AAEJ,WAAQ,iBAAR;IACE,KAAK;AACH,mBAAc;AACd;IACF,KAAK;AACH,mBAAc;AACd;IACF,KAAK;AACH,mBAAc;AACd;IACF,KAAK;AACH,mBAAc;AACd;IACF,KAAK;AACH,mBAAc;AACd;IACF,QACE,QAAO;;AAEX,OAAI,WAAW,OACb,QAAQ,YAAoB,WAAW,QACrC,GAAI,WAAW,QAAQ,EAAE,CAC1B;OAED,QAAO,IAAK,YAAoB,GAAI,WAAW,QAAQ,EAAE,CAAE;UAEvD;AACN,UAAO;;WAEA,4BAA4B,WAAW,CAChD,QAAO,KAAK,KAAK,UAAU,WAAW,CAAC;AAGzC,SAAO;;AAGX,QAAO;;AAGT,SAAS,uBAEP,aACA,QACA,MACA,QACQ;AACR,QAAO;EACL,IAAI;EACJ,MAAM;EACN,IAAI,CAAC,YAAY,KAAK;EACtB,QAAQ,UAAU;EAClB,MAAM,QAAQ,EAAE;EAChB,QAAQ,UAAU,EAAE;EACrB;;AAGH,SAAS,SAAS,KAAe;AAC/B,KAAI,QAAQ,KAAA,EACV,QAAO;EACL,IAAI;EACJ,MAAM;EACP;UACQ,eAAe,OAAO,eAAe,IAC9C,QAAO,uBAAuB,IAAI,aAAa,KAAA,GAAW,CACxD,MAAM,KAAK,IAAI,CAChB,CAAC;UACO,eAAe,OACxB,QAAO,uBAAuB,QAAQ,KAAA,GAAW,CAAC,IAAI,QAAQ,IAAI,MAAM,CAAC;UAChE,eAAe,MACxB,QAAO,uBAAuB,IAAI,aAAa,KAAA,GAAW,CAAC,IAAI,QAAQ,CAAC;UAE/D,KAAK,YAAY,OAC1B,QAAO;EACL,MAAM,IAAI;EACV,MAAM,IAAI;EACX;UACQ,eAAe,WACxB,QAAO,uBAAuB,YAAY,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;KAEpE,QAAO;;AAIX,IAAa,qBAAb,MAA8D;CAC5D,OAAiB,KAAsB;AAErC,SADgB,IAAI,aAAa,CAClB,OACb,UAAU,MAAM,GAAW,UAAe;AACxC,UAAO,SAAS,MAAM;IACtB,CACH;;CAGH,MAAM,WAAW,KAAyC;AACxD,MAAI,eAAe,WACjB,QAAO,CAAC,SAAS,IAAI;MAErB,QAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC;;CAIrC,MAAgB,OAAO,MAA4B;AAEjD,SAAO,SADQ,KAAK,MAAM,KAAK,CACR;;CAGzB,MAAM,WAAW,MAAc,MAAyC;AACtE,MAAI,SAAS,QACX,QAAO,OAAO,SAAS,WAAW,IAAI,aAAa,CAAC,OAAO,KAAK,GAAG;WAC1D,SAAS,OAClB,QAAO,KAAK,OACV,OAAO,SAAS,WAAW,OAAO,IAAI,aAAa,CAAC,OAAO,KAAK,CACjE;MAED,OAAM,IAAI,MAAM,+BAA+B,OAAO"}
1
+ {"version":3,"file":"jsonplus.js","names":[],"sources":["../../src/serde/jsonplus.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable no-instanceof/no-instanceof */\nimport { load } from \"@langchain/core/load\";\nimport { SerializerProtocol } from \"./base.js\";\nimport { stringify } from \"./utils/fast-safe-stringify/index.js\";\nimport { DeltaSnapshot } from \"./types.js\";\n\nfunction isLangChainSerializedObject(value: Record<string, unknown>) {\n return (\n value !== null &&\n value.lc === 1 &&\n value.type === \"constructor\" &&\n Array.isArray(value.id)\n );\n}\n\n/**\n * The replacer in stringify does not allow delegation to built-in LangChain\n * serialization methods, and instead immediately calls `.toJSON()` and\n * continues to stringify subfields.\n *\n * We therefore must start from the most nested elements in the input and\n * deserialize upwards rather than top-down.\n */\nasync function _reviver(value: any): Promise<any> {\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n const revivedArray = await Promise.all(\n value.map((item) => _reviver(item))\n );\n return revivedArray;\n } else {\n const revivedObj: any = {};\n for (const [k, v] of Object.entries(value)) {\n revivedObj[k] = await _reviver(v);\n }\n\n if (revivedObj.lc === 2 && revivedObj.type === \"undefined\") {\n return undefined;\n } else if (revivedObj.lc === 2 && revivedObj.type === \"delta_snapshot\") {\n // Wrapped value is already revived (bottom-up traversal).\n return new DeltaSnapshot(revivedObj.value);\n } else if (\n revivedObj.lc === 2 &&\n revivedObj.type === \"constructor\" &&\n Array.isArray(revivedObj.id)\n ) {\n try {\n const constructorName = revivedObj.id[revivedObj.id.length - 1];\n let constructor: any;\n\n switch (constructorName) {\n case \"Set\":\n constructor = Set;\n break;\n case \"Map\":\n constructor = Map;\n break;\n case \"RegExp\":\n constructor = RegExp;\n break;\n case \"Error\":\n constructor = Error;\n break;\n case \"Uint8Array\":\n constructor = Uint8Array;\n break;\n default:\n return revivedObj;\n }\n if (revivedObj.method) {\n return (constructor as any)[revivedObj.method](\n ...(revivedObj.args || [])\n );\n } else {\n return new (constructor as any)(...(revivedObj.args || []));\n }\n } catch {\n return revivedObj;\n }\n } else if (isLangChainSerializedObject(revivedObj)) {\n return load(JSON.stringify(revivedObj));\n }\n\n return revivedObj;\n }\n }\n return value;\n}\n\nfunction _encodeConstructorArgs(\n // oxlint-disable-next-line @typescript-eslint/no-unsafe-function-type\n constructor: Function,\n method?: string,\n args?: any[],\n kwargs?: Record<string, any>\n): object {\n return {\n lc: 2,\n type: \"constructor\",\n id: [constructor.name],\n method: method ?? null,\n args: args ?? [],\n kwargs: kwargs ?? {},\n };\n}\n\nfunction _default(obj: any): any {\n if (obj === undefined) {\n return {\n lc: 2,\n type: \"undefined\",\n };\n } else if (obj instanceof DeltaSnapshot) {\n return {\n lc: 2,\n type: \"delta_snapshot\",\n // `value` continues to be walked by `stringify`, so nested\n // serializable types (messages, Maps, etc.) are encoded normally.\n value: obj.value,\n };\n } else if (obj instanceof Set || obj instanceof Map) {\n return _encodeConstructorArgs(obj.constructor, undefined, [\n Array.from(obj),\n ]);\n } else if (obj instanceof RegExp) {\n return _encodeConstructorArgs(RegExp, undefined, [obj.source, obj.flags]);\n } else if (obj instanceof Error) {\n return _encodeConstructorArgs(obj.constructor, undefined, [obj.message]);\n // TODO: Remove special case\n } else if (obj?.lg_name === \"Send\") {\n return {\n node: obj.node,\n args: obj.args,\n // preserve an optional per-task timeout policy across (de)serialization\n ...(obj.timeout !== undefined ? { timeout: obj.timeout } : {}),\n };\n } else if (obj instanceof Uint8Array) {\n return _encodeConstructorArgs(Uint8Array, \"from\", [Array.from(obj)]);\n } else {\n return obj;\n }\n}\n\nexport class JsonPlusSerializer implements SerializerProtocol {\n protected _dumps(obj: any): Uint8Array {\n const encoder = new TextEncoder();\n return encoder.encode(\n stringify(obj, (_: string, value: any) => {\n return _default(value);\n })\n );\n }\n\n async dumpsTyped(obj: any): Promise<[string, Uint8Array]> {\n if (obj instanceof Uint8Array) {\n return [\"bytes\", obj];\n } else {\n return [\"json\", this._dumps(obj)];\n }\n }\n\n protected async _loads(data: string): Promise<any> {\n const parsed = JSON.parse(data);\n return _reviver(parsed);\n }\n\n async loadsTyped(type: string, data: Uint8Array | string): Promise<any> {\n if (type === \"bytes\") {\n return typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n } else if (type === \"json\") {\n return this._loads(\n typeof data === \"string\" ? data : new TextDecoder().decode(data)\n );\n } else {\n throw new Error(`Unknown serialization type: ${type}`);\n }\n }\n}\n"],"mappings":"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"}