@cloudbase/agent-adapter-langgraph 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,3 @@
1
- import "./chunk-NSSMTXJJ.mjs";
2
-
3
1
  // src/agent.ts
4
2
  import {
5
3
  EventType,
@@ -8,8 +6,7 @@ import {
8
6
  import { Observable } from "rxjs";
9
7
  import {
10
8
  Annotation,
11
- MessagesAnnotation,
12
- Command
9
+ MessagesAnnotation
13
10
  } from "@langchain/langgraph";
14
11
 
15
12
  // src/util.ts
@@ -62,7 +59,6 @@ function convertJsonSchemaToZodSchema(jsonSchema, required) {
62
59
  // src/agent.ts
63
60
  import { noopLogger, isErrorWithCode } from "@cloudbase/agent-shared";
64
61
  var LangChainCallbackHandler;
65
- var observabilityLoadAttempted = false;
66
62
  var ClientPropertiesAnnotation = Annotation.Root({
67
63
  tools: Annotation
68
64
  });
@@ -87,45 +83,7 @@ var LanggraphAgent = class extends AbstractAgent {
87
83
  const { messages, runId, threadId } = input;
88
84
  const logger = this.logger.child?.({ runId, threadId }) ?? this.logger;
89
85
  logger.info?.("Run started");
90
- if (!observabilityLoadAttempted && !this.observabilityCallback) {
91
- observabilityLoadAttempted = true;
92
- try {
93
- logger.debug?.("Attempting to load observability...");
94
- const obsModule = await import("./langchain-6EY35CAB.mjs");
95
- LangChainCallbackHandler = obsModule.CallbackHandler;
96
- if (LangChainCallbackHandler) {
97
- this.observabilityCallback = new LangChainCallbackHandler({
98
- adapterName: this.adapterName,
99
- logger
100
- // Reuse the run-specific logger with context
101
- });
102
- logger.debug?.("\u2713 Observability callback created");
103
- }
104
- } catch (e) {
105
- logger.debug?.(
106
- "\u2717 Observability not available:",
107
- e instanceof Error ? e.message : String(e)
108
- );
109
- }
110
- }
111
- if (this.observabilityCallback && input.forwardedProps?.__agui_server_context) {
112
- try {
113
- const serverContextData = input.forwardedProps.__agui_server_context;
114
- const serverSpanContext = {
115
- traceId: serverContextData.traceId,
116
- spanId: serverContextData.spanId,
117
- traceFlags: serverContextData.traceFlags,
118
- isRemote: false
119
- };
120
- this.observabilityCallback.setExternalParentContext(serverSpanContext);
121
- logger.debug?.("\u2713 Server context restored:", {
122
- traceId: serverSpanContext.traceId,
123
- spanId: serverSpanContext.spanId
124
- });
125
- } catch (e) {
126
- logger.debug?.("Failed to restore server context:", e);
127
- }
128
- }
86
+ await this.setupObservability(input, logger);
129
87
  const runStartedEvent = {
130
88
  type: EventType.RUN_STARTED,
131
89
  threadId,
@@ -133,11 +91,9 @@ var LanggraphAgent = class extends AbstractAgent {
133
91
  };
134
92
  logger.trace?.({ aguiEvent: runStartedEvent }, "Emitting AGUI event");
135
93
  subscriber.next(runStartedEvent);
136
- const isResume = !!input.forwardedProps?.resume;
137
94
  const lastUserMessage = messages.filter((m) => m.role === "user").pop();
138
95
  logger.debug?.(
139
96
  {
140
- isResume,
141
97
  messageCount: messages.length,
142
98
  toolCount: input.tools?.length ?? 0,
143
99
  tools: input.tools?.map((t) => t.name),
@@ -146,25 +102,21 @@ var LanggraphAgent = class extends AbstractAgent {
146
102
  "Preparing stream input"
147
103
  );
148
104
  logger.trace?.({ messages, tools: input.tools }, "Full input messages");
149
- const langChainMessages = isResume ? void 0 : aguiMessagesToLangChain(messages);
150
- if (langChainMessages) {
151
- logger.trace?.(
152
- {
153
- langChainMessages,
154
- messageCount: langChainMessages.length,
155
- messageTypes: langChainMessages.map((m) => ({
156
- type: m.type,
157
- id: m.id,
158
- hasToolCalls: "tool_calls" in m && Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
159
- toolCallId: "tool_call_id" in m ? m.tool_call_id : void 0
160
- }))
161
- },
162
- "Converted LangGraph messages"
163
- );
164
- }
165
- const streamEventInput = isResume ? new Command({
166
- resume: JSON.stringify(input.forwardedProps?.resume?.payload)
167
- }) : {
105
+ const langChainMessages = aguiMessagesToLangChain(messages, logger);
106
+ logger.trace?.(
107
+ {
108
+ langChainMessages,
109
+ messageCount: langChainMessages.length,
110
+ messageTypes: langChainMessages.map((m) => ({
111
+ type: m.type,
112
+ id: m.id,
113
+ hasToolCalls: "tool_calls" in m && Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
114
+ toolCallId: "tool_call_id" in m ? m.tool_call_id : void 0
115
+ }))
116
+ },
117
+ "Converted LangGraph messages"
118
+ );
119
+ const streamEventInput = {
168
120
  messages: langChainMessages,
169
121
  client: {
170
122
  tools: convertActionsToDynamicStructuredTools(
@@ -197,7 +149,6 @@ var LanggraphAgent = class extends AbstractAgent {
197
149
  "Pre-populated handled tool call IDs from input messages"
198
150
  );
199
151
  }
200
- let interrupt;
201
152
  let currentToolCall = null;
202
153
  let eventCount = 0;
203
154
  let toolCallCount = 0;
@@ -522,46 +473,17 @@ var LanggraphAgent = class extends AbstractAgent {
522
473
  "State update with messages"
523
474
  );
524
475
  }
525
- if (chunk?.__interrupt__ && Array.isArray(chunk.__interrupt__) && chunk.__interrupt__.length > 0) {
526
- const rawInterrupt = chunk.__interrupt__[0];
527
- logger.debug?.(
528
- { interruptId: rawInterrupt.id },
529
- "Interrupt received"
530
- );
531
- interrupt = {
532
- id: rawInterrupt.id,
533
- // TODO: replace with actual reason
534
- reason: "agent requested interrupt",
535
- payload: rawInterrupt.value
536
- };
537
- }
538
476
  }
539
477
  }
540
478
  const stats = { eventCount, toolCallCount, textChunkCount };
541
- if (interrupt) {
542
- const runFinishedEvent = {
543
- type: EventType.RUN_FINISHED,
544
- threadId,
545
- runId,
546
- outcome: "interrupt",
547
- interrupt
548
- };
549
- logger.info?.(
550
- { outcome: "interrupt", interruptId: interrupt.id, ...stats },
551
- "Run finished with interrupt"
552
- );
553
- logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
554
- subscriber.next(runFinishedEvent);
555
- } else {
556
- const runFinishedEvent = {
557
- type: EventType.RUN_FINISHED,
558
- threadId,
559
- runId
560
- };
561
- logger.info?.({ outcome: "complete", ...stats }, "Run finished");
562
- logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
563
- subscriber.next(runFinishedEvent);
564
- }
479
+ const runFinishedEvent = {
480
+ type: EventType.RUN_FINISHED,
481
+ threadId,
482
+ runId
483
+ };
484
+ logger.info?.({ outcome: "complete", ...stats }, "Run finished");
485
+ logger.trace?.({ aguiEvent: runFinishedEvent }, "Emitting AGUI event");
486
+ subscriber.next(runFinishedEvent);
565
487
  } catch (error) {
566
488
  logger.error?.(
567
489
  { err: error, eventCount, toolCallCount, textChunkCount },
@@ -587,9 +509,56 @@ var LanggraphAgent = class extends AbstractAgent {
587
509
  cloned.logger = logger;
588
510
  return cloned;
589
511
  }
512
+ /**
513
+ * Setup observability for agent execution.
514
+ * Lazy loads observability callback, restores server context from forwardedProps,
515
+ * and configures the callback for graph execution.
516
+ */
517
+ async setupObservability(input, logger) {
518
+ if (!LangChainCallbackHandler) {
519
+ try {
520
+ logger.debug?.("Attempting to load observability...");
521
+ const obsModule = await import("@cloudbase/agent-observability/langchain");
522
+ LangChainCallbackHandler = obsModule.CallbackHandler;
523
+ logger.debug?.("\u2713 Observability handler loaded");
524
+ } catch (e) {
525
+ logger.debug?.(
526
+ "\u2717 Observability not available:",
527
+ e instanceof Error ? e.message : String(e)
528
+ );
529
+ }
530
+ }
531
+ this.observabilityCallback = LangChainCallbackHandler ? new LangChainCallbackHandler({
532
+ adapterName: this.adapterName,
533
+ logger
534
+ }) : void 0;
535
+ if (this.observabilityCallback && input.forwardedProps?.__agui_server_context) {
536
+ try {
537
+ const serverContextData = input.forwardedProps.__agui_server_context;
538
+ const serverSpanContext = {
539
+ traceId: serverContextData.traceId,
540
+ spanId: serverContextData.spanId,
541
+ traceFlags: serverContextData.traceFlags,
542
+ isRemote: false
543
+ };
544
+ this.observabilityCallback.setExternalParentContext(serverSpanContext, {
545
+ threadId: input.threadId,
546
+ runId: input.runId
547
+ });
548
+ logger.debug?.("\u2713 Server context restored:", {
549
+ traceId: serverSpanContext.traceId,
550
+ spanId: serverSpanContext.spanId,
551
+ threadId: input.threadId,
552
+ runId: input.runId
553
+ });
554
+ } catch (e) {
555
+ logger.debug?.("Failed to restore server context:", e);
556
+ }
557
+ }
558
+ }
590
559
  };
591
- function aguiMessagesToLangChain(messages) {
592
- return messages.map((message, index) => {
560
+ function aguiMessagesToLangChain(messages, logger) {
561
+ return messages.map((message) => {
593
562
  switch (message.role) {
594
563
  case "user":
595
564
  if (typeof message.content === "string") {
@@ -600,10 +569,34 @@ function aguiMessagesToLangChain(messages) {
600
569
  type: "human"
601
570
  };
602
571
  } else {
572
+ const langChainContent = [];
573
+ for (const part of message.content) {
574
+ if (part.type === "text") {
575
+ langChainContent.push({ type: "text", text: part.text });
576
+ } else if (part.type === "binary") {
577
+ const imageUrl = convertBinaryToImageUrl(
578
+ part,
579
+ message.id,
580
+ logger
581
+ );
582
+ if (imageUrl) {
583
+ langChainContent.push({
584
+ type: "image_url",
585
+ image_url: { url: imageUrl }
586
+ });
587
+ }
588
+ }
589
+ }
590
+ if (langChainContent.length === 0) {
591
+ logger?.warn?.(
592
+ { messageId: message.id },
593
+ "User message content array resulted in empty LangChain content"
594
+ );
595
+ }
603
596
  return {
604
597
  id: message.id,
605
598
  role: message.role,
606
- content: message.content.filter((m) => m.type === "text"),
599
+ content: langChainContent,
607
600
  type: "human"
608
601
  };
609
602
  }
@@ -648,6 +641,22 @@ function isValidJson(json) {
648
641
  return false;
649
642
  }
650
643
  }
644
+ function convertBinaryToImageUrl(part, messageId, logger) {
645
+ if (!part.mimeType.startsWith("image/")) {
646
+ logger?.debug?.(
647
+ { mimeType: part.mimeType, messageId },
648
+ "Skipping non-image binary content"
649
+ );
650
+ return void 0;
651
+ }
652
+ if (part.url) {
653
+ return part.url;
654
+ }
655
+ if (part.data) {
656
+ return `data:${part.mimeType};base64,${part.data}`;
657
+ }
658
+ return void 0;
659
+ }
651
660
 
652
661
  // src/checkpoint.ts
653
662
  import {
@@ -904,11 +913,368 @@ var TDAISaver = class extends BaseCheckpointSaver {
904
913
  }
905
914
  };
906
915
 
916
+ // src/cloudbase-saver.ts
917
+ import {
918
+ BaseCheckpointSaver as BaseCheckpointSaver3
919
+ } from "@langchain/langgraph";
920
+
921
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/serde/types.js
922
+ var ERROR = "__error__";
923
+ var SCHEDULED = "__scheduled__";
924
+ var INTERRUPT = "__interrupt__";
925
+ var RESUME = "__resume__";
926
+
927
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/serde/jsonplus.js
928
+ import { load } from "@langchain/core/load";
929
+
930
+ // ../../../node_modules/.pnpm/@langchain+langgraph-checkpoint@1.0.0_@langchain+core@1.1.16_@opentelemetry+api@1.9.0_@_6d00458ac2f8320fcf28949c4b5d31a3/node_modules/@langchain/langgraph-checkpoint/dist/base.js
931
+ function deepCopy(obj) {
932
+ if (typeof obj !== "object" || obj === null) return obj;
933
+ const newObj = Array.isArray(obj) ? [] : {};
934
+ for (const key in obj) if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = deepCopy(obj[key]);
935
+ return newObj;
936
+ }
937
+ function copyCheckpoint(checkpoint) {
938
+ return {
939
+ v: checkpoint.v,
940
+ id: checkpoint.id,
941
+ ts: checkpoint.ts,
942
+ channel_values: { ...checkpoint.channel_values },
943
+ channel_versions: { ...checkpoint.channel_versions },
944
+ versions_seen: deepCopy(checkpoint.versions_seen)
945
+ };
946
+ }
947
+ var WRITES_IDX_MAP = {
948
+ [ERROR]: -1,
949
+ [SCHEDULED]: -2,
950
+ [INTERRUPT]: -3,
951
+ [RESUME]: -4
952
+ };
953
+
954
+ // src/cloudbase-saver.ts
955
+ var CloudBaseSaver = class extends BaseCheckpointSaver3 {
956
+ constructor(config) {
957
+ super();
958
+ this.db = config.db;
959
+ this.userId = config.userId;
960
+ this.agentId = config.agentId ?? "default";
961
+ this.checkpointsCollection = config.checkpointsCollection ?? "checkpoints";
962
+ this.writesCollection = config.writesCollection ?? "checkpoint_writes";
963
+ }
964
+ /**
965
+ * Setup the required collections in CloudBase.
966
+ * Call this once before using the saver. Safe to call multiple times.
967
+ */
968
+ async setup() {
969
+ const createIfNotExists = async (name) => {
970
+ try {
971
+ await this.db.createCollection(name);
972
+ } catch (e) {
973
+ const error = e;
974
+ if (error.code !== "DATABASE_COLLECTION_ALREADY_EXIST") {
975
+ throw e;
976
+ }
977
+ }
978
+ };
979
+ await Promise.all([
980
+ createIfNotExists(this.checkpointsCollection),
981
+ createIfNotExists(this.writesCollection)
982
+ ]);
983
+ }
984
+ /**
985
+ * Serialize data to JSON object using serde.
986
+ * CloudBase is a document database, so we store JSON directly instead of Base64.
987
+ * Binary data (Uint8Array) is not supported - fail early if encountered.
988
+ */
989
+ async serialize(data) {
990
+ const [type, bytes] = await this.serde.dumpsTyped(data);
991
+ if (type === "bytes") {
992
+ throw new Error(
993
+ "Binary data (Uint8Array) is not supported in CloudBaseSaver. CloudBase is a document database that only supports JSON-serializable data."
994
+ );
995
+ }
996
+ const jsonString = new TextDecoder().decode(bytes);
997
+ return JSON.parse(jsonString);
998
+ }
999
+ /**
1000
+ * Deserialize JSON object back to data using serde.
1001
+ */
1002
+ async deserialize(data) {
1003
+ const jsonString = JSON.stringify(data);
1004
+ const bytes = new TextEncoder().encode(jsonString);
1005
+ return this.serde.loadsTyped("json", bytes);
1006
+ }
1007
+ /**
1008
+ * Retrieves a checkpoint from CloudBase based on the provided config.
1009
+ */
1010
+ async getTuple(config) {
1011
+ const {
1012
+ thread_id,
1013
+ checkpoint_ns = "",
1014
+ checkpoint_id
1015
+ } = config.configurable ?? {};
1016
+ if (!thread_id) {
1017
+ return void 0;
1018
+ }
1019
+ const query = {
1020
+ _openid: this.userId,
1021
+ agent_id: this.agentId,
1022
+ thread_id,
1023
+ checkpoint_ns
1024
+ };
1025
+ if (checkpoint_id) {
1026
+ query.checkpoint_id = checkpoint_id;
1027
+ }
1028
+ const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").limit(1).get();
1029
+ if (docs.length === 0) {
1030
+ return void 0;
1031
+ }
1032
+ const doc = docs[0];
1033
+ const checkpointId = doc.checkpoint_id;
1034
+ const checkpoint = await this.deserialize(doc.checkpoint);
1035
+ const metadata = await this.deserialize(
1036
+ doc.metadata
1037
+ );
1038
+ const writeDocs = [];
1039
+ let writeSkip = 0;
1040
+ const writePageSize = 100;
1041
+ while (true) {
1042
+ const { data: docs2 } = await this.db.collection(this.writesCollection).where({
1043
+ _openid: this.userId,
1044
+ agent_id: this.agentId,
1045
+ thread_id,
1046
+ checkpoint_ns,
1047
+ checkpoint_id: checkpointId
1048
+ }).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
1049
+ writeDocs.push(...docs2);
1050
+ if (docs2.length < writePageSize) break;
1051
+ writeSkip += docs2.length;
1052
+ }
1053
+ const pendingWrites = await Promise.all(
1054
+ writeDocs.map(async (writeDoc) => {
1055
+ const value = await this.deserialize(writeDoc.value);
1056
+ return [
1057
+ writeDoc.task_id,
1058
+ writeDoc.channel,
1059
+ value
1060
+ ];
1061
+ })
1062
+ );
1063
+ return {
1064
+ config: {
1065
+ configurable: {
1066
+ thread_id,
1067
+ checkpoint_ns,
1068
+ checkpoint_id: checkpointId
1069
+ }
1070
+ },
1071
+ checkpoint,
1072
+ metadata,
1073
+ pendingWrites,
1074
+ parentConfig: doc.parent_checkpoint_id ? {
1075
+ configurable: {
1076
+ thread_id,
1077
+ checkpoint_ns,
1078
+ checkpoint_id: doc.parent_checkpoint_id
1079
+ }
1080
+ } : void 0
1081
+ };
1082
+ }
1083
+ /**
1084
+ * List checkpoints from CloudBase, ordered by checkpoint_id descending.
1085
+ */
1086
+ async *list(config, options) {
1087
+ const { limit, before, filter } = options ?? {};
1088
+ const thread_id = config.configurable?.thread_id;
1089
+ const checkpoint_ns = config.configurable?.checkpoint_ns;
1090
+ if (!thread_id) {
1091
+ throw new Error("Thread ID is required");
1092
+ }
1093
+ const _ = this.db.command;
1094
+ const query = {
1095
+ _openid: this.userId,
1096
+ agent_id: this.agentId,
1097
+ thread_id
1098
+ };
1099
+ if (checkpoint_ns !== void 0 && checkpoint_ns !== null) {
1100
+ query.checkpoint_ns = checkpoint_ns;
1101
+ }
1102
+ if (before?.configurable?.checkpoint_id) {
1103
+ query.checkpoint_id = _.lt(before.configurable.checkpoint_id);
1104
+ }
1105
+ const validCheckpointMetadataKeys = ["source", "step", "writes", "parents"];
1106
+ if (filter) {
1107
+ for (const [key, value] of Object.entries(filter)) {
1108
+ if (value !== void 0 && validCheckpointMetadataKeys.includes(key)) {
1109
+ query[`metadata.${key}`] = value;
1110
+ }
1111
+ }
1112
+ }
1113
+ let skip = 0;
1114
+ const pageSize = Math.min(limit ?? 100, 100);
1115
+ let yielded = 0;
1116
+ while (true) {
1117
+ const { data: docs } = await this.db.collection(this.checkpointsCollection).where(query).orderBy("checkpoint_id", "desc").skip(skip).limit(pageSize).get();
1118
+ if (docs.length === 0) break;
1119
+ for (const doc of docs) {
1120
+ const checkpoint = await this.deserialize(
1121
+ doc.checkpoint
1122
+ );
1123
+ const metadata = await this.deserialize(
1124
+ doc.metadata
1125
+ );
1126
+ const writeDocs = [];
1127
+ let writeSkip = 0;
1128
+ const writePageSize = 100;
1129
+ while (true) {
1130
+ const { data: pageWriteDocs } = await this.db.collection(this.writesCollection).where({
1131
+ _openid: this.userId,
1132
+ agent_id: this.agentId,
1133
+ thread_id: doc.thread_id,
1134
+ checkpoint_ns: doc.checkpoint_ns,
1135
+ checkpoint_id: doc.checkpoint_id
1136
+ }).orderBy("idx", "asc").skip(writeSkip).limit(writePageSize).get();
1137
+ writeDocs.push(...pageWriteDocs);
1138
+ if (pageWriteDocs.length < writePageSize) break;
1139
+ writeSkip += pageWriteDocs.length;
1140
+ }
1141
+ const pendingWrites = await Promise.all(
1142
+ writeDocs.map(async (writeDoc) => {
1143
+ const value = await this.deserialize(writeDoc.value);
1144
+ return [
1145
+ writeDoc.task_id,
1146
+ writeDoc.channel,
1147
+ value
1148
+ ];
1149
+ })
1150
+ );
1151
+ yield {
1152
+ config: {
1153
+ configurable: {
1154
+ thread_id: doc.thread_id,
1155
+ checkpoint_ns: doc.checkpoint_ns,
1156
+ checkpoint_id: doc.checkpoint_id
1157
+ }
1158
+ },
1159
+ checkpoint,
1160
+ metadata,
1161
+ pendingWrites,
1162
+ parentConfig: doc.parent_checkpoint_id ? {
1163
+ configurable: {
1164
+ thread_id: doc.thread_id,
1165
+ checkpoint_ns: doc.checkpoint_ns,
1166
+ checkpoint_id: doc.parent_checkpoint_id
1167
+ }
1168
+ } : void 0
1169
+ };
1170
+ yielded++;
1171
+ if (limit && yielded >= limit) return;
1172
+ }
1173
+ skip += docs.length;
1174
+ if (docs.length < pageSize) break;
1175
+ }
1176
+ }
1177
+ /**
1178
+ * Save a checkpoint to CloudBase.
1179
+ */
1180
+ async put(config, checkpoint, metadata) {
1181
+ if (!config.configurable) {
1182
+ throw new Error("Empty configuration supplied.");
1183
+ }
1184
+ const thread_id = config.configurable.thread_id;
1185
+ const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
1186
+ const checkpoint_id = checkpoint.id;
1187
+ if (!thread_id) {
1188
+ throw new Error(
1189
+ 'Missing "thread_id" field in passed "config.configurable".'
1190
+ );
1191
+ }
1192
+ const preparedCheckpoint = copyCheckpoint(checkpoint);
1193
+ const serializedCheckpoint = await this.serialize(preparedCheckpoint);
1194
+ const serializedMetadata = await this.serialize(metadata);
1195
+ const doc = {
1196
+ _openid: this.userId,
1197
+ agent_id: this.agentId,
1198
+ thread_id,
1199
+ checkpoint_ns,
1200
+ checkpoint_id,
1201
+ parent_checkpoint_id: config.configurable.checkpoint_id ?? null,
1202
+ checkpoint: serializedCheckpoint,
1203
+ metadata: serializedMetadata,
1204
+ created_at: /* @__PURE__ */ new Date()
1205
+ };
1206
+ const docId = `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}`;
1207
+ await this.db.collection(this.checkpointsCollection).doc(docId).set(doc);
1208
+ return {
1209
+ configurable: {
1210
+ thread_id,
1211
+ checkpoint_ns,
1212
+ checkpoint_id
1213
+ }
1214
+ };
1215
+ }
1216
+ /**
1217
+ * Save intermediate writes to CloudBase.
1218
+ */
1219
+ async putWrites(config, writes, taskId) {
1220
+ if (!config.configurable) {
1221
+ throw new Error("Empty configuration supplied.");
1222
+ }
1223
+ if (!config.configurable.thread_id) {
1224
+ throw new Error("Missing thread_id field in config.configurable.");
1225
+ }
1226
+ if (!config.configurable.checkpoint_id) {
1227
+ throw new Error("Missing checkpoint_id field in config.configurable.");
1228
+ }
1229
+ const thread_id = config.configurable.thread_id;
1230
+ const checkpoint_ns = config.configurable.checkpoint_ns ?? "";
1231
+ const checkpoint_id = config.configurable.checkpoint_id;
1232
+ const serializedWrites = await Promise.all(
1233
+ writes.map(async ([channel, value], idx) => ({
1234
+ channel,
1235
+ value: await this.serialize(value),
1236
+ idx,
1237
+ docId: `${this.agentId}:${thread_id}:${checkpoint_ns}:${checkpoint_id}:${taskId}:${idx}`
1238
+ }))
1239
+ );
1240
+ await this.db.runTransaction(async (transaction) => {
1241
+ for (const { channel, value, idx, docId } of serializedWrites) {
1242
+ await transaction.collection(this.writesCollection).doc(docId).set({
1243
+ _openid: this.userId,
1244
+ agent_id: this.agentId,
1245
+ thread_id,
1246
+ checkpoint_ns,
1247
+ checkpoint_id,
1248
+ task_id: taskId,
1249
+ idx,
1250
+ channel,
1251
+ value
1252
+ });
1253
+ }
1254
+ });
1255
+ }
1256
+ /**
1257
+ * Delete all checkpoints and writes for a thread.
1258
+ */
1259
+ async deleteThread(threadId) {
1260
+ await this.db.collection(this.checkpointsCollection).where({
1261
+ _openid: this.userId,
1262
+ agent_id: this.agentId,
1263
+ thread_id: threadId
1264
+ }).remove();
1265
+ await this.db.collection(this.writesCollection).where({
1266
+ _openid: this.userId,
1267
+ agent_id: this.agentId,
1268
+ thread_id: threadId
1269
+ }).remove();
1270
+ }
1271
+ };
1272
+
907
1273
  // src/store/tdai-store.ts
908
1274
  import {
909
- BaseStore
1275
+ BaseStore as BaseStore2
910
1276
  } from "@langchain/langgraph";
911
- var TDAIStore = class extends BaseStore {
1277
+ var TDAIStore = class extends BaseStore2 {
912
1278
  constructor(config) {
913
1279
  super();
914
1280
  this.isSetup = false;
@@ -1337,6 +1703,7 @@ import { noopLogger as noopLogger2, createConsoleLogger } from "@cloudbase/agent
1337
1703
  export {
1338
1704
  ClientPropertiesAnnotation,
1339
1705
  ClientStateAnnotation,
1706
+ CloudBaseSaver,
1340
1707
  LanggraphAgent,
1341
1708
  TDAISaver,
1342
1709
  TDAIStore,