@axiom-lattice/core 2.1.23 → 2.1.24

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.js CHANGED
@@ -37,42 +37,73 @@ __export(index_exports, {
37
37
  AgentType: () => import_protocols.AgentType,
38
38
  ChunkBuffer: () => ChunkBuffer,
39
39
  ChunkBufferLatticeManager: () => ChunkBufferLatticeManager,
40
+ CompositeBackend: () => CompositeBackend,
40
41
  ConsoleLoggerClient: () => ConsoleLoggerClient,
41
42
  DefaultScheduleClient: () => DefaultScheduleClient,
43
+ EMPTY_CONTENT_WARNING: () => EMPTY_CONTENT_WARNING,
42
44
  EmbeddingsLatticeManager: () => EmbeddingsLatticeManager,
43
45
  FileSystemSkillStore: () => FileSystemSkillStore,
46
+ FilesystemBackend: () => FilesystemBackend,
44
47
  GraphBuildOptions: () => import_protocols.GraphBuildOptions,
45
48
  InMemoryAssistantStore: () => InMemoryAssistantStore,
46
49
  InMemoryChunkBuffer: () => InMemoryChunkBuffer,
50
+ InMemoryDatabaseConfigStore: () => InMemoryDatabaseConfigStore,
51
+ InMemoryMailboxStore: () => InMemoryMailboxStore,
52
+ InMemoryTaskListStore: () => InMemoryTaskListStore,
47
53
  InMemoryThreadStore: () => InMemoryThreadStore,
54
+ LINE_NUMBER_WIDTH: () => LINE_NUMBER_WIDTH,
48
55
  LoggerLatticeManager: () => LoggerLatticeManager,
56
+ MAX_LINE_LENGTH: () => MAX_LINE_LENGTH,
49
57
  McpLatticeManager: () => McpLatticeManager,
58
+ MemoryBackend: () => MemoryBackend,
50
59
  MemoryLatticeManager: () => MemoryLatticeManager,
51
60
  MemoryQueueClient: () => MemoryQueueClient,
52
61
  MemoryScheduleStorage: () => MemoryScheduleStorage,
53
62
  MemoryType: () => import_protocols2.MemoryType,
63
+ MessageType: () => MessageType,
54
64
  ModelLatticeManager: () => ModelLatticeManager,
55
65
  PinoLoggerClient: () => PinoLoggerClient,
56
66
  PostgresDatabase: () => PostgresDatabase,
57
67
  Protocols: () => Protocols,
58
68
  QueueLatticeManager: () => QueueLatticeManager,
69
+ SandboxFilesystem: () => SandboxFilesystem,
59
70
  SandboxLatticeManager: () => SandboxLatticeManager,
60
71
  ScheduleLatticeManager: () => ScheduleLatticeManager,
61
72
  SkillLatticeManager: () => SkillLatticeManager,
62
73
  SqlDatabaseManager: () => SqlDatabaseManager,
74
+ StateBackend: () => StateBackend,
75
+ StoreBackend: () => StoreBackend,
63
76
  StoreLatticeManager: () => StoreLatticeManager,
77
+ TOOL_RESULT_TOKEN_LIMIT: () => TOOL_RESULT_TOKEN_LIMIT,
78
+ TRUNCATION_GUIDANCE: () => TRUNCATION_GUIDANCE,
79
+ TaskStatus: () => TaskStatus,
80
+ TeamAgentGraphBuilder: () => TeamAgentGraphBuilder,
64
81
  ThreadStatus: () => ThreadStatus,
65
82
  ToolLatticeManager: () => ToolLatticeManager,
66
83
  VectorStoreLatticeManager: () => VectorStoreLatticeManager,
67
84
  agentLatticeManager: () => agentLatticeManager,
85
+ buildGrepResultsDict: () => buildGrepResultsDict,
86
+ checkEmptyContent: () => checkEmptyContent,
87
+ clearEncryptionKeyCache: () => clearEncryptionKeyCache,
88
+ createAgentTeam: () => createAgentTeam,
89
+ createFileData: () => createFileData,
68
90
  createInfoSqlTool: () => createInfoSqlTool,
69
91
  createListTablesSqlTool: () => createListTablesSqlTool,
70
92
  createQueryCheckerSqlTool: () => createQueryCheckerSqlTool,
71
93
  createQuerySqlTool: () => createQuerySqlTool,
94
+ createTeamMiddleware: () => createTeamMiddleware,
95
+ createTeammateTools: () => createTeammateTools,
96
+ decrypt: () => decrypt,
72
97
  describeCronExpression: () => describeCronExpression,
73
98
  embeddingsLatticeManager: () => embeddingsLatticeManager,
99
+ encrypt: () => encrypt,
74
100
  eventBus: () => eventBus,
75
101
  eventBusDefault: () => event_bus_default,
102
+ fileDataToString: () => fileDataToString,
103
+ formatContentWithLineNumbers: () => formatContentWithLineNumbers,
104
+ formatGrepMatches: () => formatGrepMatches,
105
+ formatGrepResults: () => formatGrepResults,
106
+ formatReadResponse: () => formatReadResponse,
76
107
  getAgentClient: () => getAgentClient,
77
108
  getAgentConfig: () => getAgentConfig,
78
109
  getAgentLattice: () => getAgentLattice,
@@ -82,6 +113,7 @@ __export(index_exports, {
82
113
  getChunkBuffer: () => getChunkBuffer,
83
114
  getEmbeddingsClient: () => getEmbeddingsClient,
84
115
  getEmbeddingsLattice: () => getEmbeddingsLattice,
116
+ getEncryptionKey: () => getEncryptionKey,
85
117
  getLoggerLattice: () => getLoggerLattice,
86
118
  getModelLattice: () => getModelLattice,
87
119
  getNextCronTime: () => getNextCronTime,
@@ -94,7 +126,11 @@ __export(index_exports, {
94
126
  getToolLattice: () => getToolLattice,
95
127
  getVectorStoreClient: () => getVectorStoreClient,
96
128
  getVectorStoreLattice: () => getVectorStoreLattice,
129
+ globSearchFiles: () => globSearchFiles,
130
+ grepMatchesFromFiles: () => grepMatchesFromFiles,
131
+ grepSearchFiles: () => grepSearchFiles,
97
132
  hasChunkBuffer: () => hasChunkBuffer,
133
+ isUsingDefaultKey: () => isUsingDefaultKey,
98
134
  isValidCronExpression: () => isValidCronExpression,
99
135
  isValidSandboxName: () => isValidSandboxName,
100
136
  isValidSkillName: () => isValidSkillName,
@@ -103,6 +139,7 @@ __export(index_exports, {
103
139
  modelLatticeManager: () => modelLatticeManager,
104
140
  normalizeSandboxName: () => normalizeSandboxName,
105
141
  parseCronExpression: () => parseCronExpression,
142
+ performStringReplacement: () => performStringReplacement,
106
143
  queueLatticeManager: () => queueLatticeManager,
107
144
  registerAgentLattice: () => registerAgentLattice,
108
145
  registerAgentLattices: () => registerAgentLattices,
@@ -115,15 +152,22 @@ __export(index_exports, {
115
152
  registerQueueLattice: () => registerQueueLattice,
116
153
  registerScheduleLattice: () => registerScheduleLattice,
117
154
  registerStoreLattice: () => registerStoreLattice,
155
+ registerTeammateAgent: () => registerTeammateAgent,
118
156
  registerToolLattice: () => registerToolLattice,
119
157
  registerVectorStoreLattice: () => registerVectorStoreLattice,
120
158
  sandboxLatticeManager: () => sandboxLatticeManager,
159
+ sanitizeToolCallId: () => sanitizeToolCallId,
121
160
  scheduleLatticeManager: () => scheduleLatticeManager,
122
161
  skillLatticeManager: () => skillLatticeManager,
123
162
  sqlDatabaseManager: () => sqlDatabaseManager,
124
163
  storeLatticeManager: () => storeLatticeManager,
125
164
  toolLatticeManager: () => toolLatticeManager,
165
+ truncateIfTooLong: () => truncateIfTooLong,
166
+ unregisterTeammateAgent: () => unregisterTeammateAgent,
167
+ updateFileData: () => updateFileData,
126
168
  validateAgentInput: () => validateAgentInput,
169
+ validateEncryptionKey: () => validateEncryptionKey,
170
+ validatePath: () => validatePath,
127
171
  validateSkillName: () => validateSkillName,
128
172
  validateToolInput: () => validateToolInput,
129
173
  vectorStoreLatticeManager: () => vectorStoreLatticeManager
@@ -636,11 +680,11 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
636
680
  * @param key Lattice键名
637
681
  * @param tool 已有的StructuredTool实例
638
682
  */
639
- registerExistingTool(key, tool36) {
683
+ registerExistingTool(key, tool38) {
640
684
  const config = {
641
- name: tool36.name,
642
- description: tool36.description,
643
- schema: tool36.schema,
685
+ name: tool38.name,
686
+ description: tool38.description,
687
+ schema: tool38.schema,
644
688
  // StructuredTool的schema已经是Zod兼容的
645
689
  needUserApprove: false
646
690
  // MCP工具默认不需要用户批准
@@ -648,7 +692,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
648
692
  const toolLattice = {
649
693
  key,
650
694
  config,
651
- client: tool36
695
+ client: tool38
652
696
  };
653
697
  this.register(key, toolLattice);
654
698
  }
@@ -674,7 +718,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
674
718
  };
675
719
  var toolLatticeManager = ToolLatticeManager.getInstance();
676
720
  var registerToolLattice = (key, config, executor) => toolLatticeManager.registerLattice(key, config, executor);
677
- var registerExistingTool = (key, tool36) => toolLatticeManager.registerExistingTool(key, tool36);
721
+ var registerExistingTool = (key, tool38) => toolLatticeManager.registerExistingTool(key, tool38);
678
722
  var getToolLattice = (key) => toolLatticeManager.getToolLattice(key);
679
723
  var getToolDefinition = (key) => toolLatticeManager.getToolDefinition(key);
680
724
  var getToolClient = (key) => toolLatticeManager.getToolClient(key);
@@ -746,7 +790,7 @@ var PostgresDatabase = class {
746
790
  this.config = config;
747
791
  }
748
792
  async connect() {
749
- if (this.connected) return;
793
+ if (this.connected && this.pool) return;
750
794
  try {
751
795
  const { Pool } = await import("pg");
752
796
  const poolConfig = this.config.connectionString ? { connectionString: this.config.connectionString } : {
@@ -759,16 +803,27 @@ var PostgresDatabase = class {
759
803
  };
760
804
  this.pool = new Pool(poolConfig);
761
805
  const client = await this.pool.connect();
762
- client.release();
806
+ try {
807
+ await client.query("SELECT 1");
808
+ } finally {
809
+ client.release();
810
+ }
763
811
  this.connected = true;
764
812
  } catch (error) {
813
+ this.connected = false;
765
814
  throw new Error(`Failed to connect to PostgreSQL: ${error}`);
766
815
  }
767
816
  }
768
817
  async disconnect() {
769
818
  if (this.pool) {
770
- await this.pool.end();
771
- this.connected = false;
819
+ try {
820
+ await this.pool.end();
821
+ } catch (error) {
822
+ console.warn("Warning: Error closing PostgreSQL pool:", error);
823
+ } finally {
824
+ this.pool = null;
825
+ this.connected = false;
826
+ }
772
827
  }
773
828
  }
774
829
  async listTables() {
@@ -964,45 +1019,55 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
964
1019
  await database.disconnect();
965
1020
  }
966
1021
  }
967
- };
968
- function parseConnectionString(connectionString) {
969
- const url = new URL(connectionString);
970
- const protocol = url.protocol.replace(":", "");
971
- if (protocol !== "postgres" && protocol !== "postgresql") {
972
- throw new Error(`Unsupported protocol: ${protocol}. Only postgresql:// is supported.`);
1022
+ /**
1023
+ * Load database configurations from a DatabaseConfigStore
1024
+ * and register them with this manager
1025
+ *
1026
+ * @param store - The database configuration store
1027
+ * @param tenantId - Tenant identifier
1028
+ */
1029
+ async loadConfigsFromStore(store, tenantId) {
1030
+ const configs = await store.getAllConfigs(tenantId);
1031
+ for (const entry of configs) {
1032
+ this.registerDatabase(entry.key, entry.config);
1033
+ }
973
1034
  }
974
- return {
975
- host: url.hostname || "localhost",
976
- port: parseInt(url.port || "5432"),
977
- database: url.pathname.slice(1) || url.pathname,
978
- user: url.username,
979
- password: url.password,
980
- ssl: url.searchParams.get("sslmode") === "require"
981
- };
982
- }
1035
+ /**
1036
+ * Load all database configurations from a DatabaseConfigStore
1037
+ * across all tenants and register them with this manager
1038
+ *
1039
+ * @param store - The database configuration store
1040
+ */
1041
+ async loadAllConfigsFromStore(store) {
1042
+ const configs = await store.getAllConfigsWithoutTenant();
1043
+ for (const entry of configs) {
1044
+ this.registerDatabase(entry.key, entry.config);
1045
+ }
1046
+ }
1047
+ };
983
1048
  var sqlDatabaseManager = SqlDatabaseManager.getInstance();
984
1049
 
985
1050
  // src/tool_lattice/sql/list_tables_sql.ts
986
1051
  var import_zod3 = __toESM(require("zod"));
987
1052
  var import_langchain = require("langchain");
988
1053
  var LIST_TABLES_SQL_DESCRIPTION = `List all tables in the connected SQL database. Returns a comma-separated list of table names. Use this tool first to understand what tables are available before querying the database.`;
989
- var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
1054
+ var createListTablesSqlTool = ({ databaseKeys, databaseDescriptions }) => {
1055
+ const availableDbsText = databaseKeys.length > 0 ? `
1056
+
1057
+ Available databases:
1058
+ ${databaseKeys.map(
1059
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1060
+ ).join("\n")}` : "";
990
1061
  return (0, import_langchain.tool)(
991
- async (_input, exe_config) => {
1062
+ async ({ databaseKey }, exe_config) => {
992
1063
  try {
993
- let dbKey;
994
- if (databaseKey) {
995
- dbKey = databaseKey;
996
- } else if (connectionString) {
997
- dbKey = connectionString;
998
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
999
- const config = parseConnectionString(connectionString);
1000
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1001
- }
1002
- } else {
1003
- return "Error: Must provide databaseKey or connectionString";
1064
+ if (!databaseKey) {
1065
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
1004
1066
  }
1005
- const database = sqlDatabaseManager.getDatabase(dbKey);
1067
+ if (!databaseKeys.includes(databaseKey)) {
1068
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1069
+ }
1070
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1006
1071
  const tables = await database.listTables();
1007
1072
  if (tables.length === 0) {
1008
1073
  return "No tables found in the database.";
@@ -1017,8 +1082,10 @@ var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
1017
1082
  },
1018
1083
  {
1019
1084
  name: "list_tables_sql",
1020
- description: LIST_TABLES_SQL_DESCRIPTION,
1021
- schema: import_zod3.default.object({})
1085
+ description: `${LIST_TABLES_SQL_DESCRIPTION}${availableDbsText}`,
1086
+ schema: import_zod3.default.object({
1087
+ databaseKey: import_zod3.default.string().describe(`Target database to list tables. Choose from: ${databaseKeys.join(", ")}`)
1088
+ })
1022
1089
  }
1023
1090
  );
1024
1091
  };
@@ -1027,49 +1094,26 @@ var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
1027
1094
  var import_zod4 = __toESM(require("zod"));
1028
1095
  var import_langchain2 = require("langchain");
1029
1096
  var INFO_SQL_DESCRIPTION = `Get detailed schema information for specified tables, including column names, types, constraints (primary keys, foreign keys), and sample rows. Input should be a comma-separated list of table names.`;
1030
- function formatTableSchema(schema) {
1031
- const lines = [];
1032
- lines.push(`
1033
- Table: ${schema.tableName}`);
1034
- lines.push("-".repeat(40));
1035
- lines.push("Columns:");
1036
- for (const col of schema.columns) {
1037
- const constraints = [];
1038
- if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
1039
- if (col.isForeignKey && col.foreignKeyRef)
1040
- constraints.push(`FK -> ${col.foreignKeyRef}`);
1041
- if (!col.nullable) constraints.push("NOT NULL");
1042
- const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
1043
- lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
1044
- }
1045
- if (schema.sampleRows && schema.sampleRows.length > 0) {
1046
- lines.push("\nSample Rows (up to 3):");
1047
- for (const row of schema.sampleRows) {
1048
- const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
1049
- lines.push(` ${rowStr}`);
1050
- }
1051
- }
1052
- return lines.join("\n");
1053
- }
1054
- var createInfoSqlTool = ({ databaseKey, connectionString }) => {
1097
+ var createInfoSqlTool = ({ databaseKeys, databaseDescriptions }) => {
1098
+ const availableDbsText = databaseKeys.length > 0 ? `
1099
+
1100
+ Available databases:
1101
+ ${databaseKeys.map(
1102
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1103
+ ).join("\n")}` : "";
1055
1104
  return (0, import_langchain2.tool)(
1056
1105
  async ({
1057
- tables
1106
+ tables,
1107
+ databaseKey
1058
1108
  }, exe_config) => {
1059
1109
  try {
1060
- let dbKey;
1061
- if (databaseKey) {
1062
- dbKey = databaseKey;
1063
- } else if (connectionString) {
1064
- dbKey = connectionString;
1065
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
1066
- const config = parseConnectionString(connectionString);
1067
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1068
- }
1069
- } else {
1070
- return "Error: Must provide databaseKey or connectionString";
1110
+ if (!databaseKey) {
1111
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
1112
+ }
1113
+ if (!databaseKeys.includes(databaseKey)) {
1114
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1071
1115
  }
1072
- const database = sqlDatabaseManager.getDatabase(dbKey);
1116
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1073
1117
  const tableNames = tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
1074
1118
  if (tableNames.length === 0) {
1075
1119
  return "Error: No table names provided. Please provide a comma-separated list of table names.";
@@ -1086,15 +1130,40 @@ var createInfoSqlTool = ({ databaseKey, connectionString }) => {
1086
1130
  },
1087
1131
  {
1088
1132
  name: "info_sql",
1089
- description: INFO_SQL_DESCRIPTION,
1133
+ description: `${INFO_SQL_DESCRIPTION}${availableDbsText}`,
1090
1134
  schema: import_zod4.default.object({
1091
1135
  tables: import_zod4.default.string().describe(
1092
1136
  "Comma-separated list of table names to get information for. Example: 'users, orders, products'"
1093
- )
1137
+ ),
1138
+ databaseKey: import_zod4.default.string().describe(`Target database to get table info. Choose from: ${databaseKeys.join(", ")}`)
1094
1139
  })
1095
1140
  }
1096
1141
  );
1097
1142
  };
1143
+ function formatTableSchema(schema) {
1144
+ const lines = [];
1145
+ lines.push(`
1146
+ Table: ${schema.tableName}`);
1147
+ lines.push("-".repeat(40));
1148
+ lines.push("Columns:");
1149
+ for (const col of schema.columns) {
1150
+ const constraints = [];
1151
+ if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
1152
+ if (col.isForeignKey && col.foreignKeyRef)
1153
+ constraints.push(`FK -> ${col.foreignKeyRef}`);
1154
+ if (!col.nullable) constraints.push("NOT NULL");
1155
+ const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
1156
+ lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
1157
+ }
1158
+ if (schema.sampleRows && schema.sampleRows.length > 0) {
1159
+ lines.push("\nSample Rows (up to 3):");
1160
+ for (const row of schema.sampleRows) {
1161
+ const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
1162
+ lines.push(` ${rowStr}`);
1163
+ }
1164
+ }
1165
+ return lines.join("\n");
1166
+ }
1098
1167
 
1099
1168
  // src/tool_lattice/sql/query_checker_sql.ts
1100
1169
  var import_zod5 = __toESM(require("zod"));
@@ -1156,10 +1225,17 @@ function checkDangerousOperations(query) {
1156
1225
  }
1157
1226
  return warnings;
1158
1227
  }
1159
- var createQueryCheckerSqlTool = ({ databaseKey, connectionString }) => {
1228
+ var createQueryCheckerSqlTool = ({ databaseKeys, databaseDescriptions }) => {
1229
+ const availableDbsText = databaseKeys.length > 0 ? `
1230
+
1231
+ Available databases:
1232
+ ${databaseKeys.map(
1233
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1234
+ ).join("\n")}` : "";
1160
1235
  return (0, import_langchain3.tool)(
1161
1236
  async ({
1162
- query
1237
+ query,
1238
+ databaseKey
1163
1239
  }, exe_config) => {
1164
1240
  try {
1165
1241
  const trimmedQuery = query.trim();
@@ -1191,19 +1267,9 @@ ${trimmedQuery}
1191
1267
  } else {
1192
1268
  results.push("Safety: No dangerous operations detected");
1193
1269
  }
1194
- try {
1195
- let dbKey;
1196
- if (databaseKey) {
1197
- dbKey = databaseKey;
1198
- } else if (connectionString) {
1199
- dbKey = connectionString;
1200
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
1201
- const config = parseConnectionString(connectionString);
1202
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1203
- }
1204
- }
1205
- if (dbKey) {
1206
- const database = sqlDatabaseManager.getDatabase(dbKey);
1270
+ if (databaseKey && databaseKeys.includes(databaseKey)) {
1271
+ try {
1272
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1207
1273
  const dbType = database.getDatabaseType();
1208
1274
  if (dbType === "postgres") {
1209
1275
  try {
@@ -1215,10 +1281,14 @@ ${trimmedQuery}
1215
1281
  );
1216
1282
  }
1217
1283
  }
1284
+ } catch {
1285
+ results.push(
1286
+ "Database Validation: Skipped (database connection unavailable)"
1287
+ );
1218
1288
  }
1219
- } catch {
1289
+ } else {
1220
1290
  results.push(
1221
- "Database Validation: Skipped (no database connection available)"
1291
+ "Database Validation: Skipped (no valid databaseKey provided)"
1222
1292
  );
1223
1293
  }
1224
1294
  const hasErrors = syntaxIssues.some((i) => !i.startsWith("Warning:")) || dangerWarnings.length > 0;
@@ -1237,9 +1307,10 @@ ${trimmedQuery}
1237
1307
  },
1238
1308
  {
1239
1309
  name: "query_checker_sql",
1240
- description: "Check a SQL query for common issues before execution. This tool validates syntax, checks for dangerous operations, and provides suggestions for improvement. Use this before executing queries to ensure they are safe and correct.",
1310
+ description: `Check a SQL query for common issues before execution. This tool validates syntax, checks for dangerous operations, and provides suggestions for improvement. Use this before executing queries to ensure they are safe and correct.${availableDbsText}`,
1241
1311
  schema: import_zod5.default.object({
1242
- query: import_zod5.default.string().describe("The SQL query to check and validate.")
1312
+ query: import_zod5.default.string().describe("The SQL query to check and validate."),
1313
+ databaseKey: import_zod5.default.string().describe(`Target database to validate query against. Choose from: ${databaseKeys.join(", ")}`)
1243
1314
  })
1244
1315
  }
1245
1316
  );
@@ -1248,7 +1319,6 @@ ${trimmedQuery}
1248
1319
  // src/tool_lattice/sql/query_sql.ts
1249
1320
  var import_zod6 = __toESM(require("zod"));
1250
1321
  var import_langchain4 = require("langchain");
1251
- var QUERY_SQL_DESCRIPTION = `Execute a SQL query against the database and return the results. Input should be a valid SQL query. Use this tool to retrieve data from the database. For complex queries, first use list_tables_sql and info_sql to understand the database schema.`;
1252
1322
  function formatQueryResult(rows, fields) {
1253
1323
  if (rows.length === 0) {
1254
1324
  return "Query executed successfully. No rows returned.";
@@ -1276,29 +1346,30 @@ function formatQueryResult(rows, fields) {
1276
1346
  Total rows: ${rows.length}`);
1277
1347
  return lines.join("\n");
1278
1348
  }
1279
- var createQuerySqlTool = ({ databaseKey, connectionString }) => {
1349
+ var createQuerySqlTool = ({ databaseKeys, databaseDescriptions }) => {
1350
+ const availableDbsText = databaseKeys.length > 0 ? `
1351
+
1352
+ Available databases:
1353
+ ${databaseKeys.map(
1354
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1355
+ ).join("\n")}` : "";
1280
1356
  return (0, import_langchain4.tool)(
1281
1357
  async ({
1282
- query
1358
+ query,
1359
+ databaseKey
1283
1360
  }, exe_config) => {
1284
1361
  try {
1285
- let dbKey;
1286
- if (databaseKey) {
1287
- dbKey = databaseKey;
1288
- } else if (connectionString) {
1289
- dbKey = connectionString;
1290
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
1291
- const config = parseConnectionString(connectionString);
1292
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1293
- }
1294
- } else {
1295
- return "Error: Must provide databaseKey or connectionString";
1362
+ if (!databaseKey) {
1363
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
1364
+ }
1365
+ if (!databaseKeys.includes(databaseKey)) {
1366
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1296
1367
  }
1297
1368
  const trimmedQuery = query.trim();
1298
1369
  if (!trimmedQuery) {
1299
1370
  return "Error: Empty query provided. Please provide a valid SQL query.";
1300
1371
  }
1301
- const database = sqlDatabaseManager.getDatabase(dbKey);
1372
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1302
1373
  const result = await database.executeQuery(trimmedQuery);
1303
1374
  return formatQueryResult(result.rows, result.fields);
1304
1375
  } catch (error) {
@@ -1307,11 +1378,12 @@ var createQuerySqlTool = ({ databaseKey, connectionString }) => {
1307
1378
  },
1308
1379
  {
1309
1380
  name: "query_sql",
1310
- description: QUERY_SQL_DESCRIPTION,
1381
+ description: `Execute a SQL query against the database and return the results.${availableDbsText}`,
1311
1382
  schema: import_zod6.default.object({
1312
1383
  query: import_zod6.default.string().describe(
1313
1384
  "The SQL query to execute. Should be a valid SELECT, INSERT, UPDATE, or DELETE statement."
1314
- )
1385
+ ),
1386
+ databaseKey: import_zod6.default.string().describe(`Target database to execute the query. Choose from: ${databaseKeys.join(", ")}`)
1315
1387
  })
1316
1388
  }
1317
1389
  );
@@ -2727,8 +2799,8 @@ var SandboxFilesystem = class {
2727
2799
  */
2728
2800
  toVirtualPath(realPath) {
2729
2801
  const rootPath = path2.join(this.homeDir, this.workingDirectory);
2730
- const relative2 = path2.relative(rootPath, realPath);
2731
- const normalized = relative2.split(path2.sep).join("/");
2802
+ const relative3 = path2.relative(rootPath, realPath);
2803
+ const normalized = relative3.split(path2.sep).join("/");
2732
2804
  return "/" + normalized;
2733
2805
  }
2734
2806
  /**
@@ -3010,17 +3082,17 @@ function createBrowserMiddleware(params = { isolatedLevel: "global" }) {
3010
3082
  // src/middlewares/sqlMiddleware.ts
3011
3083
  var import_langchain31 = require("langchain");
3012
3084
  function createSqlMiddleware(params) {
3013
- const { connectionMode, databaseKey, connectionString } = params;
3014
- const toolParams = {};
3015
- if (connectionMode === "databaseKey" && databaseKey) {
3016
- toolParams.databaseKey = databaseKey;
3017
- } else if (connectionString) {
3018
- toolParams.connectionString = connectionString;
3019
- } else if (databaseKey) {
3020
- toolParams.databaseKey = databaseKey;
3021
- } else if (connectionString) {
3022
- toolParams.connectionString = connectionString;
3085
+ const { databaseKeys, databaseDescriptions } = params;
3086
+ if (!databaseKeys || databaseKeys.length === 0) {
3087
+ return (0, import_langchain31.createMiddleware)({
3088
+ name: "sqlMiddleware",
3089
+ tools: []
3090
+ });
3023
3091
  }
3092
+ const toolParams = {
3093
+ databaseKeys,
3094
+ databaseDescriptions
3095
+ };
3024
3096
  return (0, import_langchain31.createMiddleware)({
3025
3097
  name: "sqlMiddleware",
3026
3098
  tools: [
@@ -3685,6 +3757,258 @@ ${body}` : `${frontmatter}
3685
3757
  }
3686
3758
  };
3687
3759
 
3760
+ // src/store_lattice/InMemoryWorkspaceStore.ts
3761
+ var InMemoryWorkspaceStore = class {
3762
+ constructor() {
3763
+ this.workspaces = /* @__PURE__ */ new Map();
3764
+ this.initDefaultData();
3765
+ }
3766
+ initDefaultData() {
3767
+ const defaultTenantId = "default";
3768
+ const now = /* @__PURE__ */ new Date();
3769
+ const defaultWorkspace = {
3770
+ id: "default-workspace",
3771
+ tenantId: defaultTenantId,
3772
+ name: "\u9ED8\u8BA4\u5DE5\u4F5C\u533A",
3773
+ description: "\u7CFB\u7EDF\u9ED8\u8BA4\u5DE5\u4F5C\u533A",
3774
+ storageType: "sandbox",
3775
+ createdAt: now,
3776
+ updatedAt: now
3777
+ };
3778
+ this.workspaces.set(`${defaultTenantId}:default-workspace`, defaultWorkspace);
3779
+ }
3780
+ getKey(tenantId, id) {
3781
+ return `${tenantId}:${id}`;
3782
+ }
3783
+ async getAllWorkspaces(tenantId) {
3784
+ return Array.from(this.workspaces.values()).filter(
3785
+ (w) => w.tenantId === tenantId
3786
+ );
3787
+ }
3788
+ async getWorkspaceById(tenantId, id) {
3789
+ const key = this.getKey(tenantId, id);
3790
+ return this.workspaces.get(key) || null;
3791
+ }
3792
+ async createWorkspace(tenantId, id, data) {
3793
+ const now = /* @__PURE__ */ new Date();
3794
+ const workspace = {
3795
+ id,
3796
+ tenantId,
3797
+ name: data.name,
3798
+ description: data.description,
3799
+ storageType: data.storageType,
3800
+ createdAt: now,
3801
+ updatedAt: now
3802
+ };
3803
+ const key = this.getKey(tenantId, id);
3804
+ this.workspaces.set(key, workspace);
3805
+ return workspace;
3806
+ }
3807
+ async updateWorkspace(tenantId, id, updates) {
3808
+ const key = this.getKey(tenantId, id);
3809
+ const existing = this.workspaces.get(key);
3810
+ if (!existing) {
3811
+ return null;
3812
+ }
3813
+ const updated = {
3814
+ ...existing,
3815
+ ...updates,
3816
+ updatedAt: /* @__PURE__ */ new Date()
3817
+ };
3818
+ this.workspaces.set(key, updated);
3819
+ return updated;
3820
+ }
3821
+ async deleteWorkspace(tenantId, id) {
3822
+ const key = this.getKey(tenantId, id);
3823
+ return this.workspaces.delete(key);
3824
+ }
3825
+ clear() {
3826
+ this.workspaces.clear();
3827
+ }
3828
+ };
3829
+
3830
+ // src/store_lattice/InMemoryProjectStore.ts
3831
+ var InMemoryProjectStore = class {
3832
+ constructor() {
3833
+ this.projects = /* @__PURE__ */ new Map();
3834
+ this.initDefaultData();
3835
+ }
3836
+ initDefaultData() {
3837
+ const defaultTenantId = "default";
3838
+ const defaultWorkspaceId = "default-workspace";
3839
+ const now = /* @__PURE__ */ new Date();
3840
+ const defaultProject = {
3841
+ id: "default-project",
3842
+ tenantId: defaultTenantId,
3843
+ workspaceId: defaultWorkspaceId,
3844
+ name: "\u9ED8\u8BA4\u9879\u76EE",
3845
+ description: "\u7CFB\u7EDF\u9ED8\u8BA4\u9879\u76EE",
3846
+ createdAt: now,
3847
+ updatedAt: now
3848
+ };
3849
+ this.projects.set(`${defaultTenantId}:default-project`, defaultProject);
3850
+ }
3851
+ getKey(tenantId, id) {
3852
+ return `${tenantId}:${id}`;
3853
+ }
3854
+ async getProjectsByWorkspace(tenantId, workspaceId) {
3855
+ return Array.from(this.projects.values()).filter(
3856
+ (p) => p.tenantId === tenantId && p.workspaceId === workspaceId
3857
+ );
3858
+ }
3859
+ async getProjectById(tenantId, id) {
3860
+ const key = this.getKey(tenantId, id);
3861
+ return this.projects.get(key) || null;
3862
+ }
3863
+ async createProject(tenantId, workspaceId, id, data) {
3864
+ const now = /* @__PURE__ */ new Date();
3865
+ const project = {
3866
+ id,
3867
+ tenantId,
3868
+ workspaceId,
3869
+ name: data.name,
3870
+ description: data.description,
3871
+ createdAt: now,
3872
+ updatedAt: now
3873
+ };
3874
+ const key = this.getKey(tenantId, id);
3875
+ this.projects.set(key, project);
3876
+ return project;
3877
+ }
3878
+ async updateProject(tenantId, id, updates) {
3879
+ const key = this.getKey(tenantId, id);
3880
+ const existing = this.projects.get(key);
3881
+ if (!existing) {
3882
+ return null;
3883
+ }
3884
+ const updated = {
3885
+ ...existing,
3886
+ ...updates,
3887
+ updatedAt: /* @__PURE__ */ new Date()
3888
+ };
3889
+ this.projects.set(key, updated);
3890
+ return updated;
3891
+ }
3892
+ async deleteProject(tenantId, id) {
3893
+ const key = this.getKey(tenantId, id);
3894
+ return this.projects.delete(key);
3895
+ }
3896
+ clear() {
3897
+ this.projects.clear();
3898
+ }
3899
+ };
3900
+
3901
+ // src/store_lattice/InMemoryDatabaseConfigStore.ts
3902
+ var InMemoryDatabaseConfigStore = class {
3903
+ constructor() {
3904
+ this.configs = /* @__PURE__ */ new Map();
3905
+ }
3906
+ /**
3907
+ * Get composite key for storage
3908
+ */
3909
+ getKey(tenantId, id) {
3910
+ return `${tenantId}:${id}`;
3911
+ }
3912
+ /**
3913
+ * Get all database configurations for a tenant
3914
+ */
3915
+ async getAllConfigs(tenantId) {
3916
+ return Array.from(this.configs.values()).filter(
3917
+ (config) => config.tenantId === tenantId
3918
+ );
3919
+ }
3920
+ /**
3921
+ * Get all database configurations across all tenants
3922
+ */
3923
+ async getAllConfigsWithoutTenant() {
3924
+ return Array.from(this.configs.values());
3925
+ }
3926
+ /**
3927
+ * Get database configuration by ID
3928
+ */
3929
+ async getConfigById(tenantId, id) {
3930
+ const key = this.getKey(tenantId, id);
3931
+ return this.configs.get(key) || null;
3932
+ }
3933
+ /**
3934
+ * Get database configuration by business key
3935
+ */
3936
+ async getConfigByKey(tenantId, key) {
3937
+ const configs = await this.getAllConfigs(tenantId);
3938
+ return configs.find((config) => config.key === key) || null;
3939
+ }
3940
+ /**
3941
+ * Create a new database configuration
3942
+ */
3943
+ async createConfig(tenantId, id, data) {
3944
+ const now = /* @__PURE__ */ new Date();
3945
+ const entry = {
3946
+ id,
3947
+ tenantId,
3948
+ key: data.key,
3949
+ config: data.config,
3950
+ name: data.name,
3951
+ description: data.description,
3952
+ createdAt: now,
3953
+ updatedAt: now
3954
+ };
3955
+ const storageKey = this.getKey(tenantId, id);
3956
+ this.configs.set(storageKey, entry);
3957
+ return entry;
3958
+ }
3959
+ /**
3960
+ * Update an existing database configuration
3961
+ */
3962
+ async updateConfig(tenantId, id, updates) {
3963
+ const key = this.getKey(tenantId, id);
3964
+ const existing = this.configs.get(key);
3965
+ if (!existing) {
3966
+ return null;
3967
+ }
3968
+ const updated = {
3969
+ ...existing,
3970
+ ...updates,
3971
+ config: updates.config ? { ...existing.config, ...updates.config } : existing.config,
3972
+ key: updates.key || existing.key,
3973
+ name: updates.name !== void 0 ? updates.name : existing.name,
3974
+ description: updates.description !== void 0 ? updates.description : existing.description,
3975
+ updatedAt: /* @__PURE__ */ new Date()
3976
+ };
3977
+ this.configs.set(key, updated);
3978
+ return updated;
3979
+ }
3980
+ /**
3981
+ * Delete a database configuration by ID
3982
+ */
3983
+ async deleteConfig(tenantId, id) {
3984
+ const key = this.getKey(tenantId, id);
3985
+ return this.configs.delete(key);
3986
+ }
3987
+ /**
3988
+ * Check if configuration exists
3989
+ */
3990
+ async hasConfig(tenantId, id) {
3991
+ const key = this.getKey(tenantId, id);
3992
+ return this.configs.has(key);
3993
+ }
3994
+ /**
3995
+ * Clear all configurations (useful for testing)
3996
+ */
3997
+ clear() {
3998
+ this.configs.clear();
3999
+ }
4000
+ /**
4001
+ * Clear configurations for a specific tenant
4002
+ */
4003
+ clearByTenant(tenantId) {
4004
+ for (const key of this.configs.keys()) {
4005
+ if (key.startsWith(`${tenantId}:`)) {
4006
+ this.configs.delete(key);
4007
+ }
4008
+ }
4009
+ }
4010
+ };
4011
+
3688
4012
  // src/store_lattice/StoreLatticeManager.ts
3689
4013
  var StoreLatticeManager = class _StoreLatticeManager extends BaseLatticeManager {
3690
4014
  /**
@@ -3812,6 +4136,9 @@ var getStoreLattice = (key, type) => storeLatticeManager.getStoreLattice(key, ty
3812
4136
  var defaultThreadStore = new InMemoryThreadStore();
3813
4137
  var defaultAssistantStore = new InMemoryAssistantStore();
3814
4138
  var defaultSkillStore = new FileSystemSkillStore();
4139
+ var defaultWorkspaceStore = new InMemoryWorkspaceStore();
4140
+ var defaultProjectStore = new InMemoryProjectStore();
4141
+ var defaultDatabaseConfigStore = new InMemoryDatabaseConfigStore();
3815
4142
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
3816
4143
  storeLatticeManager.registerLattice(
3817
4144
  "default",
@@ -3819,6 +4146,9 @@ storeLatticeManager.registerLattice(
3819
4146
  defaultAssistantStore
3820
4147
  );
3821
4148
  storeLatticeManager.registerLattice("default", "skill", defaultSkillStore);
4149
+ storeLatticeManager.registerLattice("default", "workspace", defaultWorkspaceStore);
4150
+ storeLatticeManager.registerLattice("default", "project", defaultProjectStore);
4151
+ storeLatticeManager.registerLattice("default", "database", defaultDatabaseConfigStore);
3822
4152
 
3823
4153
  // src/tool_lattice/skill/load_skills.ts
3824
4154
  var import_zod33 = __toESM(require("zod"));
@@ -3915,6 +4245,7 @@ var DEFAULT_EXTRA_NOTE = "Use the load_skill_content tool when you need detailed
3915
4245
  function createSkillMiddleware(params = {}) {
3916
4246
  const {
3917
4247
  skills = [],
4248
+ readAll = false,
3918
4249
  heading = DEFAULT_HEADING,
3919
4250
  extraNote = DEFAULT_EXTRA_NOTE
3920
4251
  } = params;
@@ -3922,14 +4253,16 @@ function createSkillMiddleware(params = {}) {
3922
4253
  return (0, import_langchain34.createMiddleware)({
3923
4254
  name: "skillMiddleware",
3924
4255
  tools: [
3925
- createLoadSkillsTool({ skills }),
4256
+ createLoadSkillsTool({ skills: readAll ? void 0 : skills }),
3926
4257
  createLoadSkillContentTool()
3927
4258
  ],
3928
4259
  beforeAgent: async () => {
3929
4260
  try {
3930
- if (skills && skills.length > 0) {
3931
- const storeLattice = getStoreLattice("default", "skill");
3932
- const skillStore = storeLattice?.store;
4261
+ const storeLattice = getStoreLattice("default", "skill");
4262
+ const skillStore = storeLattice?.store;
4263
+ if (readAll) {
4264
+ latestSkills = await skillStore.getAllSkills();
4265
+ } else if (skills && skills.length > 0) {
3933
4266
  const skillLatticePromises = skills.map(
3934
4267
  (skillId) => skillStore.getSkillById(skillId)
3935
4268
  );
@@ -3971,6 +4304,8 @@ var import_path = require("path");
3971
4304
  var EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
3972
4305
  var MAX_LINE_LENGTH = 1e4;
3973
4306
  var LINE_NUMBER_WIDTH = 6;
4307
+ var TOOL_RESULT_TOKEN_LIMIT = 2e4;
4308
+ var TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
3974
4309
  function sanitizeToolCallId(toolCallId) {
3975
4310
  return toolCallId.replace(/\./g, "_").replace(/\//g, "_").replace(/\\/g, "_");
3976
4311
  }
@@ -4066,8 +4401,24 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
4066
4401
  const newContent = content.split(oldString).join(newString);
4067
4402
  return [newContent, occurrences];
4068
4403
  }
4069
- function validatePath(path4) {
4070
- const pathStr = path4 || "/";
4404
+ function truncateIfTooLong(result) {
4405
+ if (Array.isArray(result)) {
4406
+ const totalChars = result.reduce((sum, item) => sum + item.length, 0);
4407
+ if (totalChars > TOOL_RESULT_TOKEN_LIMIT * 4) {
4408
+ const truncateAt = Math.floor(
4409
+ result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars
4410
+ );
4411
+ return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
4412
+ }
4413
+ return result;
4414
+ }
4415
+ if (result.length > TOOL_RESULT_TOKEN_LIMIT * 4) {
4416
+ return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n" + TRUNCATION_GUIDANCE;
4417
+ }
4418
+ return result;
4419
+ }
4420
+ function validatePath(path5) {
4421
+ const pathStr = path5 || "/";
4071
4422
  if (!pathStr || pathStr.trim() === "") {
4072
4423
  throw new Error("Path cannot be empty");
4073
4424
  }
@@ -4077,10 +4428,10 @@ function validatePath(path4) {
4077
4428
  }
4078
4429
  return normalized;
4079
4430
  }
4080
- function globSearchFiles(files, pattern, path4 = "/") {
4431
+ function globSearchFiles(files, pattern, path5 = "/") {
4081
4432
  let normalizedPath;
4082
4433
  try {
4083
- normalizedPath = validatePath(path4);
4434
+ normalizedPath = validatePath(path5);
4084
4435
  } catch {
4085
4436
  return "No files found";
4086
4437
  }
@@ -4090,15 +4441,15 @@ function globSearchFiles(files, pattern, path4 = "/") {
4090
4441
  const effectivePattern = pattern;
4091
4442
  const matches = [];
4092
4443
  for (const [filePath, fileData] of Object.entries(filtered)) {
4093
- let relative2 = filePath.substring(normalizedPath.length);
4094
- if (relative2.startsWith("/")) {
4095
- relative2 = relative2.substring(1);
4444
+ let relative3 = filePath.substring(normalizedPath.length);
4445
+ if (relative3.startsWith("/")) {
4446
+ relative3 = relative3.substring(1);
4096
4447
  }
4097
- if (!relative2) {
4448
+ if (!relative3) {
4098
4449
  const parts = filePath.split("/");
4099
- relative2 = parts[parts.length - 1] || "";
4450
+ relative3 = parts[parts.length - 1] || "";
4100
4451
  }
4101
- if (import_micromatch.default.isMatch(relative2, effectivePattern, {
4452
+ if (import_micromatch.default.isMatch(relative3, effectivePattern, {
4102
4453
  dot: true,
4103
4454
  nobrace: false
4104
4455
  })) {
@@ -4111,7 +4462,28 @@ function globSearchFiles(files, pattern, path4 = "/") {
4111
4462
  }
4112
4463
  return matches.map(([fp]) => fp).join("\n");
4113
4464
  }
4114
- function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4465
+ function formatGrepResults(results, outputMode) {
4466
+ if (outputMode === "files_with_matches") {
4467
+ return Object.keys(results).sort().join("\n");
4468
+ }
4469
+ if (outputMode === "count") {
4470
+ const lines2 = [];
4471
+ for (const filePath of Object.keys(results).sort()) {
4472
+ const count = results[filePath].length;
4473
+ lines2.push(`${filePath}: ${count}`);
4474
+ }
4475
+ return lines2.join("\n");
4476
+ }
4477
+ const lines = [];
4478
+ for (const filePath of Object.keys(results).sort()) {
4479
+ lines.push(`${filePath}:`);
4480
+ for (const [lineNum, line] of results[filePath]) {
4481
+ lines.push(` ${lineNum}: ${line}`);
4482
+ }
4483
+ }
4484
+ return lines.join("\n");
4485
+ }
4486
+ function grepSearchFiles(files, pattern, path5 = null, glob = null, outputMode = "files_with_matches") {
4115
4487
  let regex;
4116
4488
  try {
4117
4489
  regex = new RegExp(pattern);
@@ -4120,9 +4492,9 @@ function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4120
4492
  }
4121
4493
  let normalizedPath;
4122
4494
  try {
4123
- normalizedPath = validatePath(path4);
4495
+ normalizedPath = validatePath(path5);
4124
4496
  } catch {
4125
- return [];
4497
+ return "No matches found";
4126
4498
  }
4127
4499
  let filtered = Object.fromEntries(
4128
4500
  Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
@@ -4134,49 +4506,106 @@ function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4134
4506
  )
4135
4507
  );
4136
4508
  }
4137
- const matches = [];
4509
+ const results = {};
4138
4510
  for (const [filePath, fileData] of Object.entries(filtered)) {
4139
4511
  for (let i = 0; i < fileData.content.length; i++) {
4140
4512
  const line = fileData.content[i];
4141
4513
  const lineNum = i + 1;
4142
4514
  if (regex.test(line)) {
4143
- matches.push({ path: filePath, line: lineNum, text: line });
4515
+ if (!results[filePath]) {
4516
+ results[filePath] = [];
4517
+ }
4518
+ results[filePath].push([lineNum, line]);
4144
4519
  }
4145
4520
  }
4146
4521
  }
4147
- return matches;
4148
- }
4149
-
4150
- // src/deep_agent_new/backends/state.ts
4151
- var StateBackend = class {
4152
- constructor(stateAndStore) {
4153
- this.stateAndStore = stateAndStore;
4522
+ if (Object.keys(results).length === 0) {
4523
+ return "No matches found";
4154
4524
  }
4155
- /**
4156
- * Get files from current state.
4157
- */
4158
- getFiles() {
4159
- return this.stateAndStore.state.files || {};
4525
+ return formatGrepResults(results, outputMode);
4526
+ }
4527
+ function grepMatchesFromFiles(files, pattern, path5 = null, glob = null) {
4528
+ let regex;
4529
+ try {
4530
+ regex = new RegExp(pattern);
4531
+ } catch (e) {
4532
+ return `Invalid regex pattern: ${e.message}`;
4160
4533
  }
4161
- /**
4534
+ let normalizedPath;
4535
+ try {
4536
+ normalizedPath = validatePath(path5);
4537
+ } catch {
4538
+ return [];
4539
+ }
4540
+ let filtered = Object.fromEntries(
4541
+ Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
4542
+ );
4543
+ if (glob) {
4544
+ filtered = Object.fromEntries(
4545
+ Object.entries(filtered).filter(
4546
+ ([fp]) => import_micromatch.default.isMatch((0, import_path.basename)(fp), glob, { dot: true, nobrace: false })
4547
+ )
4548
+ );
4549
+ }
4550
+ const matches = [];
4551
+ for (const [filePath, fileData] of Object.entries(filtered)) {
4552
+ for (let i = 0; i < fileData.content.length; i++) {
4553
+ const line = fileData.content[i];
4554
+ const lineNum = i + 1;
4555
+ if (regex.test(line)) {
4556
+ matches.push({ path: filePath, line: lineNum, text: line });
4557
+ }
4558
+ }
4559
+ }
4560
+ return matches;
4561
+ }
4562
+ function buildGrepResultsDict(matches) {
4563
+ const grouped = {};
4564
+ for (const m of matches) {
4565
+ if (!grouped[m.path]) {
4566
+ grouped[m.path] = [];
4567
+ }
4568
+ grouped[m.path].push([m.line, m.text]);
4569
+ }
4570
+ return grouped;
4571
+ }
4572
+ function formatGrepMatches(matches, outputMode) {
4573
+ if (matches.length === 0) {
4574
+ return "No matches found";
4575
+ }
4576
+ return formatGrepResults(buildGrepResultsDict(matches), outputMode);
4577
+ }
4578
+
4579
+ // src/deep_agent_new/backends/state.ts
4580
+ var StateBackend = class {
4581
+ constructor(stateAndStore) {
4582
+ this.stateAndStore = stateAndStore;
4583
+ }
4584
+ /**
4585
+ * Get files from current state.
4586
+ */
4587
+ getFiles() {
4588
+ return this.stateAndStore.state.files || {};
4589
+ }
4590
+ /**
4162
4591
  * List files and directories in the specified directory (non-recursive).
4163
4592
  *
4164
4593
  * @param path - Absolute path to directory
4165
4594
  * @returns List of FileInfo objects for files and directories directly in the directory.
4166
4595
  * Directories have a trailing / in their path and is_dir=true.
4167
4596
  */
4168
- lsInfo(path4) {
4597
+ lsInfo(path5) {
4169
4598
  const files = this.getFiles();
4170
4599
  const infos = [];
4171
4600
  const subdirs = /* @__PURE__ */ new Set();
4172
- const normalizedPath = path4.endsWith("/") ? path4 : path4 + "/";
4601
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
4173
4602
  for (const [k, fd] of Object.entries(files)) {
4174
4603
  if (!k.startsWith(normalizedPath)) {
4175
4604
  continue;
4176
4605
  }
4177
- const relative2 = k.substring(normalizedPath.length);
4178
- if (relative2.includes("/")) {
4179
- const subdirName = relative2.split("/")[0];
4606
+ const relative3 = k.substring(normalizedPath.length);
4607
+ if (relative3.includes("/")) {
4608
+ const subdirName = relative3.split("/")[0];
4180
4609
  subdirs.add(normalizedPath + subdirName + "/");
4181
4610
  continue;
4182
4611
  }
@@ -4275,16 +4704,16 @@ var StateBackend = class {
4275
4704
  /**
4276
4705
  * Structured search results or error string for invalid input.
4277
4706
  */
4278
- grepRaw(pattern, path4 = "/", glob = null) {
4707
+ grepRaw(pattern, path5 = "/", glob = null) {
4279
4708
  const files = this.getFiles();
4280
- return grepMatchesFromFiles(files, pattern, path4, glob);
4709
+ return grepMatchesFromFiles(files, pattern, path5, glob);
4281
4710
  }
4282
4711
  /**
4283
4712
  * Structured glob matching returning FileInfo objects.
4284
4713
  */
4285
- globInfo(pattern, path4 = "/") {
4714
+ globInfo(pattern, path5 = "/") {
4286
4715
  const files = this.getFiles();
4287
- const result = globSearchFiles(files, pattern, path4);
4716
+ const result = globSearchFiles(files, pattern, path5);
4288
4717
  if (result === "No files found") {
4289
4718
  return [];
4290
4719
  }
@@ -4365,15 +4794,17 @@ function createLsTool(backend, options) {
4365
4794
  const { customDescription } = options;
4366
4795
  return (0, import_langchain35.tool)(
4367
4796
  async (input, config) => {
4797
+ const { runConfig } = config.configurable;
4368
4798
  const stateAndStore = {
4369
4799
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4370
- store: config.store
4800
+ store: config.store,
4801
+ ...runConfig
4371
4802
  };
4372
4803
  const resolvedBackend = await getBackend(backend, stateAndStore);
4373
- const path4 = input.path || "/";
4374
- const infos = await resolvedBackend.lsInfo(path4);
4804
+ const path5 = input.path || "/";
4805
+ const infos = await resolvedBackend.lsInfo(path5);
4375
4806
  if (infos.length === 0) {
4376
- return `No files found in ${path4}`;
4807
+ return `No files found in ${path5}`;
4377
4808
  }
4378
4809
  const lines = [];
4379
4810
  for (const info of infos) {
@@ -4399,9 +4830,11 @@ function createReadFileTool(backend, options) {
4399
4830
  const { customDescription } = options;
4400
4831
  return (0, import_langchain35.tool)(
4401
4832
  async (input, config) => {
4833
+ const { runConfig } = config.configurable;
4402
4834
  const stateAndStore = {
4403
4835
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4404
- store: config.store
4836
+ store: config.store,
4837
+ ...runConfig
4405
4838
  };
4406
4839
  const resolvedBackend = await getBackend(backend, stateAndStore);
4407
4840
  const { file_path, offset = 0, limit = 2e3 } = input;
@@ -4422,9 +4855,11 @@ function createWriteFileTool(backend, options) {
4422
4855
  const { customDescription } = options;
4423
4856
  return (0, import_langchain35.tool)(
4424
4857
  async (input, config) => {
4858
+ const { runConfig } = config.configurable;
4425
4859
  const stateAndStore = {
4426
4860
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4427
- store: config.store
4861
+ store: config.store,
4862
+ ...runConfig
4428
4863
  };
4429
4864
  const resolvedBackend = await getBackend(backend, stateAndStore);
4430
4865
  const { file_path, content } = input;
@@ -4459,9 +4894,11 @@ function createEditFileTool(backend, options) {
4459
4894
  const { customDescription } = options;
4460
4895
  return (0, import_langchain35.tool)(
4461
4896
  async (input, config) => {
4897
+ const { runConfig } = config.configurable;
4462
4898
  const stateAndStore = {
4463
4899
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4464
- store: config.store
4900
+ store: config.store,
4901
+ ...runConfig
4465
4902
  };
4466
4903
  const resolvedBackend = await getBackend(backend, stateAndStore);
4467
4904
  const { file_path, old_string, new_string, replace_all = false } = input;
@@ -4503,13 +4940,15 @@ function createGlobTool(backend, options) {
4503
4940
  const { customDescription } = options;
4504
4941
  return (0, import_langchain35.tool)(
4505
4942
  async (input, config) => {
4943
+ const { runConfig } = config.configurable;
4506
4944
  const stateAndStore = {
4507
4945
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4508
- store: config.store
4946
+ store: config.store,
4947
+ ...runConfig
4509
4948
  };
4510
4949
  const resolvedBackend = await getBackend(backend, stateAndStore);
4511
- const { pattern, path: path4 = "/" } = input;
4512
- const infos = await resolvedBackend.globInfo(pattern, path4);
4950
+ const { pattern, path: path5 = "/" } = input;
4951
+ const infos = await resolvedBackend.globInfo(pattern, path5);
4513
4952
  if (infos.length === 0) {
4514
4953
  return `No files found matching pattern '${pattern}'`;
4515
4954
  }
@@ -4529,13 +4968,15 @@ function createGrepTool(backend, options) {
4529
4968
  const { customDescription } = options;
4530
4969
  return (0, import_langchain35.tool)(
4531
4970
  async (input, config) => {
4971
+ const { runConfig } = config.configurable;
4532
4972
  const stateAndStore = {
4533
4973
  state: (0, import_langgraph4.getCurrentTaskInput)(config),
4534
- store: config.store
4974
+ store: config.store,
4975
+ ...runConfig
4535
4976
  };
4536
4977
  const resolvedBackend = await getBackend(backend, stateAndStore);
4537
- const { pattern, path: path4 = "/", glob = null } = input;
4538
- const result = await resolvedBackend.grepRaw(pattern, path4, glob);
4978
+ const { pattern, path: path5 = "/", glob = null } = input;
4979
+ const result = await resolvedBackend.grepRaw(pattern, path5, glob);
4539
4980
  if (typeof result === "string") {
4540
4981
  return result;
4541
4982
  }
@@ -4705,7 +5146,22 @@ function createCommonMiddlewares(middlewareConfigs, filesystemBackend) {
4705
5146
  middlewares.push(createBrowserMiddleware(config.config));
4706
5147
  break;
4707
5148
  case "sql":
4708
- middlewares.push(createSqlMiddleware(config.config));
5149
+ {
5150
+ const sqlConfig = config.config;
5151
+ if (sqlConfig.databaseKeys && sqlConfig.databaseKeys.length > 0) {
5152
+ const databaseConfigs = global.__DATABASE_CONFIGS__ || [];
5153
+ const descriptions = {};
5154
+ for (const db of databaseConfigs) {
5155
+ if (db.key && sqlConfig.databaseKeys.includes(db.key)) {
5156
+ descriptions[db.key] = db.description || db.name || "";
5157
+ }
5158
+ }
5159
+ middlewares.push(createSqlMiddleware({
5160
+ databaseKeys: sqlConfig.databaseKeys,
5161
+ databaseDescriptions: descriptions
5162
+ }));
5163
+ }
5164
+ }
4709
5165
  break;
4710
5166
  case "skill":
4711
5167
  middlewares.push(createSkillMiddleware(config.config));
@@ -4724,6 +5180,7 @@ var ReActAgentGraphBuilder = class {
4724
5180
  }
4725
5181
  const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
4726
5182
  return async (config) => {
5183
+ const { workspaceId, projectId } = config;
4727
5184
  let sandboxName = "global";
4728
5185
  if (isolatedLevel === "agent") {
4729
5186
  sandboxName = "agent";
@@ -4735,7 +5192,8 @@ var ReActAgentGraphBuilder = class {
4735
5192
  throw new Error("Sandbox manager not found");
4736
5193
  }
4737
5194
  return new SandboxFilesystem({
4738
- sandboxInstance: await sandboxManager.createSandbox(sandboxName)
5195
+ sandboxInstance: await sandboxManager.createSandbox(sandboxName),
5196
+ workingDirectory: workspaceId && projectId ? `/${workspaceId}/${projectId}` : "/"
4739
5197
  });
4740
5198
  };
4741
5199
  }
@@ -4751,9 +5209,9 @@ var ReActAgentGraphBuilder = class {
4751
5209
  */
4752
5210
  build(agentLattice, params) {
4753
5211
  const tools = params.tools.map((t) => {
4754
- const tool36 = getToolClient(t.key);
4755
- return tool36;
4756
- }).filter((tool36) => tool36 !== void 0);
5212
+ const tool38 = getToolClient(t.key);
5213
+ return tool38;
5214
+ }).filter((tool38) => tool38 !== void 0);
4757
5215
  const stateSchema2 = createReactAgentSchema(params.stateSchema);
4758
5216
  const middlewareConfigs = params.middleware || [];
4759
5217
  const filesystemBackend = this.createFilesystemBackendFactory(middlewareConfigs);
@@ -5036,12 +5494,12 @@ var AgentManager = class _AgentManager {
5036
5494
  return _AgentManager.instance;
5037
5495
  }
5038
5496
  callAgentInQueue(queue, return_agent_state) {
5039
- return new Promise((resolve2, reject) => {
5497
+ return new Promise((resolve3, reject) => {
5040
5498
  const callback_event = `${queue.assistant_id}::${queue.thread_id}`;
5041
5499
  if (return_agent_state) {
5042
5500
  event_bus_default.subscribeOnce(callback_event, (data) => {
5043
5501
  if (data.success) {
5044
- resolve2(data.state);
5502
+ resolve3(data.state);
5045
5503
  } else {
5046
5504
  reject(data.error);
5047
5505
  }
@@ -5056,7 +5514,7 @@ var AgentManager = class _AgentManager {
5056
5514
  },
5057
5515
  true
5058
5516
  );
5059
- !return_agent_state && resolve2({ callback_event_id: callback_event, success: true });
5517
+ !return_agent_state && resolve3({ callback_event_id: callback_event, success: true });
5060
5518
  } catch (error) {
5061
5519
  !return_agent_state && reject({
5062
5520
  callback_event_id: callback_event,
@@ -5546,59 +6004,1144 @@ function createPatchToolCallsMiddleware() {
5546
6004
  });
5547
6005
  }
5548
6006
 
6007
+ // src/deep_agent_new/backends/store.ts
6008
+ var StoreBackend = class {
6009
+ constructor(stateAndStore) {
6010
+ this.stateAndStore = stateAndStore;
6011
+ }
6012
+ /**
6013
+ * Get the store instance.
6014
+ *
6015
+ * @returns BaseStore instance
6016
+ * @throws Error if no store is available
6017
+ */
6018
+ getStore() {
6019
+ const store = this.stateAndStore.store;
6020
+ if (!store) {
6021
+ throw new Error("Store is required but not available in StateAndStore");
6022
+ }
6023
+ return store;
6024
+ }
6025
+ /**
6026
+ * Get the namespace for store operations.
6027
+ *
6028
+ * If an assistant_id is available in stateAndStore, return
6029
+ * [assistant_id, "filesystem"] to provide per-assistant isolation.
6030
+ * Otherwise return ["filesystem"].
6031
+ */
6032
+ getNamespace() {
6033
+ const namespace = "filesystem";
6034
+ const assistantId = this.stateAndStore.assistantId;
6035
+ if (assistantId) {
6036
+ return [assistantId, namespace];
6037
+ }
6038
+ return [namespace];
6039
+ }
6040
+ /**
6041
+ * Convert a store Item to FileData format.
6042
+ *
6043
+ * @param storeItem - The store Item containing file data
6044
+ * @returns FileData object
6045
+ * @throws Error if required fields are missing or have incorrect types
6046
+ */
6047
+ convertStoreItemToFileData(storeItem) {
6048
+ const value = storeItem.value;
6049
+ if (!value.content || !Array.isArray(value.content) || typeof value.created_at !== "string" || typeof value.modified_at !== "string") {
6050
+ throw new Error(
6051
+ `Store item does not contain valid FileData fields. Got keys: ${Object.keys(value).join(", ")}`
6052
+ );
6053
+ }
6054
+ return {
6055
+ content: value.content,
6056
+ created_at: value.created_at,
6057
+ modified_at: value.modified_at
6058
+ };
6059
+ }
6060
+ /**
6061
+ * Convert FileData to a value suitable for store.put().
6062
+ *
6063
+ * @param fileData - The FileData to convert
6064
+ * @returns Object with content, created_at, and modified_at fields
6065
+ */
6066
+ convertFileDataToStoreValue(fileData) {
6067
+ return {
6068
+ content: fileData.content,
6069
+ created_at: fileData.created_at,
6070
+ modified_at: fileData.modified_at
6071
+ };
6072
+ }
6073
+ /**
6074
+ * Search store with automatic pagination to retrieve all results.
6075
+ *
6076
+ * @param store - The store to search
6077
+ * @param namespace - Hierarchical path prefix to search within
6078
+ * @param options - Optional query, filter, and page_size
6079
+ * @returns List of all items matching the search criteria
6080
+ */
6081
+ async searchStorePaginated(store, namespace, options = {}) {
6082
+ const { query, filter, pageSize = 100 } = options;
6083
+ const allItems = [];
6084
+ let offset = 0;
6085
+ while (true) {
6086
+ const pageItems = await store.search(namespace, {
6087
+ query,
6088
+ filter,
6089
+ limit: pageSize,
6090
+ offset
6091
+ });
6092
+ if (!pageItems || pageItems.length === 0) {
6093
+ break;
6094
+ }
6095
+ allItems.push(...pageItems);
6096
+ if (pageItems.length < pageSize) {
6097
+ break;
6098
+ }
6099
+ offset += pageSize;
6100
+ }
6101
+ return allItems;
6102
+ }
6103
+ /**
6104
+ * List files and directories in the specified directory (non-recursive).
6105
+ *
6106
+ * @param path - Absolute path to directory
6107
+ * @returns List of FileInfo objects for files and directories directly in the directory.
6108
+ * Directories have a trailing / in their path and is_dir=true.
6109
+ */
6110
+ async lsInfo(path5) {
6111
+ const store = this.getStore();
6112
+ const namespace = this.getNamespace();
6113
+ const items = await this.searchStorePaginated(store, namespace);
6114
+ const infos = [];
6115
+ const subdirs = /* @__PURE__ */ new Set();
6116
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
6117
+ for (const item of items) {
6118
+ const itemKey = String(item.key);
6119
+ if (!itemKey.startsWith(normalizedPath)) {
6120
+ continue;
6121
+ }
6122
+ const relative3 = itemKey.substring(normalizedPath.length);
6123
+ if (relative3.includes("/")) {
6124
+ const subdirName = relative3.split("/")[0];
6125
+ subdirs.add(normalizedPath + subdirName + "/");
6126
+ continue;
6127
+ }
6128
+ try {
6129
+ const fd = this.convertStoreItemToFileData(item);
6130
+ const size = fd.content.join("\n").length;
6131
+ infos.push({
6132
+ path: itemKey,
6133
+ is_dir: false,
6134
+ size,
6135
+ modified_at: fd.modified_at
6136
+ });
6137
+ } catch {
6138
+ continue;
6139
+ }
6140
+ }
6141
+ for (const subdir of Array.from(subdirs).sort()) {
6142
+ infos.push({
6143
+ path: subdir,
6144
+ is_dir: true,
6145
+ size: 0,
6146
+ modified_at: ""
6147
+ });
6148
+ }
6149
+ infos.sort((a, b) => a.path.localeCompare(b.path));
6150
+ return infos;
6151
+ }
6152
+ /**
6153
+ * Read file content with line numbers.
6154
+ *
6155
+ * @param filePath - Absolute file path
6156
+ * @param offset - Line offset to start reading from (0-indexed)
6157
+ * @param limit - Maximum number of lines to read
6158
+ * @returns Formatted file content with line numbers, or error message
6159
+ */
6160
+ async read(filePath, offset = 0, limit = 2e3) {
6161
+ try {
6162
+ const fileData = await this.readRaw(filePath);
6163
+ return formatReadResponse(fileData, offset, limit);
6164
+ } catch (e) {
6165
+ return `Error: ${e.message}`;
6166
+ }
6167
+ }
6168
+ /**
6169
+ * Read file content as raw FileData.
6170
+ *
6171
+ * @param filePath - Absolute file path
6172
+ * @returns Raw file content as FileData
6173
+ */
6174
+ async readRaw(filePath) {
6175
+ const store = this.getStore();
6176
+ const namespace = this.getNamespace();
6177
+ const item = await store.get(namespace, filePath);
6178
+ if (!item) throw new Error(`File '${filePath}' not found`);
6179
+ return this.convertStoreItemToFileData(item);
6180
+ }
6181
+ /**
6182
+ * Create a new file with content.
6183
+ * Returns WriteResult. External storage sets filesUpdate=null.
6184
+ */
6185
+ async write(filePath, content) {
6186
+ const store = this.getStore();
6187
+ const namespace = this.getNamespace();
6188
+ const existing = await store.get(namespace, filePath);
6189
+ if (existing) {
6190
+ return {
6191
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
6192
+ };
6193
+ }
6194
+ const fileData = createFileData(content);
6195
+ const storeValue = this.convertFileDataToStoreValue(fileData);
6196
+ await store.put(namespace, filePath, storeValue);
6197
+ return { path: filePath, filesUpdate: null };
6198
+ }
6199
+ /**
6200
+ * Edit a file by replacing string occurrences.
6201
+ * Returns EditResult. External storage sets filesUpdate=null.
6202
+ */
6203
+ async edit(filePath, oldString, newString, replaceAll = false) {
6204
+ const store = this.getStore();
6205
+ const namespace = this.getNamespace();
6206
+ const item = await store.get(namespace, filePath);
6207
+ if (!item) {
6208
+ return { error: `Error: File '${filePath}' not found` };
6209
+ }
6210
+ try {
6211
+ const fileData = this.convertStoreItemToFileData(item);
6212
+ const content = fileDataToString(fileData);
6213
+ const result = performStringReplacement(
6214
+ content,
6215
+ oldString,
6216
+ newString,
6217
+ replaceAll
6218
+ );
6219
+ if (typeof result === "string") {
6220
+ return { error: result };
6221
+ }
6222
+ const [newContent, occurrences] = result;
6223
+ const newFileData = updateFileData(fileData, newContent);
6224
+ const storeValue = this.convertFileDataToStoreValue(newFileData);
6225
+ await store.put(namespace, filePath, storeValue);
6226
+ return { path: filePath, filesUpdate: null, occurrences };
6227
+ } catch (e) {
6228
+ return { error: `Error: ${e.message}` };
6229
+ }
6230
+ }
6231
+ /**
6232
+ * Structured search results or error string for invalid input.
6233
+ */
6234
+ async grepRaw(pattern, path5 = "/", glob = null) {
6235
+ const store = this.getStore();
6236
+ const namespace = this.getNamespace();
6237
+ const items = await this.searchStorePaginated(store, namespace);
6238
+ const files = {};
6239
+ for (const item of items) {
6240
+ try {
6241
+ files[item.key] = this.convertStoreItemToFileData(item);
6242
+ } catch {
6243
+ continue;
6244
+ }
6245
+ }
6246
+ return grepMatchesFromFiles(files, pattern, path5, glob);
6247
+ }
6248
+ /**
6249
+ * Structured glob matching returning FileInfo objects.
6250
+ */
6251
+ async globInfo(pattern, path5 = "/") {
6252
+ const store = this.getStore();
6253
+ const namespace = this.getNamespace();
6254
+ const items = await this.searchStorePaginated(store, namespace);
6255
+ const files = {};
6256
+ for (const item of items) {
6257
+ try {
6258
+ files[item.key] = this.convertStoreItemToFileData(item);
6259
+ } catch {
6260
+ continue;
6261
+ }
6262
+ }
6263
+ const result = globSearchFiles(files, pattern, path5);
6264
+ if (result === "No files found") {
6265
+ return [];
6266
+ }
6267
+ const paths = result.split("\n");
6268
+ const infos = [];
6269
+ for (const p of paths) {
6270
+ const fd = files[p];
6271
+ const size = fd ? fd.content.join("\n").length : 0;
6272
+ infos.push({
6273
+ path: p,
6274
+ is_dir: false,
6275
+ size,
6276
+ modified_at: fd?.modified_at || ""
6277
+ });
6278
+ }
6279
+ return infos;
6280
+ }
6281
+ };
6282
+
5549
6283
  // src/deep_agent_new/backends/filesystem.ts
6284
+ var fs2 = __toESM(require("fs/promises"));
5550
6285
  var fsSync = __toESM(require("fs"));
6286
+ var path4 = __toESM(require("path"));
6287
+ var import_child_process = require("child_process");
5551
6288
  var import_fast_glob = __toESM(require("fast-glob"));
5552
6289
  var import_micromatch2 = __toESM(require("micromatch"));
5553
6290
  var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
5554
-
5555
- // src/deep_agent_new/middleware/todos.ts
5556
- var import_langgraph8 = require("@langchain/langgraph");
5557
- var import_zod36 = require("zod");
5558
- var import_langchain39 = require("langchain");
5559
- var WRITE_TODOS_DESCRIPTION = `Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
5560
- It also helps the user understand the progress of the task and overall progress of their requests.
5561
- Only use this tool if you think it will be helpful in staying organized. If the user's request is trivial and takes less than 3 steps, it is better to NOT use this tool and just do the taks directly.
5562
-
5563
- ## When to Use This Tool
5564
- Use this tool in these scenarios:
5565
-
5566
- 1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
5567
- 2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
5568
- 3. User explicitly requests todo list - When the user directly asks you to use the todo list
5569
- 4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
5570
- 5. The plan may need future revisions or updates based on results from the first few steps. Keeping track of this in a list is helpful.
5571
-
5572
- ## How to Use This Tool
5573
- 1. When you start working on a task - Mark it as in_progress BEFORE beginning work.
5574
- 2. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation.
5575
- 3. You can also update future tasks, such as deleting them if they are no longer necessary, or adding new tasks that are necessary. Don't change previously completed tasks.
5576
- 4. You can make several updates to the todo list at once. For example, when you complete a task, you can mark the next task you need to start as in_progress.
5577
-
5578
- ## When NOT to Use This Tool
5579
- It is important to skip using this tool when:
5580
- 1. There is only a single, straightforward task
5581
- 2. The task is trivial and tracking it provides no benefit
5582
- 3. The task can be completed in less than 3 trivial steps
5583
- 4. The task is purely conversational or informational
5584
-
5585
- ## Examples of When to Use the Todo List
5586
-
5587
- <example>
5588
- User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
5589
- Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
5590
- *Creates todo list with the following items:*
5591
- 1. Create dark mode toggle component in Settings page
5592
- 2. Add dark mode state management (context/store)
5593
- 3. Implement CSS-in-JS styles for dark theme
5594
- 4. Update existing components to support theme switching
5595
- 5. Run tests and build process, addressing any failures or errors that occur
5596
- *Begins working on the first task*
5597
-
5598
- <reasoning>
5599
- The assistant used the todo list because:
5600
- 1. Adding dark mode in it of itself is a multi-step feature requiring UI, state management, and styling changes
5601
- 2. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
6291
+ var FilesystemBackend = class {
6292
+ constructor(options = {}) {
6293
+ const { rootDir, virtualMode = false, maxFileSizeMb = 10 } = options;
6294
+ this.cwd = rootDir ? path4.resolve(rootDir) : process.cwd();
6295
+ this.virtualMode = virtualMode;
6296
+ this.maxFileSizeBytes = maxFileSizeMb * 1024 * 1024;
6297
+ }
6298
+ /**
6299
+ * Resolve a file path with security checks.
6300
+ *
6301
+ * When virtualMode=true, treat incoming paths as virtual absolute paths under
6302
+ * this.cwd, disallow traversal (.., ~) and ensure resolved path stays within root.
6303
+ * When virtualMode=false, preserve legacy behavior: absolute paths are allowed
6304
+ * as-is; relative paths resolve under cwd.
6305
+ *
6306
+ * @param key - File path (absolute, relative, or virtual when virtualMode=true)
6307
+ * @returns Resolved absolute path string
6308
+ * @throws Error if path traversal detected or path outside root
6309
+ */
6310
+ resolvePath(key) {
6311
+ if (this.virtualMode) {
6312
+ const vpath = key.startsWith("/") ? key : "/" + key;
6313
+ if (vpath.includes("..") || vpath.startsWith("~")) {
6314
+ throw new Error("Path traversal not allowed");
6315
+ }
6316
+ const full = path4.resolve(this.cwd, vpath.substring(1));
6317
+ const relative3 = path4.relative(this.cwd, full);
6318
+ if (relative3.startsWith("..") || path4.isAbsolute(relative3)) {
6319
+ throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
6320
+ }
6321
+ return full;
6322
+ }
6323
+ if (path4.isAbsolute(key)) {
6324
+ return key;
6325
+ }
6326
+ return path4.resolve(this.cwd, key);
6327
+ }
6328
+ /**
6329
+ * List files and directories in the specified directory (non-recursive).
6330
+ *
6331
+ * @param dirPath - Absolute directory path to list files from
6332
+ * @returns List of FileInfo objects for files and directories directly in the directory.
6333
+ * Directories have a trailing / in their path and is_dir=true.
6334
+ */
6335
+ async lsInfo(dirPath) {
6336
+ try {
6337
+ const resolvedPath = this.resolvePath(dirPath);
6338
+ const stat3 = await fs2.stat(resolvedPath);
6339
+ if (!stat3.isDirectory()) {
6340
+ return [];
6341
+ }
6342
+ const entries = await fs2.readdir(resolvedPath, { withFileTypes: true });
6343
+ const results = [];
6344
+ const cwdStr = this.cwd.endsWith(path4.sep) ? this.cwd : this.cwd + path4.sep;
6345
+ for (const entry of entries) {
6346
+ const fullPath = path4.join(resolvedPath, entry.name);
6347
+ try {
6348
+ const entryStat = await fs2.stat(fullPath);
6349
+ const isFile = entryStat.isFile();
6350
+ const isDir = entryStat.isDirectory();
6351
+ if (!this.virtualMode) {
6352
+ if (isFile) {
6353
+ results.push({
6354
+ path: fullPath,
6355
+ is_dir: false,
6356
+ size: entryStat.size,
6357
+ modified_at: entryStat.mtime.toISOString()
6358
+ });
6359
+ } else if (isDir) {
6360
+ results.push({
6361
+ path: fullPath + path4.sep,
6362
+ is_dir: true,
6363
+ size: 0,
6364
+ modified_at: entryStat.mtime.toISOString()
6365
+ });
6366
+ }
6367
+ } else {
6368
+ let relativePath;
6369
+ if (fullPath.startsWith(cwdStr)) {
6370
+ relativePath = fullPath.substring(cwdStr.length);
6371
+ } else if (fullPath.startsWith(this.cwd)) {
6372
+ relativePath = fullPath.substring(this.cwd.length).replace(/^[/\\]/, "");
6373
+ } else {
6374
+ relativePath = fullPath;
6375
+ }
6376
+ relativePath = relativePath.split(path4.sep).join("/");
6377
+ const virtPath = "/" + relativePath;
6378
+ if (isFile) {
6379
+ results.push({
6380
+ path: virtPath,
6381
+ is_dir: false,
6382
+ size: entryStat.size,
6383
+ modified_at: entryStat.mtime.toISOString()
6384
+ });
6385
+ } else if (isDir) {
6386
+ results.push({
6387
+ path: virtPath + "/",
6388
+ is_dir: true,
6389
+ size: 0,
6390
+ modified_at: entryStat.mtime.toISOString()
6391
+ });
6392
+ }
6393
+ }
6394
+ } catch {
6395
+ continue;
6396
+ }
6397
+ }
6398
+ results.sort((a, b) => a.path.localeCompare(b.path));
6399
+ return results;
6400
+ } catch (e) {
6401
+ console.error(`Error listing files in ${dirPath}:`, e);
6402
+ return [];
6403
+ }
6404
+ }
6405
+ /**
6406
+ * Read file content with line numbers.
6407
+ *
6408
+ * @param filePath - Absolute or relative file path
6409
+ * @param offset - Line offset to start reading from (0-indexed)
6410
+ * @param limit - Maximum number of lines to read
6411
+ * @returns Formatted file content with line numbers, or error message
6412
+ */
6413
+ async read(filePath, offset = 0, limit = 2e3) {
6414
+ try {
6415
+ const resolvedPath = this.resolvePath(filePath);
6416
+ let content;
6417
+ if (SUPPORTS_NOFOLLOW) {
6418
+ const stat3 = await fs2.stat(resolvedPath);
6419
+ if (!stat3.isFile()) {
6420
+ return `Error: File '${filePath}' not found`;
6421
+ }
6422
+ const fd = await fs2.open(
6423
+ resolvedPath,
6424
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6425
+ );
6426
+ try {
6427
+ content = await fd.readFile({ encoding: "utf-8" });
6428
+ } finally {
6429
+ await fd.close();
6430
+ }
6431
+ } else {
6432
+ const stat3 = await fs2.lstat(resolvedPath);
6433
+ if (stat3.isSymbolicLink()) {
6434
+ return `Error: Symlinks are not allowed: ${filePath}`;
6435
+ }
6436
+ if (!stat3.isFile()) {
6437
+ return `Error: File '${filePath}' not found`;
6438
+ }
6439
+ content = await fs2.readFile(resolvedPath, "utf-8");
6440
+ }
6441
+ const emptyMsg = checkEmptyContent(content);
6442
+ if (emptyMsg) {
6443
+ return emptyMsg;
6444
+ }
6445
+ const lines = content.split("\n");
6446
+ const startIdx = offset;
6447
+ const endIdx = Math.min(startIdx + limit, lines.length);
6448
+ if (startIdx >= lines.length) {
6449
+ return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
6450
+ }
6451
+ const selectedLines = lines.slice(startIdx, endIdx);
6452
+ return formatContentWithLineNumbers(selectedLines, startIdx + 1);
6453
+ } catch (e) {
6454
+ return `Error reading file '${filePath}': ${e.message}`;
6455
+ }
6456
+ }
6457
+ /**
6458
+ * Read file content as raw FileData.
6459
+ *
6460
+ * @param filePath - Absolute file path
6461
+ * @returns Raw file content as FileData
6462
+ */
6463
+ async readRaw(filePath) {
6464
+ const resolvedPath = this.resolvePath(filePath);
6465
+ let content;
6466
+ let stat3;
6467
+ if (SUPPORTS_NOFOLLOW) {
6468
+ stat3 = await fs2.stat(resolvedPath);
6469
+ if (!stat3.isFile()) throw new Error(`File '${filePath}' not found`);
6470
+ const fd = await fs2.open(
6471
+ resolvedPath,
6472
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6473
+ );
6474
+ try {
6475
+ content = await fd.readFile({ encoding: "utf-8" });
6476
+ } finally {
6477
+ await fd.close();
6478
+ }
6479
+ } else {
6480
+ stat3 = await fs2.lstat(resolvedPath);
6481
+ if (stat3.isSymbolicLink()) {
6482
+ throw new Error(`Symlinks are not allowed: ${filePath}`);
6483
+ }
6484
+ if (!stat3.isFile()) throw new Error(`File '${filePath}' not found`);
6485
+ content = await fs2.readFile(resolvedPath, "utf-8");
6486
+ }
6487
+ return {
6488
+ content: content.split("\n"),
6489
+ created_at: stat3.ctime.toISOString(),
6490
+ modified_at: stat3.mtime.toISOString()
6491
+ };
6492
+ }
6493
+ /**
6494
+ * Create a new file with content.
6495
+ * Returns WriteResult. External storage sets filesUpdate=null.
6496
+ */
6497
+ async write(filePath, content) {
6498
+ try {
6499
+ const resolvedPath = this.resolvePath(filePath);
6500
+ try {
6501
+ const stat3 = await fs2.lstat(resolvedPath);
6502
+ if (stat3.isSymbolicLink()) {
6503
+ return {
6504
+ error: `Cannot write to ${filePath} because it is a symlink. Symlinks are not allowed.`
6505
+ };
6506
+ }
6507
+ return {
6508
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
6509
+ };
6510
+ } catch {
6511
+ }
6512
+ await fs2.mkdir(path4.dirname(resolvedPath), { recursive: true });
6513
+ if (SUPPORTS_NOFOLLOW) {
6514
+ const flags = fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_TRUNC | fsSync.constants.O_NOFOLLOW;
6515
+ const fd = await fs2.open(resolvedPath, flags, 420);
6516
+ try {
6517
+ await fd.writeFile(content, "utf-8");
6518
+ } finally {
6519
+ await fd.close();
6520
+ }
6521
+ } else {
6522
+ await fs2.writeFile(resolvedPath, content, "utf-8");
6523
+ }
6524
+ return { path: filePath, filesUpdate: null };
6525
+ } catch (e) {
6526
+ return { error: `Error writing file '${filePath}': ${e.message}` };
6527
+ }
6528
+ }
6529
+ /**
6530
+ * Edit a file by replacing string occurrences.
6531
+ * Returns EditResult. External storage sets filesUpdate=null.
6532
+ */
6533
+ async edit(filePath, oldString, newString, replaceAll = false) {
6534
+ try {
6535
+ const resolvedPath = this.resolvePath(filePath);
6536
+ let content;
6537
+ if (SUPPORTS_NOFOLLOW) {
6538
+ const stat3 = await fs2.stat(resolvedPath);
6539
+ if (!stat3.isFile()) {
6540
+ return { error: `Error: File '${filePath}' not found` };
6541
+ }
6542
+ const fd = await fs2.open(
6543
+ resolvedPath,
6544
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6545
+ );
6546
+ try {
6547
+ content = await fd.readFile({ encoding: "utf-8" });
6548
+ } finally {
6549
+ await fd.close();
6550
+ }
6551
+ } else {
6552
+ const stat3 = await fs2.lstat(resolvedPath);
6553
+ if (stat3.isSymbolicLink()) {
6554
+ return { error: `Error: Symlinks are not allowed: ${filePath}` };
6555
+ }
6556
+ if (!stat3.isFile()) {
6557
+ return { error: `Error: File '${filePath}' not found` };
6558
+ }
6559
+ content = await fs2.readFile(resolvedPath, "utf-8");
6560
+ }
6561
+ const result = performStringReplacement(
6562
+ content,
6563
+ oldString,
6564
+ newString,
6565
+ replaceAll
6566
+ );
6567
+ if (typeof result === "string") {
6568
+ return { error: result };
6569
+ }
6570
+ const [newContent, occurrences] = result;
6571
+ if (SUPPORTS_NOFOLLOW) {
6572
+ const flags = fsSync.constants.O_WRONLY | fsSync.constants.O_TRUNC | fsSync.constants.O_NOFOLLOW;
6573
+ const fd = await fs2.open(resolvedPath, flags);
6574
+ try {
6575
+ await fd.writeFile(newContent, "utf-8");
6576
+ } finally {
6577
+ await fd.close();
6578
+ }
6579
+ } else {
6580
+ await fs2.writeFile(resolvedPath, newContent, "utf-8");
6581
+ }
6582
+ return { path: filePath, filesUpdate: null, occurrences };
6583
+ } catch (e) {
6584
+ return { error: `Error editing file '${filePath}': ${e.message}` };
6585
+ }
6586
+ }
6587
+ /**
6588
+ * Structured search results or error string for invalid input.
6589
+ */
6590
+ async grepRaw(pattern, dirPath = "/", glob = null) {
6591
+ try {
6592
+ new RegExp(pattern);
6593
+ } catch (e) {
6594
+ return `Invalid regex pattern: ${e.message}`;
6595
+ }
6596
+ let baseFull;
6597
+ try {
6598
+ baseFull = this.resolvePath(dirPath || ".");
6599
+ } catch {
6600
+ return [];
6601
+ }
6602
+ try {
6603
+ await fs2.stat(baseFull);
6604
+ } catch {
6605
+ return [];
6606
+ }
6607
+ let results = await this.ripgrepSearch(pattern, baseFull, glob);
6608
+ if (results === null) {
6609
+ results = await this.pythonSearch(pattern, baseFull, glob);
6610
+ }
6611
+ const matches = [];
6612
+ for (const [fpath, items] of Object.entries(results)) {
6613
+ for (const [lineNum, lineText] of items) {
6614
+ matches.push({ path: fpath, line: lineNum, text: lineText });
6615
+ }
6616
+ }
6617
+ return matches;
6618
+ }
6619
+ /**
6620
+ * Try to use ripgrep for fast searching.
6621
+ * Returns null if ripgrep is not available or fails.
6622
+ */
6623
+ async ripgrepSearch(pattern, baseFull, includeGlob) {
6624
+ return new Promise((resolve3) => {
6625
+ const args = ["--json"];
6626
+ if (includeGlob) {
6627
+ args.push("--glob", includeGlob);
6628
+ }
6629
+ args.push("--", pattern, baseFull);
6630
+ const proc = (0, import_child_process.spawn)("rg", args, { timeout: 3e4 });
6631
+ const results = {};
6632
+ let output = "";
6633
+ proc.stdout.on("data", (data) => {
6634
+ output += data.toString();
6635
+ });
6636
+ proc.on("close", (code) => {
6637
+ if (code !== 0 && code !== 1) {
6638
+ resolve3(null);
6639
+ return;
6640
+ }
6641
+ for (const line of output.split("\n")) {
6642
+ if (!line.trim()) continue;
6643
+ try {
6644
+ const data = JSON.parse(line);
6645
+ if (data.type !== "match") continue;
6646
+ const pdata = data.data || {};
6647
+ const ftext = pdata.path?.text;
6648
+ if (!ftext) continue;
6649
+ let virtPath;
6650
+ if (this.virtualMode) {
6651
+ try {
6652
+ const resolved = path4.resolve(ftext);
6653
+ const relative3 = path4.relative(this.cwd, resolved);
6654
+ if (relative3.startsWith("..")) continue;
6655
+ const normalizedRelative = relative3.split(path4.sep).join("/");
6656
+ virtPath = "/" + normalizedRelative;
6657
+ } catch {
6658
+ continue;
6659
+ }
6660
+ } else {
6661
+ virtPath = ftext;
6662
+ }
6663
+ const ln = pdata.line_number;
6664
+ const lt = pdata.lines?.text?.replace(/\n$/, "") || "";
6665
+ if (ln === void 0) continue;
6666
+ if (!results[virtPath]) {
6667
+ results[virtPath] = [];
6668
+ }
6669
+ results[virtPath].push([ln, lt]);
6670
+ } catch {
6671
+ continue;
6672
+ }
6673
+ }
6674
+ resolve3(results);
6675
+ });
6676
+ proc.on("error", () => {
6677
+ resolve3(null);
6678
+ });
6679
+ });
6680
+ }
6681
+ /**
6682
+ * Fallback regex search implementation.
6683
+ */
6684
+ async pythonSearch(pattern, baseFull, includeGlob) {
6685
+ let regex;
6686
+ try {
6687
+ regex = new RegExp(pattern);
6688
+ } catch {
6689
+ return {};
6690
+ }
6691
+ const results = {};
6692
+ const stat3 = await fs2.stat(baseFull);
6693
+ const root = stat3.isDirectory() ? baseFull : path4.dirname(baseFull);
6694
+ const files = await (0, import_fast_glob.default)("**/*", {
6695
+ cwd: root,
6696
+ absolute: true,
6697
+ onlyFiles: true,
6698
+ dot: true
6699
+ });
6700
+ for (const fp of files) {
6701
+ try {
6702
+ if (includeGlob && !import_micromatch2.default.isMatch(path4.basename(fp), includeGlob)) {
6703
+ continue;
6704
+ }
6705
+ const stat4 = await fs2.stat(fp);
6706
+ if (stat4.size > this.maxFileSizeBytes) {
6707
+ continue;
6708
+ }
6709
+ const content = await fs2.readFile(fp, "utf-8");
6710
+ const lines = content.split("\n");
6711
+ for (let i = 0; i < lines.length; i++) {
6712
+ const line = lines[i];
6713
+ if (regex.test(line)) {
6714
+ let virtPath;
6715
+ if (this.virtualMode) {
6716
+ try {
6717
+ const relative3 = path4.relative(this.cwd, fp);
6718
+ if (relative3.startsWith("..")) continue;
6719
+ const normalizedRelative = relative3.split(path4.sep).join("/");
6720
+ virtPath = "/" + normalizedRelative;
6721
+ } catch {
6722
+ continue;
6723
+ }
6724
+ } else {
6725
+ virtPath = fp;
6726
+ }
6727
+ if (!results[virtPath]) {
6728
+ results[virtPath] = [];
6729
+ }
6730
+ results[virtPath].push([i + 1, line]);
6731
+ }
6732
+ }
6733
+ } catch {
6734
+ continue;
6735
+ }
6736
+ }
6737
+ return results;
6738
+ }
6739
+ /**
6740
+ * Structured glob matching returning FileInfo objects.
6741
+ */
6742
+ async globInfo(pattern, searchPath = "/") {
6743
+ if (pattern.startsWith("/")) {
6744
+ pattern = pattern.substring(1);
6745
+ }
6746
+ const resolvedSearchPath = searchPath === "/" ? this.cwd : this.resolvePath(searchPath);
6747
+ try {
6748
+ const stat3 = await fs2.stat(resolvedSearchPath);
6749
+ if (!stat3.isDirectory()) {
6750
+ return [];
6751
+ }
6752
+ } catch {
6753
+ return [];
6754
+ }
6755
+ const results = [];
6756
+ try {
6757
+ const matches = await (0, import_fast_glob.default)(pattern, {
6758
+ cwd: resolvedSearchPath,
6759
+ absolute: true,
6760
+ onlyFiles: true,
6761
+ dot: true
6762
+ });
6763
+ for (const matchedPath of matches) {
6764
+ try {
6765
+ const stat3 = await fs2.stat(matchedPath);
6766
+ if (!stat3.isFile()) continue;
6767
+ const normalizedPath = matchedPath.split("/").join(path4.sep);
6768
+ if (!this.virtualMode) {
6769
+ results.push({
6770
+ path: normalizedPath,
6771
+ is_dir: false,
6772
+ size: stat3.size,
6773
+ modified_at: stat3.mtime.toISOString()
6774
+ });
6775
+ } else {
6776
+ const cwdStr = this.cwd.endsWith(path4.sep) ? this.cwd : this.cwd + path4.sep;
6777
+ let relativePath;
6778
+ if (normalizedPath.startsWith(cwdStr)) {
6779
+ relativePath = normalizedPath.substring(cwdStr.length);
6780
+ } else if (normalizedPath.startsWith(this.cwd)) {
6781
+ relativePath = normalizedPath.substring(this.cwd.length).replace(/^[/\\]/, "");
6782
+ } else {
6783
+ relativePath = normalizedPath;
6784
+ }
6785
+ relativePath = relativePath.split(path4.sep).join("/");
6786
+ const virt = "/" + relativePath;
6787
+ results.push({
6788
+ path: virt,
6789
+ is_dir: false,
6790
+ size: stat3.size,
6791
+ modified_at: stat3.mtime.toISOString()
6792
+ });
6793
+ }
6794
+ } catch {
6795
+ continue;
6796
+ }
6797
+ }
6798
+ } catch {
6799
+ }
6800
+ results.sort((a, b) => a.path.localeCompare(b.path));
6801
+ return results;
6802
+ }
6803
+ };
6804
+
6805
+ // src/deep_agent_new/backends/composite.ts
6806
+ var CompositeBackend = class {
6807
+ constructor(defaultBackend, routes) {
6808
+ this.default = defaultBackend;
6809
+ this.routes = routes;
6810
+ this.sortedRoutes = Object.entries(routes).sort(
6811
+ (a, b) => b[0].length - a[0].length
6812
+ );
6813
+ }
6814
+ /**
6815
+ * Determine which backend handles this key and strip prefix.
6816
+ *
6817
+ * @param key - Original file path
6818
+ * @returns Tuple of [backend, stripped_key] where stripped_key has the route
6819
+ * prefix removed (but keeps leading slash).
6820
+ */
6821
+ getBackendAndKey(key) {
6822
+ for (const [prefix, backend] of this.sortedRoutes) {
6823
+ if (key.startsWith(prefix)) {
6824
+ const suffix = key.substring(prefix.length);
6825
+ const strippedKey = suffix ? "/" + suffix : "/";
6826
+ return [backend, strippedKey];
6827
+ }
6828
+ }
6829
+ return [this.default, key];
6830
+ }
6831
+ /**
6832
+ * List files and directories in the specified directory (non-recursive).
6833
+ *
6834
+ * @param path - Absolute path to directory
6835
+ * @returns List of FileInfo objects with route prefixes added, for files and directories
6836
+ * directly in the directory. Directories have a trailing / in their path and is_dir=true.
6837
+ */
6838
+ async lsInfo(path5) {
6839
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6840
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6841
+ const suffix = path5.substring(routePrefix.length);
6842
+ const searchPath = suffix ? "/" + suffix : "/";
6843
+ const infos = await backend.lsInfo(searchPath);
6844
+ const prefixed = [];
6845
+ for (const fi of infos) {
6846
+ prefixed.push({
6847
+ ...fi,
6848
+ path: routePrefix.slice(0, -1) + fi.path
6849
+ });
6850
+ }
6851
+ return prefixed;
6852
+ }
6853
+ }
6854
+ if (path5 === "/") {
6855
+ const results = [];
6856
+ const defaultInfos = await this.default.lsInfo(path5);
6857
+ results.push(...defaultInfos);
6858
+ for (const [routePrefix] of this.sortedRoutes) {
6859
+ results.push({
6860
+ path: routePrefix,
6861
+ is_dir: true,
6862
+ size: 0,
6863
+ modified_at: ""
6864
+ });
6865
+ }
6866
+ results.sort((a, b) => a.path.localeCompare(b.path));
6867
+ return results;
6868
+ }
6869
+ return await this.default.lsInfo(path5);
6870
+ }
6871
+ /**
6872
+ * Read file content, routing to appropriate backend.
6873
+ *
6874
+ * @param filePath - Absolute file path
6875
+ * @param offset - Line offset to start reading from (0-indexed)
6876
+ * @param limit - Maximum number of lines to read
6877
+ * @returns Formatted file content with line numbers, or error message
6878
+ */
6879
+ async read(filePath, offset = 0, limit = 2e3) {
6880
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6881
+ return await backend.read(strippedKey, offset, limit);
6882
+ }
6883
+ /**
6884
+ * Read file content as raw FileData.
6885
+ *
6886
+ * @param filePath - Absolute file path
6887
+ * @returns Raw file content as FileData
6888
+ */
6889
+ async readRaw(filePath) {
6890
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6891
+ return await backend.readRaw(strippedKey);
6892
+ }
6893
+ /**
6894
+ * Structured search results or error string for invalid input.
6895
+ */
6896
+ async grepRaw(pattern, path5 = "/", glob = null) {
6897
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6898
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6899
+ const searchPath = path5.substring(routePrefix.length - 1);
6900
+ const raw = await backend.grepRaw(pattern, searchPath || "/", glob);
6901
+ if (typeof raw === "string") {
6902
+ return raw;
6903
+ }
6904
+ return raw.map((m) => ({
6905
+ ...m,
6906
+ path: routePrefix.slice(0, -1) + m.path
6907
+ }));
6908
+ }
6909
+ }
6910
+ const allMatches = [];
6911
+ const rawDefault = await this.default.grepRaw(pattern, path5, glob);
6912
+ if (typeof rawDefault === "string") {
6913
+ return rawDefault;
6914
+ }
6915
+ allMatches.push(...rawDefault);
6916
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
6917
+ const raw = await backend.grepRaw(pattern, "/", glob);
6918
+ if (typeof raw === "string") {
6919
+ return raw;
6920
+ }
6921
+ allMatches.push(
6922
+ ...raw.map((m) => ({
6923
+ ...m,
6924
+ path: routePrefix.slice(0, -1) + m.path
6925
+ }))
6926
+ );
6927
+ }
6928
+ return allMatches;
6929
+ }
6930
+ /**
6931
+ * Structured glob matching returning FileInfo objects.
6932
+ */
6933
+ async globInfo(pattern, path5 = "/") {
6934
+ const results = [];
6935
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6936
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6937
+ const searchPath = path5.substring(routePrefix.length - 1);
6938
+ const infos = await backend.globInfo(pattern, searchPath || "/");
6939
+ return infos.map((fi) => ({
6940
+ ...fi,
6941
+ path: routePrefix.slice(0, -1) + fi.path
6942
+ }));
6943
+ }
6944
+ }
6945
+ const defaultInfos = await this.default.globInfo(pattern, path5);
6946
+ results.push(...defaultInfos);
6947
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
6948
+ const infos = await backend.globInfo(pattern, "/");
6949
+ results.push(
6950
+ ...infos.map((fi) => ({
6951
+ ...fi,
6952
+ path: routePrefix.slice(0, -1) + fi.path
6953
+ }))
6954
+ );
6955
+ }
6956
+ results.sort((a, b) => a.path.localeCompare(b.path));
6957
+ return results;
6958
+ }
6959
+ /**
6960
+ * Create a new file, routing to appropriate backend.
6961
+ *
6962
+ * @param filePath - Absolute file path
6963
+ * @param content - File content as string
6964
+ * @returns WriteResult with path or error
6965
+ */
6966
+ async write(filePath, content) {
6967
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6968
+ return await backend.write(strippedKey, content);
6969
+ }
6970
+ /**
6971
+ * Edit a file, routing to appropriate backend.
6972
+ *
6973
+ * @param filePath - Absolute file path
6974
+ * @param oldString - String to find and replace
6975
+ * @param newString - Replacement string
6976
+ * @param replaceAll - If true, replace all occurrences
6977
+ * @returns EditResult with path, occurrences, or error
6978
+ */
6979
+ async edit(filePath, oldString, newString, replaceAll = false) {
6980
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6981
+ return await backend.edit(strippedKey, oldString, newString, replaceAll);
6982
+ }
6983
+ };
6984
+
6985
+ // src/deep_agent_new/backends/memory.ts
6986
+ var MemoryBackend = class {
6987
+ constructor(files) {
6988
+ this.files = files ?? /* @__PURE__ */ new Map();
6989
+ }
6990
+ getFiles() {
6991
+ return Object.fromEntries(this.files);
6992
+ }
6993
+ lsInfo(path5) {
6994
+ const files = this.getFiles();
6995
+ const infos = [];
6996
+ const subdirs = /* @__PURE__ */ new Set();
6997
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
6998
+ for (const [k, fd] of Object.entries(files)) {
6999
+ if (!k.startsWith(normalizedPath)) {
7000
+ continue;
7001
+ }
7002
+ const relative3 = k.substring(normalizedPath.length);
7003
+ if (relative3.includes("/")) {
7004
+ const subdirName = relative3.split("/")[0];
7005
+ subdirs.add(normalizedPath + subdirName + "/");
7006
+ continue;
7007
+ }
7008
+ const size = fd.content.join("\n").length;
7009
+ infos.push({
7010
+ path: k,
7011
+ is_dir: false,
7012
+ size,
7013
+ modified_at: fd.modified_at
7014
+ });
7015
+ }
7016
+ for (const subdir of Array.from(subdirs).sort()) {
7017
+ infos.push({
7018
+ path: subdir,
7019
+ is_dir: true,
7020
+ size: 0,
7021
+ modified_at: ""
7022
+ });
7023
+ }
7024
+ infos.sort((a, b) => a.path.localeCompare(b.path));
7025
+ return infos;
7026
+ }
7027
+ read(filePath, offset = 0, limit = 2e3) {
7028
+ const files = this.getFiles();
7029
+ const fileData = files[filePath];
7030
+ if (!fileData) {
7031
+ return `Error: File '${filePath}' not found`;
7032
+ }
7033
+ return formatReadResponse(fileData, offset, limit);
7034
+ }
7035
+ readRaw(filePath) {
7036
+ const fileData = this.files.get(filePath);
7037
+ if (!fileData) {
7038
+ throw new Error(`File '${filePath}' not found`);
7039
+ }
7040
+ return fileData;
7041
+ }
7042
+ write(filePath, content) {
7043
+ if (this.files.has(filePath)) {
7044
+ return {
7045
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
7046
+ };
7047
+ }
7048
+ const newFileData = createFileData(content);
7049
+ this.files.set(filePath, newFileData);
7050
+ return { path: filePath, filesUpdate: null };
7051
+ }
7052
+ edit(filePath, oldString, newString, replaceAll = false) {
7053
+ const fileData = this.files.get(filePath);
7054
+ if (!fileData) {
7055
+ return { error: `Error: File '${filePath}' not found` };
7056
+ }
7057
+ const content = fileDataToString(fileData);
7058
+ const result = performStringReplacement(
7059
+ content,
7060
+ oldString,
7061
+ newString,
7062
+ replaceAll
7063
+ );
7064
+ if (typeof result === "string") {
7065
+ return { error: result };
7066
+ }
7067
+ const [newContent, occurrences] = result;
7068
+ const newFileData = updateFileData(fileData, newContent);
7069
+ this.files.set(filePath, newFileData);
7070
+ return { path: filePath, filesUpdate: null, occurrences };
7071
+ }
7072
+ grepRaw(pattern, path5 = "/", glob = null) {
7073
+ const files = this.getFiles();
7074
+ return grepMatchesFromFiles(files, pattern, path5, glob);
7075
+ }
7076
+ globInfo(pattern, path5 = "/") {
7077
+ const files = this.getFiles();
7078
+ const result = globSearchFiles(files, pattern, path5);
7079
+ if (result === "No files found") {
7080
+ return [];
7081
+ }
7082
+ const paths = result.split("\n");
7083
+ const infos = [];
7084
+ for (const p of paths) {
7085
+ const fd = files[p];
7086
+ const size = fd ? fd.content.join("\n").length : 0;
7087
+ infos.push({
7088
+ path: p,
7089
+ is_dir: false,
7090
+ size,
7091
+ modified_at: fd?.modified_at || ""
7092
+ });
7093
+ }
7094
+ return infos;
7095
+ }
7096
+ };
7097
+
7098
+ // src/deep_agent_new/middleware/todos.ts
7099
+ var import_langgraph8 = require("@langchain/langgraph");
7100
+ var import_zod36 = require("zod");
7101
+ var import_langchain39 = require("langchain");
7102
+ var WRITE_TODOS_DESCRIPTION = `Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
7103
+ It also helps the user understand the progress of the task and overall progress of their requests.
7104
+ Only use this tool if you think it will be helpful in staying organized. If the user's request is trivial and takes less than 3 steps, it is better to NOT use this tool and just do the taks directly.
7105
+
7106
+ ## When to Use This Tool
7107
+ Use this tool in these scenarios:
7108
+
7109
+ 1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
7110
+ 2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
7111
+ 3. User explicitly requests todo list - When the user directly asks you to use the todo list
7112
+ 4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
7113
+ 5. The plan may need future revisions or updates based on results from the first few steps. Keeping track of this in a list is helpful.
7114
+
7115
+ ## How to Use This Tool
7116
+ 1. When you start working on a task - Mark it as in_progress BEFORE beginning work.
7117
+ 2. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation.
7118
+ 3. You can also update future tasks, such as deleting them if they are no longer necessary, or adding new tasks that are necessary. Don't change previously completed tasks.
7119
+ 4. You can make several updates to the todo list at once. For example, when you complete a task, you can mark the next task you need to start as in_progress.
7120
+
7121
+ ## When NOT to Use This Tool
7122
+ It is important to skip using this tool when:
7123
+ 1. There is only a single, straightforward task
7124
+ 2. The task is trivial and tracking it provides no benefit
7125
+ 3. The task can be completed in less than 3 trivial steps
7126
+ 4. The task is purely conversational or informational
7127
+
7128
+ ## Examples of When to Use the Todo List
7129
+
7130
+ <example>
7131
+ User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
7132
+ Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
7133
+ *Creates todo list with the following items:*
7134
+ 1. Create dark mode toggle component in Settings page
7135
+ 2. Add dark mode state management (context/store)
7136
+ 3. Implement CSS-in-JS styles for dark theme
7137
+ 4. Update existing components to support theme switching
7138
+ 5. Run tests and build process, addressing any failures or errors that occur
7139
+ *Begins working on the first task*
7140
+
7141
+ <reasoning>
7142
+ The assistant used the todo list because:
7143
+ 1. Adding dark mode in it of itself is a multi-step feature requiring UI, state management, and styling changes
7144
+ 2. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
5602
7145
  3. Both of the user's requests are complex and require multiple steps to complete.
5603
7146
  </reasoning>
5604
7147
  </example>
@@ -5704,289 +7247,1896 @@ Assistant: I'll check the current time in Tokyo for you.
5704
7247
 
5705
7248
  *Searches for current time in Tokyo*
5706
7249
 
5707
- The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
7250
+ The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
7251
+
7252
+ <reasoning>
7253
+ The assistant did not use the todo list because this is a single information lookup with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward request.
7254
+ </reasoning>
7255
+ </example>
7256
+
7257
+ <example>
7258
+ User: I need to write a function that checks if a number is prime and then test it out.
7259
+ Assistant: I'll help you write a function that checks if a number is prime and then test it out.
7260
+ *Writes function that checks if a number is prime*
7261
+ *Tests the function*
7262
+
7263
+ <reasoning>
7264
+ Even though this is a multi-step task, it is very straightforward and can be completed in two trivial steps (which is less than 3 steps!). Using the todo list here is overkill and wastes time and tokens.
7265
+ </reasoning>
7266
+ </example>
7267
+
7268
+ <example>
7269
+ User: I want you to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
7270
+ Assistant: I'll help you order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
7271
+ *Orders a pizza from Dominos*
7272
+ *Orders a burger from McDonald's*
7273
+ *Orders a salad from Subway*
7274
+
7275
+ <reasoning>
7276
+ Even though this is a multi-step task, assuming the assistant has the ability to order from these restaurants, it is very straightforward and can be completed in three trivial tool calls.
7277
+ Using the todo list here is overkill and wastes time and tokens. These three tool calls should be made in parallel, in fact.
7278
+ </reasoning>
7279
+ </example>
7280
+
7281
+
7282
+ ## Task States and Management
7283
+
7284
+ 1. **Task States**: Use these states to track progress:
7285
+ - pending: Task not yet started
7286
+ - in_progress: Currently working on (you can have multiple tasks in_progress at a time if they are not related to each other and can be run in parallel)
7287
+ - completed: Task finished successfully
7288
+
7289
+ 2. **Task Management**:
7290
+ - Update task status in real-time as you work
7291
+ - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
7292
+ - Complete current tasks before starting new ones
7293
+ - Remove tasks that are no longer relevant from the list entirely
7294
+ - IMPORTANT: When you write this todo list, you should mark your first task (or tasks) as in_progress immediately!.
7295
+ - IMPORTANT: Unless all tasks are completed, you should always have at least one task in_progress to show the user that you are working on something.
7296
+
7297
+ 3. **Task Completion Requirements**:
7298
+ - ONLY mark a task as completed when you have FULLY accomplished it
7299
+ - If you encounter errors, blockers, or cannot finish, keep the task as in_progress
7300
+ - When blocked, create a new task describing what needs to be resolved
7301
+ - Never mark a task as completed if:
7302
+ - There are unresolved issues or errors
7303
+ - Work is partial or incomplete
7304
+ - You encountered blockers that prevent completion
7305
+ - You couldn't find necessary resources or dependencies
7306
+ - Quality standards haven't been met
7307
+
7308
+ 4. **Task Breakdown**:
7309
+ - Create specific, actionable items
7310
+ - Break complex tasks into smaller, manageable steps
7311
+ - Use clear, descriptive task names
7312
+
7313
+ Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully
7314
+ Remember: If you only need to make a few tool calls to complete a task, and it is clear what you need to do, it is better to just do the task directly and NOT call this tool at all.`;
7315
+ var TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT = `## \`write_todos\`
7316
+
7317
+ You have access to the \`write_todos\` tool to help you manage and plan complex objectives.
7318
+ Use this tool for complex objectives to ensure that you are tracking each necessary step and giving the user visibility into your progress.
7319
+ This tool is very helpful for planning complex objectives, and for breaking down these larger complex objectives into smaller steps.
7320
+
7321
+ It is critical that you mark todos as completed as soon as you are done with a step. Do not batch up multiple steps before marking them as completed.
7322
+ For simple objectives that only require a few steps, it is better to just complete the objective directly and NOT use this tool.
7323
+ Writing todos takes time and tokens, use it when it is helpful for managing complex many-step problems! But not for simple few-step requests.
5708
7324
 
5709
- <reasoning>
5710
- The assistant did not use the todo list because this is a single information lookup with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward request.
5711
- </reasoning>
5712
- </example>
7325
+ ## Important To-Do List Usage Notes to Remember
7326
+ - The \`write_todos\` tool should never be called multiple times in parallel.
7327
+ - Don't be afraid to revise the To-Do list as you go. New information may reveal new tasks that need to be done, or old tasks that are irrelevant.`;
7328
+ var TodoStatus = import_zod36.z.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
7329
+ var TodoSchema = import_zod36.z.object({
7330
+ content: import_zod36.z.string().describe("Content of the todo item"),
7331
+ status: TodoStatus
7332
+ });
7333
+ var stateSchema = import_zod36.z.object({ todos: import_zod36.z.array(TodoSchema).default([]) });
7334
+ function todoListMiddleware(options) {
7335
+ const writeTodos = (0, import_langchain39.tool)(
7336
+ ({ todos }, config) => {
7337
+ return new import_langgraph8.Command({
7338
+ update: {
7339
+ todos,
7340
+ messages: [
7341
+ new import_langchain39.ToolMessage({
7342
+ content: genUIMarkdown("todo_list", todos),
7343
+ tool_call_id: config.toolCall?.id
7344
+ })
7345
+ ]
7346
+ }
7347
+ });
7348
+ },
7349
+ {
7350
+ name: "write_todos",
7351
+ description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
7352
+ schema: import_zod36.z.object({
7353
+ todos: import_zod36.z.array(TodoSchema).describe("List of todo items to update")
7354
+ })
7355
+ }
7356
+ );
7357
+ return (0, import_langchain39.createMiddleware)({
7358
+ name: "todoListMiddleware",
7359
+ stateSchema,
7360
+ tools: [writeTodos],
7361
+ wrapModelCall: (request, handler) => handler({
7362
+ ...request,
7363
+ systemPrompt: (request.systemPrompt ? `${request.systemPrompt}
5713
7364
 
5714
- <example>
5715
- User: I need to write a function that checks if a number is prime and then test it out.
5716
- Assistant: I'll help you write a function that checks if a number is prime and then test it out.
5717
- *Writes function that checks if a number is prime*
5718
- *Tests the function*
7365
+ ` : "") + (options?.systemPrompt ?? TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT)
7366
+ })
7367
+ });
7368
+ }
5719
7369
 
5720
- <reasoning>
5721
- Even though this is a multi-step task, it is very straightforward and can be completed in two trivial steps (which is less than 3 steps!). Using the todo list here is overkill and wastes time and tokens.
5722
- </reasoning>
5723
- </example>
7370
+ // src/deep_agent_new/agent.ts
7371
+ var BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
7372
+ function createDeepAgent(params = {}) {
7373
+ const {
7374
+ model = "claude-sonnet-4-5-20250929",
7375
+ tools = [],
7376
+ systemPrompt,
7377
+ middleware: customMiddleware = [],
7378
+ subagents = [],
7379
+ responseFormat,
7380
+ contextSchema,
7381
+ checkpointer,
7382
+ store,
7383
+ backend,
7384
+ interruptOn,
7385
+ name,
7386
+ skills
7387
+ } = params;
7388
+ const finalSystemPrompt = systemPrompt ? `${systemPrompt}
5724
7389
 
5725
- <example>
5726
- User: I want you to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
5727
- Assistant: I'll help you order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
5728
- *Orders a pizza from Dominos*
5729
- *Orders a burger from McDonald's*
5730
- *Orders a salad from Subway*
7390
+ ${BASE_PROMPT}` : BASE_PROMPT;
7391
+ const filesystemBackend = backend ? backend : async (config) => new StateBackend(config);
7392
+ const middleware = [
7393
+ // Provides todo list management capabilities for tracking tasks
7394
+ todoListMiddleware(),
7395
+ // Enables filesystem operations and optional long-term memory storage
7396
+ createFilesystemMiddleware({ backend: filesystemBackend }),
7397
+ // Enables delegation to specialized subagents for complex tasks
7398
+ createSubAgentMiddleware({
7399
+ defaultModel: model,
7400
+ defaultTools: tools,
7401
+ defaultMiddleware: [
7402
+ // Subagent middleware: Todo list management
7403
+ todoListMiddleware(),
7404
+ // Subagent middleware: Filesystem operations
7405
+ createFilesystemMiddleware({
7406
+ backend: filesystemBackend
7407
+ }),
7408
+ // Subagent middleware: Automatic conversation summarization when token limits are approached
7409
+ (0, import_langchain40.summarizationMiddleware)({
7410
+ model,
7411
+ trigger: { tokens: 17e4 },
7412
+ keep: { messages: 6 }
7413
+ }),
7414
+ // Subagent middleware: Anthropic prompt caching for improved performance
7415
+ (0, import_langchain40.anthropicPromptCachingMiddleware)({
7416
+ unsupportedModelBehavior: "ignore"
7417
+ }),
7418
+ // Subagent middleware: Patches tool calls for compatibility
7419
+ createPatchToolCallsMiddleware()
7420
+ ],
7421
+ defaultInterruptOn: interruptOn,
7422
+ subagents,
7423
+ generalPurposeAgent: false
7424
+ }),
7425
+ // Automatically summarizes conversation history when token limits are approached
7426
+ (0, import_langchain40.summarizationMiddleware)({
7427
+ model,
7428
+ trigger: { tokens: 17e4 },
7429
+ keep: { messages: 6 }
7430
+ }),
7431
+ // Enables Anthropic prompt caching for improved performance and reduced costs
7432
+ (0, import_langchain40.anthropicPromptCachingMiddleware)({
7433
+ unsupportedModelBehavior: "ignore"
7434
+ }),
7435
+ // Patches tool calls to ensure compatibility across different model providers
7436
+ createPatchToolCallsMiddleware()
7437
+ ];
7438
+ if (interruptOn) {
7439
+ middleware.push((0, import_langchain40.humanInTheLoopMiddleware)({ interruptOn }));
7440
+ }
7441
+ middleware.push(...customMiddleware);
7442
+ return (0, import_langchain40.createAgent)({
7443
+ model,
7444
+ systemPrompt: finalSystemPrompt,
7445
+ tools,
7446
+ middleware,
7447
+ responseFormat,
7448
+ contextSchema,
7449
+ checkpointer,
7450
+ store,
7451
+ name
7452
+ });
7453
+ }
5731
7454
 
5732
- <reasoning>
5733
- Even though this is a multi-step task, assuming the assistant has the ability to order from these restaurants, it is very straightforward and can be completed in three trivial tool calls.
5734
- Using the todo list here is overkill and wastes time and tokens. These three tool calls should be made in parallel, in fact.
5735
- </reasoning>
5736
- </example>
7455
+ // src/agent_lattice/builders/filesystemBackend.ts
7456
+ function createFilesystemBackendFactory(middlewareConfigs) {
7457
+ const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
7458
+ if (!filesystemConfig || !filesystemConfig.enabled) {
7459
+ return void 0;
7460
+ }
7461
+ const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
7462
+ return async (config) => {
7463
+ const { workspaceId, projectId } = config;
7464
+ let sandboxName = "global";
7465
+ if (isolatedLevel === "agent") {
7466
+ sandboxName = "agent";
7467
+ } else if (isolatedLevel === "thread") {
7468
+ sandboxName = "thread";
7469
+ }
7470
+ const sandboxManager = sandboxLatticeManager.getSandboxLattice("default");
7471
+ if (!sandboxManager) {
7472
+ throw new Error("Sandbox manager not found");
7473
+ }
7474
+ return new SandboxFilesystem({
7475
+ sandboxInstance: await sandboxManager.createSandbox(sandboxName),
7476
+ workingDirectory: workspaceId && projectId ? `/workspaces/${workspaceId}/${projectId}` : "/"
7477
+ });
7478
+ };
7479
+ }
5737
7480
 
7481
+ // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
7482
+ var DeepAgentGraphBuilder = class {
7483
+ /**
7484
+ * 根据 middleware 配置创建 middlewares
7485
+ */
7486
+ createMiddlewares(middlewareConfigs) {
7487
+ return createCommonMiddlewares(middlewareConfigs);
7488
+ }
7489
+ /**
7490
+ * 构建Deep Agent Graph
7491
+ *
7492
+ * @param agentLattice Agent Lattice对象
7493
+ * @param params Agent构建参数
7494
+ * @returns 返回CompiledGraph对象
7495
+ */
7496
+ build(agentLattice, params) {
7497
+ const tools = params.tools.map((t) => {
7498
+ const toolClient = getToolClient(t.key);
7499
+ return toolClient;
7500
+ }).filter((tool38) => tool38 !== void 0);
7501
+ const subagents = params.subAgents.map((sa) => {
7502
+ if (sa.client) {
7503
+ return {
7504
+ name: sa.config.name,
7505
+ description: sa.config.description,
7506
+ runnable: sa.client
7507
+ };
7508
+ } else {
7509
+ const subagentClient = createAgentClientFromAgentLattice({
7510
+ config: sa.config
7511
+ });
7512
+ return {
7513
+ name: sa.config.name,
7514
+ description: sa.config.description,
7515
+ runnable: subagentClient
7516
+ };
7517
+ }
7518
+ });
7519
+ const middlewareConfigs = params.middleware || [];
7520
+ const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
7521
+ const middlewares = this.createMiddlewares(middlewareConfigs);
7522
+ const deepAgent = createDeepAgent({
7523
+ tools,
7524
+ model: params.model,
7525
+ contextSchema: params.stateSchema,
7526
+ systemPrompt: params.prompt,
7527
+ subagents,
7528
+ checkpointer: getCheckpointSaver("default"),
7529
+ skills: params.skillCategories,
7530
+ backend: filesystemBackend,
7531
+ middleware: middlewares
7532
+ });
7533
+ return deepAgent;
7534
+ }
7535
+ };
5738
7536
 
5739
- ## Task States and Management
7537
+ // src/agent_team/agent_team.ts
7538
+ var import_v35 = require("zod/v3");
7539
+ var import_langchain43 = require("langchain");
7540
+
7541
+ // src/agent_team/types.ts
7542
+ var TaskStatus = /* @__PURE__ */ ((TaskStatus3) => {
7543
+ TaskStatus3["PENDING"] = "pending";
7544
+ TaskStatus3["CLAIMED"] = "claimed";
7545
+ TaskStatus3["IN_PROGRESS"] = "in_progress";
7546
+ TaskStatus3["COMPLETED"] = "completed";
7547
+ TaskStatus3["FAILED"] = "failed";
7548
+ return TaskStatus3;
7549
+ })(TaskStatus || {});
7550
+ var MessageType = /* @__PURE__ */ ((MessageType2) => {
7551
+ MessageType2["DIRECT_MESSAGE"] = "direct_message";
7552
+ MessageType2["BROADCAST"] = "broadcast";
7553
+ MessageType2["PLAN_REQUEST"] = "plan_request";
7554
+ MessageType2["PLAN_FEEDBACK"] = "plan_feedback";
7555
+ MessageType2["SHUTDOWN_REQUEST"] = "shutdown_request";
7556
+ MessageType2["SHUTDOWN_RESPONSE"] = "shutdown_response";
7557
+ MessageType2["TASK_FEEDBACK"] = "task_feedback";
7558
+ MessageType2["STATUS_UPDATE"] = "status_update";
7559
+ return MessageType2;
7560
+ })(MessageType || {});
7561
+
7562
+ // src/agent_team/stores/InMemoryTaskListStore.ts
7563
+ var import_events2 = require("events");
7564
+ var InMemoryTaskListStore = class {
7565
+ constructor() {
7566
+ /** Map<teamId, Map<taskId, TeamTask>> */
7567
+ this.tasks = /* @__PURE__ */ new Map();
7568
+ /** EventEmitter for task lifecycle events */
7569
+ this.emitter = new import_events2.EventEmitter();
7570
+ /** Auto-incrementing ID counter */
7571
+ this.idCounter = 0;
7572
+ this.emitter.setMaxListeners(200);
7573
+ }
7574
+ // -------------------------------------------------------------------------
7575
+ // Helpers
7576
+ // -------------------------------------------------------------------------
7577
+ /** Get or create the task map for a team. */
7578
+ getTeamTasks(teamId) {
7579
+ let map = this.tasks.get(teamId);
7580
+ if (!map) {
7581
+ map = /* @__PURE__ */ new Map();
7582
+ this.tasks.set(teamId, map);
7583
+ }
7584
+ return map;
7585
+ }
7586
+ /** Generate a unique task ID. */
7587
+ nextId() {
7588
+ this.idCounter += 1;
7589
+ return `task-${this.idCounter}`;
7590
+ }
7591
+ /** Emit a scoped event: `${teamId}:${event}`. */
7592
+ emit(teamId, event, task) {
7593
+ this.emitter.emit(`${teamId}:${event}`, task);
7594
+ }
7595
+ /**
7596
+ * Resolve dependency references by task IDs.
7597
+ */
7598
+ resolveDependencies(teamTasks, depIds) {
7599
+ const resolved = [];
7600
+ for (const ref of depIds) {
7601
+ if (teamTasks.has(ref)) {
7602
+ resolved.push(ref);
7603
+ }
7604
+ }
7605
+ return resolved;
7606
+ }
7607
+ /**
7608
+ * Check whether all dependencies of a task are in COMPLETED status.
7609
+ */
7610
+ areDependenciesSatisfied(teamTasks, task) {
7611
+ if (task.dependencies.length === 0) return true;
7612
+ return task.dependencies.every((depId) => {
7613
+ const dep = teamTasks.get(depId);
7614
+ return dep !== void 0 && dep.status === "completed" /* COMPLETED */;
7615
+ });
7616
+ }
7617
+ // -------------------------------------------------------------------------
7618
+ // Lifecycle
7619
+ // -------------------------------------------------------------------------
7620
+ async addTask(teamId, spec) {
7621
+ const teamTasks = this.getTeamTasks(teamId);
7622
+ if (!/^task-\d+$/.test(spec.id)) {
7623
+ throw new Error(`Invalid task ID format: ${spec.id}. Expected format: task-01, task-02, etc.`);
7624
+ }
7625
+ if (teamTasks.has(spec.id)) {
7626
+ throw new Error(`Task ID already exists: ${spec.id}`);
7627
+ }
7628
+ const now = /* @__PURE__ */ new Date();
7629
+ const task = {
7630
+ id: spec.id,
7631
+ title: spec.title,
7632
+ description: spec.description,
7633
+ assignee: spec.assignee,
7634
+ status: "pending" /* PENDING */,
7635
+ dependencies: spec.dependencies ? this.resolveDependencies(teamTasks, spec.dependencies) : [],
7636
+ createdAt: now,
7637
+ updatedAt: now
7638
+ };
7639
+ teamTasks.set(task.id, task);
7640
+ this.emit(teamId, "task:added", task);
7641
+ return task;
7642
+ }
7643
+ async addTasks(teamId, specs) {
7644
+ const teamTasks = this.getTeamTasks(teamId);
7645
+ const now = /* @__PURE__ */ new Date();
7646
+ const created = [];
7647
+ const seenIds = /* @__PURE__ */ new Set();
7648
+ for (const spec of specs) {
7649
+ if (!/^task-\d+$/.test(spec.id)) {
7650
+ throw new Error(`Invalid task ID format: ${spec.id}. Expected format: task-01, task-02, etc.`);
7651
+ }
7652
+ if (seenIds.has(spec.id)) {
7653
+ throw new Error(`Duplicate task ID: ${spec.id}`);
7654
+ }
7655
+ if (teamTasks.has(spec.id)) {
7656
+ throw new Error(`Task ID already exists: ${spec.id}`);
7657
+ }
7658
+ seenIds.add(spec.id);
7659
+ }
7660
+ for (const spec of specs) {
7661
+ const task = {
7662
+ id: spec.id,
7663
+ title: spec.title,
7664
+ description: spec.description,
7665
+ assignee: spec.assignee,
7666
+ status: "pending" /* PENDING */,
7667
+ dependencies: [],
7668
+ createdAt: now,
7669
+ updatedAt: now
7670
+ };
7671
+ teamTasks.set(task.id, task);
7672
+ created.push(task);
7673
+ }
7674
+ for (let i = 0; i < specs.length; i++) {
7675
+ if (specs[i].dependencies && specs[i].dependencies.length > 0) {
7676
+ created[i].dependencies = this.resolveDependencies(
7677
+ teamTasks,
7678
+ specs[i].dependencies
7679
+ );
7680
+ }
7681
+ }
7682
+ for (const task of created) {
7683
+ this.emit(teamId, "task:added", task);
7684
+ }
7685
+ return created;
7686
+ }
7687
+ async updateTask(teamId, taskId, updates) {
7688
+ const teamTasks = this.getTeamTasks(teamId);
7689
+ const task = teamTasks.get(taskId);
7690
+ if (!task) return null;
7691
+ if (updates.title !== void 0) task.title = updates.title;
7692
+ if (updates.description !== void 0) task.description = updates.description;
7693
+ if (updates.assignee !== void 0) task.assignee = updates.assignee;
7694
+ if (updates.status !== void 0) task.status = updates.status;
7695
+ if (updates.dependencies !== void 0) {
7696
+ task.dependencies = this.resolveDependencies(teamTasks, updates.dependencies);
7697
+ }
7698
+ task.updatedAt = /* @__PURE__ */ new Date();
7699
+ return task;
7700
+ }
7701
+ async removeTask(teamId, taskId) {
7702
+ const teamTasks = this.getTeamTasks(teamId);
7703
+ const task = teamTasks.get(taskId);
7704
+ if (!task) return false;
7705
+ teamTasks.delete(taskId);
7706
+ this.emit(teamId, "task:removed", task);
7707
+ return true;
7708
+ }
7709
+ // -------------------------------------------------------------------------
7710
+ // Claim / Complete
7711
+ // -------------------------------------------------------------------------
7712
+ /**
7713
+ * Claim a specific task by ID: set assignee to agentId and status to CLAIMED.
7714
+ * Only succeeds if the task is PENDING and all dependencies are COMPLETED.
7715
+ */
7716
+ async claimTaskById(teamId, taskId, agentId) {
7717
+ const teamTasks = this.getTeamTasks(teamId);
7718
+ const task = teamTasks.get(taskId);
7719
+ if (!task) return null;
7720
+ if (task.status !== "pending" /* PENDING */) return null;
7721
+ if (!this.areDependenciesSatisfied(teamTasks, task)) return null;
7722
+ task.status = "claimed" /* CLAIMED */;
7723
+ task.assignee = agentId;
7724
+ task.updatedAt = /* @__PURE__ */ new Date();
7725
+ this.emit(teamId, "task:claimed", task);
7726
+ return task;
7727
+ }
7728
+ async completeTask(teamId, taskId, result) {
7729
+ const teamTasks = this.getTeamTasks(teamId);
7730
+ const task = teamTasks.get(taskId);
7731
+ if (!task) return null;
7732
+ task.status = "completed" /* COMPLETED */;
7733
+ task.result = result;
7734
+ task.updatedAt = /* @__PURE__ */ new Date();
7735
+ this.emit(teamId, "task:completed", task);
7736
+ return task;
7737
+ }
7738
+ async failTask(teamId, taskId, error) {
7739
+ const teamTasks = this.getTeamTasks(teamId);
7740
+ const task = teamTasks.get(taskId);
7741
+ if (!task) return null;
7742
+ task.status = "failed" /* FAILED */;
7743
+ task.error = error;
7744
+ task.updatedAt = /* @__PURE__ */ new Date();
7745
+ this.emit(teamId, "task:failed", task);
7746
+ return task;
7747
+ }
7748
+ // -------------------------------------------------------------------------
7749
+ // Query
7750
+ // -------------------------------------------------------------------------
7751
+ async getTask(teamId, taskId) {
7752
+ const teamTasks = this.tasks.get(teamId);
7753
+ if (!teamTasks) return null;
7754
+ return teamTasks.get(taskId) ?? null;
7755
+ }
7756
+ async getTasksByStatus(teamId, status) {
7757
+ const teamTasks = this.tasks.get(teamId);
7758
+ if (!teamTasks) return [];
7759
+ return Array.from(teamTasks.values()).filter((t) => t.status === status);
7760
+ }
7761
+ async getAllTasks(teamId) {
7762
+ const teamTasks = this.tasks.get(teamId);
7763
+ if (!teamTasks) return [];
7764
+ return Array.from(teamTasks.values());
7765
+ }
7766
+ async isAllDone(teamId) {
7767
+ const teamTasks = this.tasks.get(teamId);
7768
+ if (!teamTasks || teamTasks.size === 0) return true;
7769
+ for (const task of teamTasks.values()) {
7770
+ if (task.status === "pending" /* PENDING */ || task.status === "claimed" /* CLAIMED */ || task.status === "in_progress" /* IN_PROGRESS */) {
7771
+ return false;
7772
+ }
7773
+ }
7774
+ return true;
7775
+ }
7776
+ async hasClaimable(teamId) {
7777
+ const teamTasks = this.tasks.get(teamId);
7778
+ if (!teamTasks) return false;
7779
+ for (const task of teamTasks.values()) {
7780
+ if (task.status === "pending" /* PENDING */ && this.areDependenciesSatisfied(teamTasks, task)) {
7781
+ return true;
7782
+ }
7783
+ }
7784
+ return false;
7785
+ }
7786
+ // -------------------------------------------------------------------------
7787
+ // Events
7788
+ // -------------------------------------------------------------------------
7789
+ onTaskEvent(teamId, event, callback) {
7790
+ this.emitter.on(`${teamId}:${event}`, callback);
7791
+ }
7792
+ offTaskEvent(teamId, event, callback) {
7793
+ this.emitter.off(`${teamId}:${event}`, callback);
7794
+ }
7795
+ // -------------------------------------------------------------------------
7796
+ // Utilities
7797
+ // -------------------------------------------------------------------------
7798
+ /** Remove all tasks for a team (useful for cleanup / testing). */
7799
+ clearTeam(teamId) {
7800
+ this.tasks.delete(teamId);
7801
+ }
7802
+ /** Remove all tasks across all teams (useful for testing). */
7803
+ clear() {
7804
+ this.tasks.clear();
7805
+ }
7806
+ };
5740
7807
 
5741
- 1. **Task States**: Use these states to track progress:
5742
- - pending: Task not yet started
5743
- - in_progress: Currently working on (you can have multiple tasks in_progress at a time if they are not related to each other and can be run in parallel)
5744
- - completed: Task finished successfully
7808
+ // src/agent_team/stores/InMemoryMailboxStore.ts
7809
+ var import_events3 = require("events");
7810
+ var InMemoryMailboxStore = class {
7811
+ constructor() {
7812
+ /** Map<teamId, Map<agentId, MailboxMessage[]>> */
7813
+ this.messages = /* @__PURE__ */ new Map();
7814
+ /** Map<teamId, Set<agentId>> -- registered agents for broadcast */
7815
+ this.agents = /* @__PURE__ */ new Map();
7816
+ /** EventEmitter for real-time message notifications */
7817
+ this.emitter = new import_events3.EventEmitter();
7818
+ /** Auto-incrementing message ID counter */
7819
+ this.idCounter = 0;
7820
+ this.emitter.setMaxListeners(200);
7821
+ }
7822
+ // -------------------------------------------------------------------------
7823
+ // Helpers
7824
+ // -------------------------------------------------------------------------
7825
+ /** Get or create the agent message list for a team. */
7826
+ getAgentMessages(teamId, agentId) {
7827
+ let teamMap = this.messages.get(teamId);
7828
+ if (!teamMap) {
7829
+ teamMap = /* @__PURE__ */ new Map();
7830
+ this.messages.set(teamId, teamMap);
7831
+ }
7832
+ let list = teamMap.get(agentId);
7833
+ if (!list) {
7834
+ list = [];
7835
+ teamMap.set(agentId, list);
7836
+ }
7837
+ return list;
7838
+ }
7839
+ /** Generate a unique message ID. */
7840
+ nextId() {
7841
+ this.idCounter += 1;
7842
+ return `msg-${this.idCounter}`;
7843
+ }
7844
+ /** Store a message and emit a real-time event for the recipient. */
7845
+ storeAndNotify(teamId, message) {
7846
+ const list = this.getAgentMessages(teamId, message.to);
7847
+ list.push(message);
7848
+ const preview = message.content.length > 80 ? message.content.slice(0, 80) + "..." : message.content;
7849
+ console.log(
7850
+ `[Mailbox] ${teamId} | ${message.from} \u2192 ${message.to} [${message.type}] ${message.id}: ${preview}`
7851
+ );
7852
+ this.emitter.emit(`${teamId}:${message.to}:message`, message);
7853
+ this.emitter.emit(`${teamId}:message`, message);
7854
+ }
7855
+ // -------------------------------------------------------------------------
7856
+ // Messaging
7857
+ // -------------------------------------------------------------------------
7858
+ async sendMessage(teamId, from, to, content, type = "direct_message" /* DIRECT_MESSAGE */) {
7859
+ const message = {
7860
+ id: this.nextId(),
7861
+ from,
7862
+ to,
7863
+ content,
7864
+ timestamp: /* @__PURE__ */ new Date(),
7865
+ type,
7866
+ read: false
7867
+ };
7868
+ this.storeAndNotify(teamId, message);
7869
+ return message;
7870
+ }
7871
+ async broadcastMessage(teamId, from, content, type = "broadcast" /* BROADCAST */) {
7872
+ const agentSet = this.agents.get(teamId);
7873
+ if (!agentSet || agentSet.size === 0) return [];
7874
+ const created = [];
7875
+ for (const agentId of agentSet) {
7876
+ if (agentId === from) continue;
7877
+ const message = {
7878
+ id: this.nextId(),
7879
+ from,
7880
+ to: agentId,
7881
+ content,
7882
+ timestamp: /* @__PURE__ */ new Date(),
7883
+ type,
7884
+ read: false
7885
+ };
7886
+ this.storeAndNotify(teamId, message);
7887
+ created.push(message);
7888
+ }
7889
+ return created;
7890
+ }
7891
+ async getMessages(teamId, agentId) {
7892
+ const teamMap = this.messages.get(teamId);
7893
+ if (!teamMap) return [];
7894
+ return teamMap.get(agentId) ?? [];
7895
+ }
7896
+ async getUnreadMessages(teamId, agentId) {
7897
+ const all = await this.getMessages(teamId, agentId);
7898
+ return all.filter((m) => !m.read);
7899
+ }
7900
+ async markAsRead(teamId, agentId, messageId) {
7901
+ const all = await this.getMessages(teamId, agentId);
7902
+ const msg = all.find((m) => m.id === messageId);
7903
+ if (msg) {
7904
+ msg.read = true;
7905
+ console.log(
7906
+ `[Mailbox] ${teamId} | ${agentId} marked ${messageId} as read (from: ${msg.from})`
7907
+ );
7908
+ }
7909
+ }
7910
+ // -------------------------------------------------------------------------
7911
+ // Agent registration
7912
+ // -------------------------------------------------------------------------
7913
+ async registerAgent(teamId, agentId) {
7914
+ let agentSet = this.agents.get(teamId);
7915
+ if (!agentSet) {
7916
+ agentSet = /* @__PURE__ */ new Set();
7917
+ this.agents.set(teamId, agentSet);
7918
+ }
7919
+ agentSet.add(agentId);
7920
+ }
7921
+ async unregisterAgent(teamId, agentId) {
7922
+ const agentSet = this.agents.get(teamId);
7923
+ if (agentSet) {
7924
+ agentSet.delete(agentId);
7925
+ }
7926
+ }
7927
+ async getRegisteredAgents(teamId) {
7928
+ const agentSet = this.agents.get(teamId);
7929
+ if (!agentSet) return [];
7930
+ return Array.from(agentSet);
7931
+ }
7932
+ // -------------------------------------------------------------------------
7933
+ // Events
7934
+ // -------------------------------------------------------------------------
7935
+ onMessage(teamId, agentId, callback) {
7936
+ this.emitter.on(`${teamId}:${agentId}:message`, callback);
7937
+ }
7938
+ offMessage(teamId, agentId, callback) {
7939
+ this.emitter.off(`${teamId}:${agentId}:message`, callback);
7940
+ }
7941
+ onTeamMessage(teamId, callback) {
7942
+ this.emitter.on(`${teamId}:message`, callback);
7943
+ }
7944
+ offTeamMessage(teamId, callback) {
7945
+ this.emitter.off(`${teamId}:message`, callback);
7946
+ }
7947
+ async getAllTeamMessages(teamId) {
7948
+ const teamMap = this.messages.get(teamId);
7949
+ if (!teamMap) return [];
7950
+ const allMessages = [];
7951
+ for (const messages of teamMap.values()) {
7952
+ allMessages.push(...messages);
7953
+ }
7954
+ return allMessages.sort(
7955
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
7956
+ );
7957
+ }
7958
+ // -------------------------------------------------------------------------
7959
+ // Utilities
7960
+ // -------------------------------------------------------------------------
7961
+ /** Remove all data for a team (messages + registrations). */
7962
+ clearTeam(teamId) {
7963
+ this.messages.delete(teamId);
7964
+ this.agents.delete(teamId);
7965
+ }
7966
+ /** Remove all data across all teams. */
7967
+ clear() {
7968
+ this.messages.clear();
7969
+ this.agents.clear();
7970
+ }
7971
+ };
5745
7972
 
5746
- 2. **Task Management**:
5747
- - Update task status in real-time as you work
5748
- - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
5749
- - Complete current tasks before starting new ones
5750
- - Remove tasks that are no longer relevant from the list entirely
5751
- - IMPORTANT: When you write this todo list, you should mark your first task (or tasks) as in_progress immediately!.
5752
- - IMPORTANT: Unless all tasks are completed, you should always have at least one task in_progress to show the user that you are working on something.
7973
+ // src/agent_team/middleware/team.ts
7974
+ var import_v34 = require("zod/v3");
7975
+ var import_langchain42 = require("langchain");
7976
+ var import_langgraph10 = require("@langchain/langgraph");
7977
+ var import_uuid = require("uuid");
7978
+
7979
+ // src/agent_team/middleware/teammate_tools.ts
7980
+ var import_v33 = require("zod/v3");
7981
+ var import_langchain41 = require("langchain");
7982
+ var import_langgraph9 = require("@langchain/langgraph");
7983
+
7984
+ // src/agent_team/middleware/formatMessages.ts
7985
+ function formatMessagesAsMarkdown(msgs) {
7986
+ if (msgs.length === 0) return "No unread messages.";
7987
+ const header = `## Unread messages (${msgs.length})
7988
+ `;
7989
+ const sections = msgs.map((m, i) => {
7990
+ const title = `### ${i + 1}. From: **${m.from}**`;
7991
+ const meta = `- **Type:** \`${m.type}\`
7992
+ - **Time:** ${m.timestamp.toISOString()}`;
7993
+ const body = m.content.trim() ? `
7994
+
7995
+ ${m.content.trim()}` : "";
7996
+ return `---
7997
+ ${title}
7998
+ ${meta}${body}`;
7999
+ });
8000
+ return header + sections.join("\n\n");
8001
+ }
5753
8002
 
5754
- 3. **Task Completion Requirements**:
5755
- - ONLY mark a task as completed when you have FULLY accomplished it
5756
- - If you encounter errors, blockers, or cannot finish, keep the task as in_progress
5757
- - When blocked, create a new task describing what needs to be resolved
5758
- - Never mark a task as completed if:
5759
- - There are unresolved issues or errors
5760
- - Work is partial or incomplete
5761
- - You encountered blockers that prevent completion
5762
- - You couldn't find necessary resources or dependencies
5763
- - Quality standards haven't been met
8003
+ // src/agent_team/middleware/teammate_tools.ts
8004
+ function createTeammateTools(options) {
8005
+ const { teamId, agentId, taskListStore, mailboxStore } = options;
8006
+ const claimTaskTool = (0, import_langchain41.tool)(
8007
+ async (input) => {
8008
+ const task = await taskListStore.claimTaskById(
8009
+ teamId,
8010
+ input.task_id,
8011
+ agentId
8012
+ );
8013
+ if (!task) {
8014
+ return `Could not claim task "${input.task_id}". Check that it exists, is PENDING, and all its dependencies are completed. Use check_tasks to see available tasks and their status.`;
8015
+ }
8016
+ return `Claimed task ${task.id} "${task.title}". Focus on this task now. When done, call complete_task or fail_task with task_id "${task.id}".
8017
+ ` + JSON.stringify(
8018
+ {
8019
+ task_id: task.id,
8020
+ title: task.title,
8021
+ description: task.description,
8022
+ dependencies: task.dependencies
8023
+ },
8024
+ null,
8025
+ 2
8026
+ );
8027
+ },
8028
+ {
8029
+ name: "claim_task",
8030
+ description: "Pick a task to work on by task_id. Use check_tasks first to see all tasks; then call this with the task_id you choose. The task's assignee is set to you and you should focus on that task until you complete_task or fail_task it.",
8031
+ schema: import_v33.z.object({
8032
+ task_id: import_v33.z.string().describe("ID of the task to claim (e.g. task-01). Use check_tasks to see IDs.")
8033
+ })
8034
+ }
8035
+ );
8036
+ const completeTaskTool = (0, import_langchain41.tool)(
8037
+ async (input) => {
8038
+ const task = await taskListStore.completeTask(
8039
+ teamId,
8040
+ input.task_id,
8041
+ input.result
8042
+ );
8043
+ if (!task) {
8044
+ return `Task ${input.task_id} not found.`;
8045
+ }
8046
+ await mailboxStore.broadcastMessage(
8047
+ teamId,
8048
+ agentId,
8049
+ `Completed task ${task.id} "${task.title}": ${input.result}`,
8050
+ "broadcast" /* BROADCAST */
8051
+ );
8052
+ return `Task ${task.id} "${task.title}" marked as completed.`;
8053
+ },
8054
+ {
8055
+ name: "complete_task",
8056
+ description: "Mark a claimed task as completed with a result summary. Call this after you have finished working on a task.",
8057
+ schema: import_v33.z.object({
8058
+ task_id: import_v33.z.string().describe("ID of the task to complete"),
8059
+ result: import_v33.z.string().describe("Summary of the task result")
8060
+ })
8061
+ }
8062
+ );
8063
+ const failTaskTool = (0, import_langchain41.tool)(
8064
+ async (input) => {
8065
+ const task = await taskListStore.failTask(
8066
+ teamId,
8067
+ input.task_id,
8068
+ input.error
8069
+ );
8070
+ if (!task) {
8071
+ return `Task ${input.task_id} not found.`;
8072
+ }
8073
+ await mailboxStore.broadcastMessage(
8074
+ teamId,
8075
+ agentId,
8076
+ `Failed task ${task.id} "${task.title}": ${input.error}`,
8077
+ "broadcast" /* BROADCAST */
8078
+ );
8079
+ return `Task ${task.id} "${task.title}" marked as failed: ${input.error}`;
8080
+ },
8081
+ {
8082
+ name: "fail_task",
8083
+ description: "Mark a claimed task as failed with an error description. Call this if you cannot complete the task.",
8084
+ schema: import_v33.z.object({
8085
+ task_id: import_v33.z.string().describe("ID of the task to fail"),
8086
+ error: import_v33.z.string().describe("Description of why the task failed")
8087
+ })
8088
+ }
8089
+ );
8090
+ const sendMessageTool = (0, import_langchain41.tool)(
8091
+ async (input) => {
8092
+ await mailboxStore.sendMessage(
8093
+ teamId,
8094
+ agentId,
8095
+ input.to,
8096
+ input.content,
8097
+ "direct_message" /* DIRECT_MESSAGE */
8098
+ );
8099
+ return `Message sent to ${input.to}.`;
8100
+ },
8101
+ {
8102
+ name: "send_message",
8103
+ description: 'Send a message to the team lead or another teammate via the mailbox. Use "team_lead" to message the team lead. Use this to report discoveries, request guidance, or suggest new tasks.',
8104
+ schema: import_v33.z.object({
8105
+ to: import_v33.z.string().describe(
8106
+ 'Recipient agent name (e.g. "team_lead" or a teammate name)'
8107
+ ),
8108
+ content: import_v33.z.string().describe("Message content")
8109
+ })
8110
+ }
8111
+ );
8112
+ const READ_MESSAGES_TIMEOUT_MS2 = 18e4;
8113
+ const getRelevantMessagesForState = async () => {
8114
+ const allMsgs = await mailboxStore.getAllTeamMessages(teamId);
8115
+ const relevantMsgs = allMsgs.filter(
8116
+ (msg) => msg.from === agentId || msg.to === agentId
8117
+ );
8118
+ return relevantMsgs.map((msg) => ({
8119
+ id: msg.id,
8120
+ from: msg.from,
8121
+ to: msg.to,
8122
+ content: msg.content,
8123
+ timestamp: msg.timestamp instanceof Date ? msg.timestamp.toISOString() : msg.timestamp,
8124
+ type: msg.type,
8125
+ read: msg.read
8126
+ }));
8127
+ };
8128
+ const readMessagesTool = (0, import_langchain41.tool)(
8129
+ async (input, config) => {
8130
+ const formatAndMarkAsRead = async (msgs2) => {
8131
+ for (const msg of msgs2) {
8132
+ await mailboxStore.markAsRead(teamId, agentId, msg.id);
8133
+ }
8134
+ return formatMessagesAsMarkdown(msgs2);
8135
+ };
8136
+ let msgs = await mailboxStore.getUnreadMessages(teamId, agentId);
8137
+ if (msgs.length > 0) {
8138
+ const formatted2 = await formatAndMarkAsRead(msgs);
8139
+ const relevantMsgs2 = await getRelevantMessagesForState();
8140
+ const toolMessage2 = new import_langchain41.ToolMessage({
8141
+ content: formatted2,
8142
+ tool_call_id: config.toolCall?.id,
8143
+ name: "read_messages"
8144
+ });
8145
+ return new import_langgraph9.Command({
8146
+ update: { team_mailbox: relevantMsgs2, messages: [toolMessage2] }
8147
+ });
8148
+ }
8149
+ const messagePromise = new Promise((resolve3) => {
8150
+ const handler = async (msg) => {
8151
+ mailboxStore.offMessage(teamId, agentId, handler);
8152
+ const allMsgs = await mailboxStore.getUnreadMessages(teamId, agentId);
8153
+ resolve3(allMsgs);
8154
+ };
8155
+ mailboxStore.onMessage(teamId, agentId, handler);
8156
+ });
8157
+ const timeoutPromise = new Promise((resolve3) => {
8158
+ setTimeout(() => resolve3([]), READ_MESSAGES_TIMEOUT_MS2);
8159
+ });
8160
+ msgs = await Promise.race([messagePromise, timeoutPromise]);
8161
+ mailboxStore.offMessage(teamId, agentId, () => {
8162
+ });
8163
+ const relevantMsgs = await getRelevantMessagesForState();
8164
+ if (msgs.length === 0) {
8165
+ const toolMessage2 = new import_langchain41.ToolMessage({
8166
+ content: "No unread messages.",
8167
+ tool_call_id: config.toolCall?.id,
8168
+ name: "read_messages"
8169
+ });
8170
+ return new import_langgraph9.Command({
8171
+ update: { team_mailbox: relevantMsgs, messages: [toolMessage2] }
8172
+ });
8173
+ }
8174
+ const formatted = await formatAndMarkAsRead(msgs);
8175
+ const toolMessage = new import_langchain41.ToolMessage({
8176
+ content: formatted,
8177
+ tool_call_id: config.toolCall?.id,
8178
+ name: "read_messages"
8179
+ });
8180
+ return new import_langgraph9.Command({
8181
+ update: { team_mailbox: relevantMsgs, messages: [toolMessage] }
8182
+ });
8183
+ },
8184
+ {
8185
+ name: "read_messages",
8186
+ description: "Read unread messages from the mailbox. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
8187
+ schema: import_v33.z.object({})
8188
+ }
8189
+ );
8190
+ const checkTasksTool = (0, import_langchain41.tool)(
8191
+ async () => {
8192
+ const tasks = await taskListStore.getAllTasks(teamId);
8193
+ return formatTaskSummary(tasks);
8194
+ },
8195
+ {
8196
+ name: "check_tasks",
8197
+ description: "Use this tool to get the current status of all tasks in a team. This is your primary way to monitor task progress.",
8198
+ schema: import_v33.z.object({})
8199
+ }
8200
+ );
8201
+ const broadcastMessageTool = (0, import_langchain41.tool)(
8202
+ async (input) => {
8203
+ const allAgents = await mailboxStore.getRegisteredAgents(teamId);
8204
+ const recipients = allAgents.filter((a) => a !== agentId);
8205
+ const promises = recipients.map(
8206
+ (recipient) => mailboxStore.sendMessage(
8207
+ teamId,
8208
+ agentId,
8209
+ recipient,
8210
+ input.content,
8211
+ "broadcast" /* BROADCAST */
8212
+ )
8213
+ );
8214
+ await Promise.all(promises);
8215
+ return `Broadcast message sent to ${recipients.length} recipient(s): ${recipients.join(", ")}`;
8216
+ },
8217
+ {
8218
+ name: "broadcast_message",
8219
+ description: "Send a message to everyone in the team except yourself. Use this to share updates or information with all teammates and the team lead at once.",
8220
+ schema: import_v33.z.object({
8221
+ content: import_v33.z.string().describe("Message content to broadcast to others")
8222
+ })
8223
+ }
8224
+ );
8225
+ return [
8226
+ claimTaskTool,
8227
+ completeTaskTool,
8228
+ failTaskTool,
8229
+ sendMessageTool,
8230
+ readMessagesTool,
8231
+ checkTasksTool,
8232
+ broadcastMessageTool
8233
+ ];
8234
+ }
5764
8235
 
5765
- 4. **Task Breakdown**:
5766
- - Create specific, actionable items
5767
- - Break complex tasks into smaller, manageable steps
5768
- - Use clear, descriptive task names
8236
+ // src/agent_team/middleware/team.ts
8237
+ var TEAM_LEAD_AGENT_ID = "team_lead";
8238
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
8239
+ var DEFAULT_SCHEDULE_LATTICE_KEY = "default";
8240
+ var READ_MESSAGES_TIMEOUT_MS = 18e4;
8241
+ var TEAM_COMMUNICATION_NORMS = `
8242
+ ### Team communication norms
8243
+
8244
+ - **Roles**: One \`team_lead\` coordinates the work; all others are teammates. Teammates report to team_lead and collaborate with each other as needed.
8245
+ - **Notifying everyone**: When the team_lead needs to tell something to **all** teammates (e.g. "submit your plan", "plan approved", "shut down"), use \`broadcast_message\`. Do not send the same message to each teammate individually.
8246
+ - **Teammate \u2192 all**: When a teammate needs to share something with the whole team, use \`broadcast_message\`.
8247
+ - **One-to-one**: Use \`send_message\` for directed messages (e.g. teammate sending a plan to team_lead, or team_lead replying to one teammate).
8248
+ - **New tasks**: If a teammate needs **new tasks** to be created (e.g. discovered during work or scope change), they must notify the team_lead via \`send_message\` to "team_lead". Only the team_lead may add tasks via \`add_tasks\`; teammates do not create tasks themselves.
8249
+ - **Plans**: Teammates submit their execution plan to team_lead via \`send_message\` to "team_lead". Team_lead reviews and approves; no external user approval is required. Team_lead notifies all teammates of approval via \`broadcast_message\`.
8250
+ - **Shutdown \u2014 check task state first**: When anyone receives a shutdown message (from team_lead or system), they **must not** shut down immediately. First: (1) **Check current task state** via \`check_tasks\` (team_lead) or \`check_tasks\` (teammate). (2) **Discuss with the team** via \`read_messages\`, \`send_message\`, or \`broadcast_message\`: should we continue these tasks, or stop/cancel them? (3) Only after the team has agreed\u2014either to **continue** (keep working) or to **stop/delete** the remaining tasks\u2014may shutdown proceed. Team_lead must not call \`disband_team\` until task state is verified and the team has aligned; teammates must not exit until the same alignment is reached.`;
8251
+ var TEAM_SYSTEM_PROMPT = `## Agent Team
8252
+
8253
+ You are the team_lead. You have tools to manage a team of specialized agents that work in parallel:
8254
+
8255
+ - \`create_team\`: Create a team with initial tasks and teammates. This is **non-blocking** -- teammates start working in the background immediately, and you stay free to continue the conversation.
8256
+ - \`add_tasks\`: Add new tasks at any time. Use the optional \`assignee\` field to assign a task to a specific teammate when you need that person to do the work; otherwise teammates will claim tasks. Sleeping teammates will wake up and claim new tasks.
8257
+ - \`assign_task\`: Set a task's assignee (reassign work to a specific teammate).
8258
+ - \`set_task_status\`: Set a task's status (pending, claimed, in_progress, completed, failed). Use to reopen a task or mark it failed.
8259
+ - \`set_task_dependencies\`: Set which task IDs must complete before this task can be claimed.
8260
+ - \`check_tasks\`: See current status of all tasks (pending, in_progress, completed, failed). Call it when \`read_messages\` indicates task changes to get full details.
8261
+ - \`send_message\`: Send a message to a specific teammate (one-to-one).
8262
+ - \`read_messages\`: Read unread messages from teammates (e.g. status updates, plans, requests). Returns immediately if messages exist, otherwise waits up to 3 minutes for new messages.
8263
+ - \`broadcast_message\`: Send a message to **all** teammates at once. Use this whenever you need to notify everyone (e.g. "submit your plan", "plan approved", "shut down"). Do not send the same message to each teammate individually.
8264
+ - \`disband_team\`: Disband the team when all work is done. This notifies all teammates and cleans up resources.
8265
+ ${TEAM_COMMUNICATION_NORMS}
8266
+
8267
+ ### Workflow
8268
+
8269
+ 1. Analyze the user's request and plan the initial tasks + which teammates to assign.
8270
+ 2. Call \`create_team\` to create the team with tasks.
8271
+ 3. **Request execution plans**: After creating the team, use \`broadcast_message\` once to tell all teammates: "Please submit your execution plan for review."
8272
+ 4. **Wait for all plans**: Use \`read_messages\` until ALL teammates have submitted their plans. Do not proceed until every teammate has submitted.
8273
+ 5. **Add plans to team tasks and assign to teammates (required)**:
8274
+ - Once you have collected execution plans from **all** teammates, you **must** add them to the team task list via \`add_tasks\`. Do not skip this step.
8275
+ - For each teammate's plan (or each step from an integrated plan), create a task with a unique \`id\` (e.g. task-01, task-02). Set \`assignee\` to the **corresponding teammate** who will execute that plan or that task (the teammate who proposed it, or the one you assign for that work).
8276
+ - If you **integrate or synthesize** multiple plans into one coherent plan, decompose it into concrete tasks and call \`add_tasks\` with each task's \`assignee\` set to the appropriate teammate. Use \`dependencies\` if task order matters.
8277
+ - If plans are acceptable as-is, still call \`add_tasks\` to add each teammate's execution plan as one or more tasks, with \`assignee\` set to that teammate. Then use \`broadcast_message\` (e.g. "Plan approved. Task list has been updated. You may now start working.").
8278
+ - If you need changes, \`send_message\` to the relevant teammate(s) with feedback and wait for revised plans; only after receiving revised plans, add tasks and assignees as above, then \`broadcast_message\` approval.
8279
+ 6. **Monitor execution**: Once approved, teammates will start working. Periodically call \`check_tasks\` and \`read_messages\` to monitor progress.
8280
+ 7. **When teammates request new tasks**: If a teammate notifies you (via \`read_messages\`) that they need new tasks, use \`add_tasks\` to add the tasks, then use \`broadcast_message\` to notify the team that new tasks have been added (e.g. "New tasks have been added to the list. Check with check_tasks and claim as needed."). Re-run plan approval only if the new tasks change the overall plan.
8281
+ 8. **When you need a specific teammate to do something**: Create a new task with \`add_tasks\` and set \`assignee\` to that teammate's name; optionally \`send_message\` to that teammate. To reassign an existing task use \`assign_task\`; to change dependencies use \`set_task_dependencies\`; to reopen or mark failed use \`set_task_status\`.
8282
+ 9. Report status to the user based on \`check_tasks\` results. Proceed to final synthesis when all tasks are done.
8283
+ 10. **Cleanup \u2014 only after verifying task state and team alignment**: Before shutting down the team:
8284
+ - Call \`check_tasks\` to get the current task state. If any tasks are still **pending**, **claimed**, or **in_progress**, do **not** broadcast shutdown yet.
8285
+ - Use \`read_messages\` and, if needed, \`send_message\` or \`broadcast_message\` to discuss with the team: should we continue these tasks or stop/cancel them?
8286
+ - If the team agrees to continue: let them work until tasks are done, then re-check with \`check_tasks\`.
8287
+ - If the team agrees to stop: use \`set_task_status\` to mark remaining tasks as **failed** or **completed** (with a note) as appropriate, then call \`check_tasks\` again to confirm.
8288
+ - Only when all tasks are in a terminal state (completed/failed) and the team has aligned, use \`broadcast_message\` to notify shutdown, then call \`disband_team\`.
8289
+
8290
+ ### Important Notes
8291
+
8292
+ - The teammates you specify in \`create_team\` are created dynamically from name, role, and description.
8293
+ - Tasks can have dependencies -- a task won't be claimable until all its dependency tasks are completed.
8294
+ - You can add tasks and communicate with teammates at any time, even while they are working.
8295
+ - **Assigning work to a teammate**: When you need a specific teammate to do something, create a new task with \`add_tasks\` and set \`assignee\`, or use \`assign_task\` to reassign an existing task. Use \`set_task_dependencies\` to change task order; use \`set_task_status\` to reopen (pending) or mark failed.
8296
+ - **New task requests**: Teammates may request new tasks via \`send_message\`. When you receive such a request, use \`add_tasks\` then notify the team via \`broadcast_message\`.
8297
+ - **Plan approval and task sync**: After collecting all teammates' execution plans, you **must** add them to the team task list via \`add_tasks\` and set \`assignee\` for each task to the corresponding teammate. Then \`broadcast_message\` to approve (e.g. "Plan approved. Task list has been updated. You may now start working."). Do not let teammates start working until you have added tasks with assignees and broadcast approval. You review and approve; no user confirmation is required.
8298
+ - **Permissions**: All teammates inherit your permission settings.
8299
+ - **Shutdown**: Only you may tell the team to shut down. Before doing so: (1) call \`check_tasks\` to verify task state; (2) if any tasks are still in progress, discuss with the team via \`read_messages\` and \`broadcast_message\`/\`send_message\` whether to continue or stop them; (3) only after aligning (continue until done, or stop/cancel tasks), use \`broadcast_message\` then \`disband_team\`. Do not disband while tasks are still pending/in_progress without team agreement.`;
8300
+ function formatTaskSummary(tasks) {
8301
+ if (tasks.length === 0) return "No tasks in the task list.";
8302
+ const statusCounts = {};
8303
+ for (const t of tasks) {
8304
+ statusCounts[t.status] = (statusCounts[t.status] || 0) + 1;
8305
+ }
8306
+ const taskBlocks = tasks.map((t) => {
8307
+ let block = `## ${t.id}
8308
+ `;
8309
+ block += `- **Status**: ${t.status.toUpperCase()}
8310
+ `;
8311
+ block += `- **Title**: ${t.title}
8312
+ `;
8313
+ if (t.description) block += `- **Description**: ${t.description}
8314
+ `;
8315
+ if (t.assignee) block += `- **Assignee**: ${t.assignee}
8316
+ `;
8317
+ if (t.dependencies.length > 0) block += `- **Dependencies**: ${t.dependencies.join(", ")}
8318
+ `;
8319
+ if (t.result) block += `- **Result**: ${t.result.slice(0, 500)}
8320
+ `;
8321
+ if (t.error) block += `- **Error**: ${t.error.slice(0, 500)}
8322
+ `;
8323
+ return block;
8324
+ });
8325
+ const summary = Object.entries(statusCounts).map(([s, c]) => `- ${s}: ${c}`).join(" | ");
8326
+ return `${taskBlocks.join("\n---\n\n")}
5769
8327
 
5770
- Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully
5771
- Remember: If you only need to make a few tool calls to complete a task, and it is clear what you need to do, it is better to just do the task directly and NOT call this tool at all.`;
5772
- var TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT = `## \`write_todos\`
8328
+ ## Summary
8329
+ ${summary}`;
8330
+ }
8331
+ function getTeammateAssistantId(teamId, agentName) {
8332
+ return `team:${teamId}:${agentName}`;
8333
+ }
8334
+ async function getOrCreateTeammateAgent(ctx, spec, teamTools, teamMiddleware) {
8335
+ const cacheKey = `${ctx.teamId}:${spec.name}`;
8336
+ let agent = ctx.agentCache.get(cacheKey);
8337
+ if (agent) return agent;
8338
+ const builtinTeammateTools = createTeammateTools({
8339
+ teamId: ctx.teamId,
8340
+ agentId: spec.name,
8341
+ taskListStore: ctx.taskListStore,
8342
+ mailboxStore: ctx.mailboxStore
8343
+ });
8344
+ const allTools = [...teamTools ?? [], ...spec.tools ?? [], ...builtinTeammateTools];
8345
+ const others = (ctx.allTeammateSpecs ?? []).filter((s) => s.name !== spec.name).map((s) => `${s.name} (role: ${s.role}${s.description ? `; ${s.description}` : ""})`);
8346
+ const teammatesBlock = others.length > 0 ? `
8347
+
8348
+ Your teammates in this team (you can \`send_message\` to them):
8349
+ - team_lead (lead)
8350
+ ${others.map((o) => `- ${o}`).join("\n")}
8351
+ ` : "\n\nYour teammates in this team: team_lead (lead).\n";
8352
+ const teammatePrompt = spec.systemPrompt ?? `You are a team member. Your name in this team is "${spec.name}" and your role is "${spec.role}". You work independently with your own context window.${teammatesBlock}
8353
+ ${TEAM_COMMUNICATION_NORMS}
8354
+
8355
+ Your job:
8356
+ 1. First, call \`check_tasks\` to see all tasks in the team.
8357
+ 2. Submit your execution plan: Use \`send_message\` to send your plan to "team_lead". Explain how you will approach the task, what steps you will take, and any dependencies or considerations.
8358
+ 3. **Wait for approval**: Do NOT start working immediately. Wait for the team_lead to approve your plan via \`read_messages\`. The lead will notify the whole team (e.g. by broadcast) with a message like "Plan approved. You may now start working." Only then may you start.
8359
+ 4. Only after receiving approval: Use \`check_tasks\` to see available tasks, then call \`claim_task\` with the \`task_id\` you choose. That sets the task's assignee to you; focus on that task until done.
8360
+ 5. Use your tools to complete the task thoroughly.
8361
+ 6. Call \`complete_task\` with a summary of your result, or \`fail_task\` if you cannot complete it.
8362
+ 7. After completing a task, call \`claim_task\` again with another \`task_id\` to get the next task (only PENDING tasks whose dependencies are done can be claimed).
8363
+ 8. **If you need new tasks**: If during work you discover that new tasks are needed (e.g. scope change, follow-up work), notify the team_lead via \`send_message\` to "team_lead". Describe what new tasks are needed; do not create tasks yourself. Only the team_lead adds tasks; wait for the lead to add them and broadcast before claiming new work.
8364
+ 9. When no tasks are left to claim, call \`read_messages\` to wait for messages (it waits up to 3 minutes for new messages). Continue until you receive a shutdown message from the team_lead.
8365
+ 10. **Shutdown \u2014 check task state and discuss first**: When you receive a shutdown message (e.g. "All tasks are complete, you may now shut down"):
8366
+ - **Do NOT shut down immediately.** First call \`check_tasks\` to check the current task state.
8367
+ - If there are still tasks in **pending**, **claimed**, or **in_progress**: use \`send_message\` to "team_lead" or \`broadcast_message\` to the team to discuss: "I see tasks still in progress / pending. Should we continue these or stop/cancel them before shutdown?"
8368
+ - Wait for team alignment (from \`read_messages\`): either (a) continue working until tasks are done, or (b) team_lead confirms tasks are stopped/cancelled.
8369
+ - Only after the team has agreed and either all your assigned work is done or tasks are explicitly stopped, then exit gracefully. Do not shut down on your own without checking task state and team agreement.
8370
+
8371
+ Important workflow:
8372
+ - NEVER start working on tasks before the team_lead has approved (you will see "Plan approved" or similar in \`read_messages\`)
8373
+ - Always submit your plan first via \`send_message\` to "team_lead"
8374
+ - If you need new tasks, notify team_lead via \`send_message\` to "team_lead"; only the lead adds tasks
8375
+ - If you receive feedback on your plan, revise and resubmit to team_lead
8376
+
8377
+ You have access to these tools:
8378
+ - \`claim_task\`: Pick a task by task_id to claim it (sets assignee to you; ONLY after plan is approved). Use check_tasks first.
8379
+ - \`complete_task\`: Mark a task as completed
8380
+ - \`fail_task\`: Mark a task as failed
8381
+ - \`send_message\`: Send a message to team_lead or a specific teammate
8382
+ - \`broadcast_message\`: Send a message to everyone in the team except yourself (use when sharing with the whole team)
8383
+ - \`read_messages\`: Read messages from team_lead or teammates
8384
+ - \`check_tasks\`: Get current status of all tasks in the team`;
8385
+ const assistantId = getTeammateAssistantId(ctx.teamId, spec.name);
8386
+ agent = (0, import_langchain42.createAgent)({
8387
+ model: spec.model ?? ctx.defaultModel,
8388
+ systemPrompt: teammatePrompt,
8389
+ tools: allTools,
8390
+ description: spec.description,
8391
+ middleware: [...teamMiddleware ?? [], ...spec.middleware ?? []],
8392
+ checkpointer: getCheckpointSaver("default")
8393
+ });
8394
+ registerTeammateAgent(assistantId, agent);
8395
+ const teammateThreadId = ctx.parentThreadId ? `${ctx.parentThreadId}____${ctx.teamId}_${spec.name}` : `standalone_${ctx.teamId}_${spec.name}`;
8396
+ const startMessage = "Start working";
8397
+ try {
8398
+ await agentWorkerGraph.invoke({
8399
+ assistant_id: assistantId,
8400
+ thread_id: teammateThreadId,
8401
+ input: { message: startMessage, messages: [{ role: "human", content: startMessage }] }
8402
+ });
8403
+ } catch (err) {
8404
+ const errorMsg = err instanceof Error ? err.message : "Unknown error during teammate execution";
8405
+ await ctx.mailboxStore.sendMessage(
8406
+ ctx.teamId,
8407
+ spec.name,
8408
+ TEAM_LEAD_AGENT_ID,
8409
+ `Teammate execution failed: ${errorMsg}`,
8410
+ "status_update" /* STATUS_UPDATE */
8411
+ );
8412
+ }
8413
+ return agent;
8414
+ }
8415
+ async function spawnTeammate(options) {
8416
+ const {
8417
+ teamId,
8418
+ spec,
8419
+ allTeammateSpecs,
8420
+ taskListStore,
8421
+ mailboxStore,
8422
+ defaultModel,
8423
+ parentThreadId = "",
8424
+ teamTools,
8425
+ teamMiddleware
8426
+ } = options;
8427
+ const ctx = {
8428
+ teamId,
8429
+ parentThreadId,
8430
+ taskListStore,
8431
+ mailboxStore,
8432
+ teammates: /* @__PURE__ */ new Map(),
8433
+ allTeammateSpecs,
8434
+ defaultModel,
8435
+ pollIntervalMs: DEFAULT_POLL_INTERVAL_MS,
8436
+ scheduleLatticeKey: DEFAULT_SCHEDULE_LATTICE_KEY,
8437
+ agentCache: /* @__PURE__ */ new Map()
8438
+ };
8439
+ await getOrCreateTeammateAgent(ctx, spec, teamTools, teamMiddleware);
8440
+ }
8441
+ function createTeamMiddleware(options) {
8442
+ const { teamConfig, taskListStore, mailboxStore } = options;
8443
+ const defaultModel = teamConfig.model ?? "claude-sonnet-4-5-20250929";
8444
+ const createTeamTool = (0, import_langchain42.tool)(
8445
+ async (input, config) => {
8446
+ const state = (0, import_langgraph10.getCurrentTaskInput)();
8447
+ if (state?.team?.teamId) {
8448
+ const existingId = state.team.teamId;
8449
+ const msg = new import_langchain42.ToolMessage({
8450
+ content: `A team is already active (id: ${existingId}). Use this team_id for \`check_tasks\`, \`read_messages\`, \`add_tasks\`, \`send_message\`, \`assign_task\`, \`set_task_status\`, and \`set_task_dependencies\`. Do not call \`create_team\` again unless you need a fresh team for a new objective.`,
8451
+ tool_call_id: config.toolCall?.id,
8452
+ name: "create_team"
8453
+ });
8454
+ return msg;
8455
+ }
8456
+ const teamId = (0, import_uuid.v4)();
8457
+ const createdTasks = await taskListStore.addTasks(
8458
+ teamId,
8459
+ input.tasks.map((t) => ({
8460
+ id: t.id,
8461
+ title: t.title,
8462
+ description: t.description,
8463
+ assignee: t.assignee,
8464
+ dependencies: t.dependencies ?? []
8465
+ }))
8466
+ );
8467
+ const matchedSpecs = [];
8468
+ const unmatchedNames = [];
8469
+ for (const req of input.teammates) {
8470
+ const spec = teamConfig.teammates.find(
8471
+ (s) => s.name === req.name || s.role === req.role
8472
+ );
8473
+ if (spec) {
8474
+ matchedSpecs.push(spec);
8475
+ } else {
8476
+ matchedSpecs.push({
8477
+ name: req.name,
8478
+ role: req.role,
8479
+ description: req.description
8480
+ });
8481
+ unmatchedNames.push(req.name);
8482
+ }
8483
+ }
8484
+ await mailboxStore.registerAgent(teamId, TEAM_LEAD_AGENT_ID);
8485
+ for (const spec of matchedSpecs) {
8486
+ await mailboxStore.registerAgent(teamId, spec.name);
8487
+ }
8488
+ const parentThreadId = config.configurable?.thread_id ?? "";
8489
+ matchedSpecs.forEach((spec) => {
8490
+ void spawnTeammate({
8491
+ teamId,
8492
+ spec,
8493
+ allTeammateSpecs: matchedSpecs,
8494
+ taskListStore,
8495
+ mailboxStore,
8496
+ defaultModel,
8497
+ parentThreadId,
8498
+ teamTools: teamConfig.tools,
8499
+ teamMiddleware: teamConfig.middleware
8500
+ });
8501
+ });
8502
+ const teamJson = JSON.stringify({
8503
+ teamId,
8504
+ teamLeadId: TEAM_LEAD_AGENT_ID,
8505
+ teammates: matchedSpecs.map((s) => ({
8506
+ name: s.name,
8507
+ role: s.role,
8508
+ description: s.description
8509
+ })),
8510
+ tasks: createdTasks.map((t) => ({
8511
+ id: t.id,
8512
+ title: t.title,
8513
+ description: t.description,
8514
+ status: t.status
8515
+ }))
8516
+ }, null, 2);
8517
+ let summary = `Team created (id: ${teamId}):
8518
+ `;
8519
+ summary += `- ${matchedSpecs.length} teammate(s): ${matchedSpecs.map((s) => s.name).join(", ")}
8520
+ `;
8521
+ summary += `- ${createdTasks.length} initial task(s): ${createdTasks.map((t) => `"${t.title}"`).join(", ")}
8522
+ `;
8523
+ if (unmatchedNames.length > 0) {
8524
+ summary += `- Teammates created with default prompts and tools.
8525
+ `;
8526
+ }
8527
+ summary += `
8528
+ Teammates are now working in the background. Keep calling \`check_tasks\` and \`read_messages\` until all tasks are completed or failed.`;
8529
+ summary += `
5773
8530
 
5774
- You have access to the \`write_todos\` tool to help you manage and plan complex objectives.
5775
- Use this tool for complex objectives to ensure that you are tracking each necessary step and giving the user visibility into your progress.
5776
- This tool is very helpful for planning complex objectives, and for breaking down these larger complex objectives into smaller steps.
8531
+ \`\`\`json
8532
+ ${teamJson}
8533
+ \`\`\``;
8534
+ const toolMessage = new import_langchain42.ToolMessage({
8535
+ content: summary,
8536
+ tool_call_id: config.toolCall?.id,
8537
+ name: "create_team"
8538
+ });
8539
+ const teamState = {
8540
+ teamId,
8541
+ teamLeadId: TEAM_LEAD_AGENT_ID,
8542
+ teammates: matchedSpecs.map((s) => ({
8543
+ name: s.name,
8544
+ role: s.role,
8545
+ description: s.description
8546
+ })),
8547
+ tasks: createdTasks.map((t) => ({
8548
+ id: t.id,
8549
+ title: t.title,
8550
+ description: t.description,
8551
+ status: t.status
8552
+ })),
8553
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8554
+ };
8555
+ return new import_langgraph10.Command({
8556
+ update: { team: teamState, messages: [toolMessage] }
8557
+ });
8558
+ },
8559
+ {
8560
+ name: "create_team",
8561
+ description: `Use this tool to create a team of specialized agents (teammates) to work on complex objectives that require multiple skills or parallel execution. The team lead (you) coordinates the work, while teammates execute tasks independently.
5777
8562
 
5778
- It is critical that you mark todos as completed as soon as you are done with a step. Do not batch up multiple steps before marking them as completed.
5779
- For simple objectives that only require a few steps, it is better to just complete the objective directly and NOT use this tool.
5780
- Writing todos takes time and tokens, use it when it is helpful for managing complex many-step problems! But not for simple few-step requests.
8563
+ When to Use This Tool
8564
+ Use this tool in these scenarios:
5781
8565
 
5782
- ## Important To-Do List Usage Notes to Remember
5783
- - The \`write_todos\` tool should never be called multiple times in parallel.
5784
- - Don't be afraid to revise the To-Do list as you go. New information may reveal new tasks that need to be done, or old tasks that are irrelevant.`;
5785
- var TodoStatus = import_zod36.z.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
5786
- var TodoSchema = import_zod36.z.object({
5787
- content: import_zod36.z.string().describe("Content of the todo item"),
5788
- status: TodoStatus
5789
- });
5790
- var stateSchema = import_zod36.z.object({ todos: import_zod36.z.array(TodoSchema).default([]) });
5791
- function todoListMiddleware(options) {
5792
- const writeTodos = (0, import_langchain39.tool)(
5793
- ({ todos }, config) => {
5794
- return new import_langgraph8.Command({
8566
+ Complex multi-step objectives - When the user's goal requires breaking into multiple tasks that can be executed in parallel or sequence
8567
+ Parallel work needed - When multiple independent subtasks can be done simultaneously by different teammates
8568
+ Diverse expertise required - When the objective needs different skills (research, writing, coding, review, etc.)
8569
+ Long-running tasks - When tasks may take time and you want to monitor progress without blocking
8570
+
8571
+ When NOT to Use This Tool
8572
+ Skip using this tool when:
8573
+
8574
+ Simple single task - The objective can be completed in one straightforward step
8575
+ Already have an active team - A team is already running for this session. Use check_tasks and read_messages to monitor instead
8576
+ Quick question or simple request - No need to create a team for trivial tasks
8577
+
8578
+ IMPORTANT: Task ID Format
8579
+ - Task IDs MUST follow the format: task-01, task-02, task-03, etc.
8580
+ - Each task needs a unique ID starting from task-01
8581
+ - Use sequential IDs to help track task order and dependencies
8582
+ - Example: { id: "task-01", title: "Research topic", description: "..." }
8583
+
8584
+ IMPORTANT: Dependencies
8585
+ - Use dependencies to express which tasks must complete before others can start
8586
+ - Dependencies should reference task IDs (e.g., ["task-01"])
8587
+ - Tasks without dependencies can be worked on immediately in parallel
8588
+
8589
+ IMPORTANT: After Creating Team
8590
+ After calling create_team, you MUST:
8591
+ 1. Use read_messages to receive updates from teammates (it waits up to 3 minutes for new messages)
8592
+ 2. When messages indicate task changes, call check_tasks to get full task status
8593
+ 3. Continue until all tasks show "completed" or "failed"
8594
+ 4. Do NOT assume tasks are done - always verify with check_tasks`,
8595
+ schema: import_v34.z.object({
8596
+ tasks: import_v34.z.array(
8597
+ import_v34.z.object({
8598
+ id: import_v34.z.string().describe("Task ID in format task-01, task-02, etc."),
8599
+ title: import_v34.z.string().describe("Short task title"),
8600
+ description: import_v34.z.string().describe("Detailed task description - what exactly needs to be done"),
8601
+ dependencies: import_v34.z.array(import_v34.z.string()).optional().default([]).describe('Array of task IDs that must complete before this task (e.g. ["task-01"])')
8602
+ })
8603
+ ).describe("List of tasks for teammates to work on. Each task needs unique ID (task-01, task-02, etc.)."),
8604
+ teammates: import_v34.z.array(
8605
+ import_v34.z.object({
8606
+ name: import_v34.z.string().describe("Teammate name (must match a pre-configured teammate type)"),
8607
+ role: import_v34.z.string().describe("Role category (e.g. researcher, writer, coder, reviewer)"),
8608
+ description: import_v34.z.string().describe("What this teammate will focus on - specific instructions for their work")
8609
+ })
8610
+ ).describe("Teammate agents to create. Each should have a clear role and focus.")
8611
+ })
8612
+ }
8613
+ );
8614
+ const resolveTeamId = () => {
8615
+ const state = (0, import_langgraph10.getCurrentTaskInput)();
8616
+ if (state?.team?.teamId) return state.team.teamId;
8617
+ throw new Error("No team_id provided and no team in state. Call create_team first.");
8618
+ };
8619
+ const addTasksTool = (0, import_langchain42.tool)(
8620
+ async (input, config) => {
8621
+ const teamId = resolveTeamId();
8622
+ const created = await taskListStore.addTasks(
8623
+ teamId,
8624
+ input.tasks.map((t) => ({
8625
+ id: t.id,
8626
+ title: t.title,
8627
+ description: t.description,
8628
+ assignee: t.assignee,
8629
+ dependencies: t.dependencies ?? []
8630
+ }))
8631
+ );
8632
+ const summary = created.map((t) => `- ${t.id}: "${t.title}"`).join("\n");
8633
+ return new import_langchain42.ToolMessage({
8634
+ content: `Added ${created.length} task(s) to team ${teamId}:
8635
+ ${summary}
8636
+ Sleeping teammates will wake up and claim these.`,
8637
+ tool_call_id: config.toolCall?.id,
8638
+ name: "add_tasks"
8639
+ });
8640
+ },
8641
+ {
8642
+ name: "add_tasks",
8643
+ description: `Use this tool to add new tasks to an existing team's task list.
8644
+
8645
+ When to Use This Tool
8646
+ Use this tool when:
8647
+
8648
+ User requests additional work - After the initial team was created, the user asks for more tasks
8649
+ Teammate feedback reveals new needs - A teammate's message indicates additional tasks are needed
8650
+ Scope expansion - The original objective grows and requires more subtasks
8651
+ Follow-up tasks discovered - While working, teammates discover tasks that need to be done
8652
+
8653
+ When NOT to Use This Tool
8654
+ Skip using this tool when:
8655
+
8656
+ No team exists - Use create_team first to create a team
8657
+ Simple message response - Just respond to teammates via send_message
8658
+ Task modification needed - Use assign_task, set_task_status, or set_task_dependencies to change existing tasks instead
8659
+
8660
+ IMPORTANT: Task ID Format
8661
+ - New task IDs must be unique and follow format: task-01, task-02, etc.
8662
+ - If the team already has tasks (e.g., task-01, task-02), continue the sequence (task-03, task-04)
8663
+ - Check existing tasks first using check_tasks to determine the next ID
8664
+
8665
+ IMPORTANT: Dependencies
8666
+ - Use dependencies to specify which existing tasks must complete before new tasks can start
8667
+ - Reference task IDs (e.g., ["task-01", "task-02"])
8668
+
8669
+ IMPORTANT: Assigning to a specific teammate
8670
+ - When you need a particular teammate to do the work, set assignee to that teammate's name (e.g. assignee: "researcher"). They can then claim or see the task as assigned to them.`,
8671
+ schema: import_v34.z.object({
8672
+ tasks: import_v34.z.array(
8673
+ import_v34.z.object({
8674
+ id: import_v34.z.string().describe("Task ID in format task-01, task-02, etc. Must be unique."),
8675
+ title: import_v34.z.string().describe("Short task title"),
8676
+ description: import_v34.z.string().describe("Detailed task description - what needs to be done"),
8677
+ assignee: import_v34.z.string().optional().describe("Teammate name to assign this task to (use when you need that person to do the work)"),
8678
+ dependencies: import_v34.z.array(import_v34.z.string()).optional().default([]).describe("Array of task IDs that must complete before this task")
8679
+ })
8680
+ ).describe("New tasks to add to the team")
8681
+ })
8682
+ }
8683
+ );
8684
+ const assignTaskTool = (0, import_langchain42.tool)(
8685
+ async (input, config) => {
8686
+ const teamId = resolveTeamId();
8687
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8688
+ assignee: input.assignee
8689
+ });
8690
+ if (!task) {
8691
+ return new import_langchain42.ToolMessage({
8692
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8693
+ tool_call_id: config.toolCall?.id,
8694
+ name: "assign_task"
8695
+ });
8696
+ }
8697
+ return new import_langchain42.ToolMessage({
8698
+ content: `Task "${task.title}" (${task.id}) assigned to ${input.assignee}.`,
8699
+ tool_call_id: config.toolCall?.id,
8700
+ name: "assign_task"
8701
+ });
8702
+ },
8703
+ {
8704
+ name: "assign_task",
8705
+ description: "Assign a task to a specific teammate. Use when you need to reassign work to a different teammate. Omit team_id to use the active team from state.",
8706
+ schema: import_v34.z.object({
8707
+ task_id: import_v34.z.string().describe("Task ID to assign"),
8708
+ assignee: import_v34.z.string().describe("Teammate name to assign this task to")
8709
+ })
8710
+ }
8711
+ );
8712
+ const setTaskStatusTool = (0, import_langchain42.tool)(
8713
+ async (input, config) => {
8714
+ const teamId = resolveTeamId();
8715
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8716
+ status: input.status
8717
+ });
8718
+ if (!task) {
8719
+ return new import_langchain42.ToolMessage({
8720
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8721
+ tool_call_id: config.toolCall?.id,
8722
+ name: "set_task_status"
8723
+ });
8724
+ }
8725
+ return new import_langchain42.ToolMessage({
8726
+ content: `Task "${task.title}" (${task.id}) status set to ${input.status}.`,
8727
+ tool_call_id: config.toolCall?.id,
8728
+ name: "set_task_status"
8729
+ });
8730
+ },
8731
+ {
8732
+ name: "set_task_status",
8733
+ description: "Set a task's status. Use to reopen a task (set to pending), mark as failed, or correct status. Values: pending, claimed, in_progress, completed, failed. Omit team_id to use the active team from state.",
8734
+ schema: import_v34.z.object({
8735
+ task_id: import_v34.z.string().describe("Task ID to update"),
8736
+ status: import_v34.z.enum(["pending", "claimed", "in_progress", "completed", "failed"]).describe("New status for the task")
8737
+ })
8738
+ }
8739
+ );
8740
+ const setTaskDependenciesTool = (0, import_langchain42.tool)(
8741
+ async (input, config) => {
8742
+ const teamId = resolveTeamId();
8743
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8744
+ dependencies: input.dependencies
8745
+ });
8746
+ if (!task) {
8747
+ return new import_langchain42.ToolMessage({
8748
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8749
+ tool_call_id: config.toolCall?.id,
8750
+ name: "set_task_dependencies"
8751
+ });
8752
+ }
8753
+ return new import_langchain42.ToolMessage({
8754
+ content: `Task "${task.title}" (${task.id}) dependencies set to [${input.dependencies.join(", ")}].`,
8755
+ tool_call_id: config.toolCall?.id,
8756
+ name: "set_task_dependencies"
8757
+ });
8758
+ },
8759
+ {
8760
+ name: "set_task_dependencies",
8761
+ description: 'Set which task IDs must complete before this task can be claimed. Pass an array of task IDs (e.g. ["task-01", "task-02"]). Use to fix task order or add/remove dependencies. Omit team_id to use the active team from state.',
8762
+ schema: import_v34.z.object({
8763
+ task_id: import_v34.z.string().describe("Task ID to update"),
8764
+ dependencies: import_v34.z.array(import_v34.z.string()).describe("Task IDs that must complete before this task can be claimed")
8765
+ })
8766
+ }
8767
+ );
8768
+ const checkTasksTool = (0, import_langchain42.tool)(
8769
+ async (input, config) => {
8770
+ const teamId = resolveTeamId();
8771
+ const tasks = await taskListStore.getAllTasks(teamId);
8772
+ const tasksSnapshot = tasks;
8773
+ return new import_langgraph10.Command({
5795
8774
  update: {
5796
- todos,
5797
- messages: [
5798
- new import_langchain39.ToolMessage({
5799
- content: genUIMarkdown("todo_list", todos),
5800
- tool_call_id: config.toolCall?.id
8775
+ tasks: tasksSnapshot,
8776
+ messages: [
8777
+ new import_langchain42.ToolMessage({
8778
+ content: formatTaskSummary(tasks),
8779
+ tool_call_id: config.toolCall?.id,
8780
+ name: "check_tasks"
5801
8781
  })
5802
8782
  ]
5803
8783
  }
5804
8784
  });
5805
8785
  },
5806
8786
  {
5807
- name: "write_todos",
5808
- description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
5809
- schema: import_zod36.z.object({
5810
- todos: import_zod36.z.array(TodoSchema).describe("List of todo items to update")
8787
+ name: "check_tasks",
8788
+ description: `Use this tool to get the current status of all tasks in a team. This is your primary way to monitor teammate progress.
8789
+
8790
+ When to Use This Tool
8791
+ Use this tool:
8792
+
8793
+ After creating a team - Check task status after create_team to verify tasks are being worked on
8794
+ When read_messages indicates task changes - Teammates report status via messages; call check_tasks to get full task list details
8795
+ Before final synthesis - Verify all tasks are complete before reporting results to user
8796
+
8797
+ IMPORTANT: How to Monitor
8798
+ 1. Use read_messages to wait for updates from teammates (it polls internally).
8799
+ 2. When you receive messages suggesting task progress or completion, call check_tasks to get the current task status.
8800
+ 3. Look at each task's status: pending, in_progress, completed, or failed.
8801
+ 4. NEVER assume tasks are done - always verify with check_tasks.
8802
+
8803
+ Task Status Values:
8804
+ - pending: Task created but not yet claimed by any teammate
8805
+ - in_progress: Teammate is actively working on this task
8806
+ - completed: Task finished successfully
8807
+ - failed: Task encountered an error`,
8808
+ schema: import_v34.z.object({
8809
+ team_id: import_v34.z.string().optional().describe("Team ID (omit to use active team)")
5811
8810
  })
5812
8811
  }
5813
8812
  );
5814
- return (0, import_langchain39.createMiddleware)({
5815
- name: "todoListMiddleware",
5816
- stateSchema,
5817
- tools: [writeTodos],
5818
- wrapModelCall: (request, handler) => handler({
5819
- ...request,
5820
- systemPrompt: (request.systemPrompt ? `${request.systemPrompt}
8813
+ const sendMessageTool = (0, import_langchain42.tool)(
8814
+ async (input, config) => {
8815
+ const teamId = resolveTeamId();
8816
+ await mailboxStore.sendMessage(
8817
+ teamId,
8818
+ TEAM_LEAD_AGENT_ID,
8819
+ input.to,
8820
+ input.content,
8821
+ "direct_message" /* DIRECT_MESSAGE */
8822
+ );
8823
+ return new import_langchain42.ToolMessage({
8824
+ content: `Message sent to ${input.to}.`,
8825
+ tool_call_id: config.toolCall?.id,
8826
+ name: "send_message"
8827
+ });
8828
+ },
8829
+ {
8830
+ name: "send_message",
8831
+ description: "Send a message to a specific teammate in the team. Omit team_id to use the active team from state.",
8832
+ schema: import_v34.z.object({
8833
+ to: import_v34.z.string().describe("Recipient teammate name"),
8834
+ content: import_v34.z.string().describe("Message content")
8835
+ })
8836
+ }
8837
+ );
8838
+ const readMessagesTool = (0, import_langchain42.tool)(
8839
+ async (input, config) => {
8840
+ const teamId = resolveTeamId();
8841
+ const formatAndMarkAsRead = async (msgs2) => {
8842
+ for (const msg of msgs2) {
8843
+ await mailboxStore.markAsRead(teamId, TEAM_LEAD_AGENT_ID, msg.id);
8844
+ }
8845
+ return formatMessagesAsMarkdown(msgs2);
8846
+ };
8847
+ const getAllTeamMessagesForState = async () => {
8848
+ const allMsgs = await mailboxStore.getAllTeamMessages(teamId);
8849
+ return allMsgs.map((msg) => ({
8850
+ id: msg.id,
8851
+ from: msg.from,
8852
+ to: msg.to,
8853
+ content: msg.content,
8854
+ timestamp: msg.timestamp instanceof Date ? msg.timestamp.toISOString() : msg.timestamp,
8855
+ type: msg.type,
8856
+ read: msg.read
8857
+ }));
8858
+ };
8859
+ let msgs = await mailboxStore.getUnreadMessages(
8860
+ teamId,
8861
+ TEAM_LEAD_AGENT_ID
8862
+ );
8863
+ if (msgs.length > 0) {
8864
+ const formatted2 = await formatAndMarkAsRead(msgs);
8865
+ const allTeamMessages2 = await getAllTeamMessagesForState();
8866
+ const toolMessage2 = new import_langchain42.ToolMessage({
8867
+ content: formatted2,
8868
+ tool_call_id: config.toolCall?.id,
8869
+ name: "read_messages"
8870
+ });
8871
+ return new import_langgraph10.Command({
8872
+ update: { team_mailbox: allTeamMessages2, messages: [toolMessage2] }
8873
+ });
8874
+ }
8875
+ const messagePromise = new Promise((resolve3) => {
8876
+ const handler = async (msg) => {
8877
+ mailboxStore.offMessage(teamId, TEAM_LEAD_AGENT_ID, handler);
8878
+ const allMsgs = await mailboxStore.getUnreadMessages(
8879
+ teamId,
8880
+ TEAM_LEAD_AGENT_ID
8881
+ );
8882
+ resolve3(allMsgs);
8883
+ };
8884
+ mailboxStore.onMessage(teamId, TEAM_LEAD_AGENT_ID, handler);
8885
+ });
8886
+ const timeoutPromise = new Promise((resolve3) => {
8887
+ setTimeout(() => resolve3([]), READ_MESSAGES_TIMEOUT_MS);
8888
+ });
8889
+ msgs = await Promise.race([messagePromise, timeoutPromise]);
8890
+ mailboxStore.offMessage(
8891
+ teamId,
8892
+ TEAM_LEAD_AGENT_ID,
8893
+ () => {
8894
+ }
8895
+ );
8896
+ const allTeamMessages = await getAllTeamMessagesForState();
8897
+ if (msgs.length === 0) {
8898
+ const toolMessage2 = new import_langchain42.ToolMessage({
8899
+ content: "No unread messages from teammates.",
8900
+ tool_call_id: config.toolCall?.id,
8901
+ name: "read_messages"
8902
+ });
8903
+ return new import_langgraph10.Command({
8904
+ update: { team_mailbox: allTeamMessages, messages: [toolMessage2] }
8905
+ });
8906
+ }
8907
+ const formatted = await formatAndMarkAsRead(msgs);
8908
+ const toolMessage = new import_langchain42.ToolMessage({
8909
+ content: formatted,
8910
+ tool_call_id: config.toolCall?.id,
8911
+ name: "read_messages"
8912
+ });
8913
+ return new import_langgraph10.Command({
8914
+ update: { team_mailbox: allTeamMessages, messages: [toolMessage] }
8915
+ });
8916
+ },
8917
+ {
8918
+ name: "read_messages",
8919
+ description: "Read unread messages from teammates. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
8920
+ schema: import_v34.z.object({
8921
+ team_id: import_v34.z.string().optional().describe("Team ID (omit to use active team)")
8922
+ })
8923
+ }
8924
+ );
8925
+ const disbandTeamTool = (0, import_langchain42.tool)(
8926
+ async (input, config) => {
8927
+ const teamId = resolveTeamId();
8928
+ await mailboxStore.broadcastMessage(
8929
+ teamId,
8930
+ TEAM_LEAD_AGENT_ID,
8931
+ "All tasks are complete. You may now shut down gracefully. Thank you for your work!",
8932
+ "shutdown_request" /* SHUTDOWN_REQUEST */
8933
+ );
8934
+ await new Promise((r) => setTimeout(r, 2e3));
8935
+ return new import_langchain42.ToolMessage({
8936
+ content: `Team ${teamId} has been disbanded. All teammates notified and resources cleaned up.`,
8937
+ tool_call_id: config.toolCall?.id,
8938
+ name: "disband_team"
8939
+ });
8940
+ },
8941
+ {
8942
+ name: "disband_team",
8943
+ description: "Disband a team when all work is done. Before calling: (1) Call check_tasks to verify no tasks are still pending/in_progress; (2) if any are, discuss with the team via read_messages and broadcast_message/send_message whether to continue or stop/cancel them; (3) only after alignment (all tasks completed/failed or explicitly stopped), then call this tool. This will: 1) Send a shutdown message to all teammates, 2) Wait briefly for them to clean up, 3) Clear all tasks and messages. Omit team_id to use the active team from state."
8944
+ }
8945
+ );
8946
+ const broadcastMessageTool = (0, import_langchain42.tool)(
8947
+ async (input, config) => {
8948
+ const teamId = resolveTeamId();
8949
+ await mailboxStore.broadcastMessage(
8950
+ teamId,
8951
+ TEAM_LEAD_AGENT_ID,
8952
+ input.content,
8953
+ "broadcast" /* BROADCAST */
8954
+ );
8955
+ return new import_langchain42.ToolMessage({
8956
+ content: `Broadcast message sent to all teammates.`,
8957
+ tool_call_id: config.toolCall?.id,
8958
+ name: "broadcast_message"
8959
+ });
8960
+ },
8961
+ {
8962
+ name: "broadcast_message",
8963
+ description: "Send a message to all teammates at once. Use this to communicate with everyone in the team. Omit team_id to use the active team from state.",
8964
+ schema: import_v34.z.object({
8965
+ content: import_v34.z.string().describe("Message content to broadcast to all teammates")
8966
+ })
8967
+ }
8968
+ );
8969
+ return (0, import_langchain42.createMiddleware)({
8970
+ name: "teamMiddleware",
8971
+ tools: [
8972
+ createTeamTool,
8973
+ addTasksTool,
8974
+ assignTaskTool,
8975
+ setTaskStatusTool,
8976
+ setTaskDependenciesTool,
8977
+ checkTasksTool,
8978
+ sendMessageTool,
8979
+ readMessagesTool,
8980
+ disbandTeamTool,
8981
+ broadcastMessageTool
8982
+ ],
8983
+ wrapModelCall: async (request, handler) => {
8984
+ const currentPrompt = request.systemPrompt || "";
8985
+ const newPrompt = currentPrompt ? `${currentPrompt}
5821
8986
 
5822
- ` : "") + (options?.systemPrompt ?? TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT)
5823
- })
8987
+ ${TEAM_SYSTEM_PROMPT}` : TEAM_SYSTEM_PROMPT;
8988
+ return handler({
8989
+ ...request,
8990
+ systemPrompt: newPrompt
8991
+ });
8992
+ }
5824
8993
  });
5825
8994
  }
5826
8995
 
5827
- // src/deep_agent_new/agent.ts
5828
- var BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
5829
- function createDeepAgent(params = {}) {
5830
- const {
5831
- model = "claude-sonnet-4-5-20250929",
5832
- tools = [],
5833
- systemPrompt,
5834
- middleware: customMiddleware = [],
5835
- subagents = [],
5836
- responseFormat,
5837
- contextSchema,
5838
- checkpointer,
5839
- store,
5840
- backend,
5841
- interruptOn,
5842
- name,
5843
- skills
5844
- } = params;
5845
- const finalSystemPrompt = systemPrompt ? `${systemPrompt}
5846
-
5847
- ${BASE_PROMPT}` : BASE_PROMPT;
5848
- const filesystemBackend = backend ? backend : async (config) => new StateBackend(config);
8996
+ // src/agent_team/agent_team.ts
8997
+ var TeammateInfoSchema = import_v35.z.object({
8998
+ name: import_v35.z.string().describe("Teammate name"),
8999
+ role: import_v35.z.string().describe("Role category (e.g. research, writing, review)"),
9000
+ description: import_v35.z.string().describe("What this teammate focuses on")
9001
+ });
9002
+ var TeamTaskInfoSchema = import_v35.z.object({
9003
+ id: import_v35.z.string(),
9004
+ title: import_v35.z.string(),
9005
+ description: import_v35.z.string(),
9006
+ status: import_v35.z.string().optional()
9007
+ });
9008
+ var MailboxMessageSchema = import_v35.z.object({
9009
+ id: import_v35.z.string().describe("Unique message identifier"),
9010
+ from: import_v35.z.string().describe("Sender agent name"),
9011
+ to: import_v35.z.string().describe("Recipient agent name"),
9012
+ content: import_v35.z.string().describe("Message content"),
9013
+ timestamp: import_v35.z.string().describe("ISO timestamp when the message was sent"),
9014
+ type: import_v35.z.nativeEnum(MessageType).describe("Message type"),
9015
+ read: import_v35.z.boolean().describe("Whether the recipient has read this message")
9016
+ });
9017
+ var TeamInfoSchema = import_v35.z.object({
9018
+ teamId: import_v35.z.string().describe("Unique team identifier"),
9019
+ teamLeadId: import_v35.z.string().default("team_lead").describe("Team lead agent ID"),
9020
+ teammates: import_v35.z.array(TeammateInfoSchema).describe("Active teammates in this team"),
9021
+ tasks: import_v35.z.array(TeamTaskInfoSchema).optional().describe("Initial tasks snapshot"),
9022
+ createdAt: import_v35.z.string().optional().describe("ISO timestamp when team was created")
9023
+ });
9024
+ var TEAM_STATE_SCHEMA = import_v35.z.object({
9025
+ team: TeamInfoSchema.optional().describe("Team info: teamId, teamLeadId, teammates, tasks. Set when create_team succeeds."),
9026
+ tasks: import_v35.z.array(TeamTaskInfoSchema).optional().describe("Current tasks snapshot from check_tasks. Updated on each check."),
9027
+ team_mailbox: import_v35.z.array(MailboxMessageSchema).optional().describe("All team mailbox messages for display")
9028
+ });
9029
+ var TEAM_LEAD_BASE_PROMPT = `You are a team lead that coordinates a team of specialized agents. In order to complete the objective that the user asks of you, you will need to:
9030
+
9031
+ 1. Understand the user's objective and break it into concrete tasks.
9032
+ 2. Create a team by calling \`create_team\` with the tasks and teammate assignments.
9033
+ 3. **While tasks are incomplete**: Use \`read_messages\` to wait for teammate updates (it waits up to 3 minutes for new messages). When messages indicate task changes, call \`check_tasks\` to get full task status. Do not assume tasks are done without checking.
9034
+ 4. **Important - Message handling**: After calling \`read_messages\`, if there are unread messages from teammates (you will see them in the tool output), you MUST:
9035
+ - Acknowledge their message
9036
+ - If they need guidance, provide it using \`send_message\`
9037
+ - If they reported a problem, address it (add tasks, adjust plan, etc.)
9038
+ - If they made a discovery, incorporate it into your plan
9039
+ Do not just read messages - always respond or take action!
9040
+ 5. **Task assignment**: You can explicitly assign tasks to specific teammates via \`send_message\`, or let them self-claim from available tasks.
9041
+ 6. **Plan approval** (optional): If a teammate needs approval before implementing, wait for their plan and approve/reject with feedback via \`send_message\`.
9042
+ 7. Respond to the user with status updates and final results.
9043
+ 8. When the user asks for changes, use \`add_tasks\`, \`assign_task\`, \`set_task_status\`, or \`set_task_dependencies\` to evolve the task list.
9044
+ 9. When teammates report discoveries or problems, adjust the plan accordingly.
9045
+ 10. **Cleanup**: When all tasks are done, tell teammates to shut down, then clean up the team resources.
9046
+
9047
+ ## Coordination Rules
9048
+
9049
+ - **Single point of contact**: User only talks to YOU - you are the only interface between user and team
9050
+ - **Don't parallel implement**: Do not start working on tasks yourself while teammates are working - wait for them to finish
9051
+ - **Always verify**: Never assume tasks are done - always verify with \`check_tasks\`
9052
+ - **Permissions**: All teammates inherit your permission settings. Adjust individual teammate modes if needed after spawning.
9053
+
9054
+ Always plan tasks with clear, actionable descriptions. Use dependencies to express ordering constraints.`;
9055
+ function createAgentTeam(config) {
9056
+ const taskListStore = config.taskListStore ?? new InMemoryTaskListStore();
9057
+ const mailboxStore = config.mailboxStore ?? new InMemoryMailboxStore();
9058
+ const filesystemMiddleware = createFilesystemMiddleware({
9059
+ backend: config.backend
9060
+ });
9061
+ const allMiddleware = [filesystemMiddleware, ...config.middleware ?? []];
9062
+ const teamConfigWithFs = {
9063
+ ...config,
9064
+ middleware: allMiddleware
9065
+ };
9066
+ const teamMiddleware = createTeamMiddleware({
9067
+ teamConfig: teamConfigWithFs,
9068
+ taskListStore,
9069
+ mailboxStore
9070
+ });
5849
9071
  const middleware = [
5850
- // Provides todo list management capabilities for tracking tasks
5851
- todoListMiddleware(),
5852
- // Enables filesystem operations and optional long-term memory storage
5853
- createFilesystemMiddleware({ backend: filesystemBackend }),
5854
- // Enables delegation to specialized subagents for complex tasks
5855
- createSubAgentMiddleware({
5856
- defaultModel: model,
5857
- defaultTools: tools,
5858
- defaultMiddleware: [
5859
- // Subagent middleware: Todo list management
5860
- todoListMiddleware(),
5861
- // Subagent middleware: Filesystem operations
5862
- createFilesystemMiddleware({
5863
- backend: filesystemBackend
5864
- }),
5865
- // Subagent middleware: Automatic conversation summarization when token limits are approached
5866
- (0, import_langchain40.summarizationMiddleware)({
5867
- model,
5868
- trigger: { tokens: 17e4 },
5869
- keep: { messages: 6 }
5870
- }),
5871
- // Subagent middleware: Anthropic prompt caching for improved performance
5872
- (0, import_langchain40.anthropicPromptCachingMiddleware)({
5873
- unsupportedModelBehavior: "ignore"
5874
- }),
5875
- // Subagent middleware: Patches tool calls for compatibility
5876
- createPatchToolCallsMiddleware()
5877
- ],
5878
- defaultInterruptOn: interruptOn,
5879
- subagents,
5880
- generalPurposeAgent: false
5881
- }),
5882
- // Automatically summarizes conversation history when token limits are approached
5883
- (0, import_langchain40.summarizationMiddleware)({
5884
- model,
5885
- trigger: { tokens: 17e4 },
5886
- keep: { messages: 6 }
5887
- }),
5888
- // Enables Anthropic prompt caching for improved performance and reduced costs
5889
- (0, import_langchain40.anthropicPromptCachingMiddleware)({
5890
- unsupportedModelBehavior: "ignore"
5891
- }),
5892
- // Patches tool calls to ensure compatibility across different model providers
5893
- createPatchToolCallsMiddleware()
9072
+ teamMiddleware,
9073
+ ...allMiddleware
5894
9074
  ];
5895
- if (interruptOn) {
5896
- middleware.push((0, import_langchain40.humanInTheLoopMiddleware)({ interruptOn }));
5897
- }
5898
- middleware.push(...customMiddleware);
5899
- return (0, import_langchain40.createAgent)({
5900
- model,
5901
- systemPrompt: finalSystemPrompt,
5902
- tools,
9075
+ const systemPrompt = config.systemPrompt + "\n\n" + TEAM_LEAD_BASE_PROMPT;
9076
+ const stateSchema2 = createReactAgentSchema(TEAM_STATE_SCHEMA);
9077
+ return (0, import_langchain43.createAgent)({
9078
+ model: config.model ?? "claude-sonnet-4-5-20250929",
9079
+ systemPrompt,
9080
+ tools: [],
9081
+ // all tools come from middleware
5903
9082
  middleware,
5904
- responseFormat,
5905
- contextSchema,
5906
- checkpointer,
5907
- store,
5908
- name
9083
+ checkpointer: config.checkpointer,
9084
+ stateSchema: stateSchema2
5909
9085
  });
5910
9086
  }
5911
9087
 
5912
- // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
5913
- var DeepAgentGraphBuilder = class {
5914
- /**
5915
- * 根据 middleware 配置创建 backend factory
5916
- */
5917
- createFilesystemBackendFactory(middlewareConfigs) {
5918
- const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
5919
- if (!filesystemConfig || !filesystemConfig.enabled) {
5920
- return void 0;
5921
- }
5922
- const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
5923
- return async (config) => {
5924
- let sandboxName = "global";
5925
- if (isolatedLevel === "agent") {
5926
- sandboxName = "agent";
5927
- } else if (isolatedLevel === "thread") {
5928
- sandboxName = "thread";
5929
- }
5930
- const sandboxManager = sandboxLatticeManager.getSandboxLattice("default");
5931
- if (!sandboxManager) {
5932
- throw new Error("Sandbox manager not found");
5933
- }
5934
- return new SandboxFilesystem({
5935
- sandboxInstance: await sandboxManager.createSandbox(sandboxName)
5936
- });
5937
- };
5938
- }
5939
- /**
5940
- * 根据 middleware 配置创建 middlewares
5941
- */
5942
- createMiddlewares(middlewareConfigs) {
5943
- return createCommonMiddlewares(middlewareConfigs);
5944
- }
9088
+ // src/agent_team/builders/TeamAgentGraphBuilder.ts
9089
+ var TeamAgentGraphBuilder = class {
5945
9090
  /**
5946
- * 构建Deep Agent Graph
9091
+ * Build a Team agent from the registered AgentLattice config.
5947
9092
  *
5948
- * @param agentLattice Agent Lattice对象
5949
- * @param params Agent构建参数
5950
- * @returns 返回CompiledGraph对象
9093
+ * @param agentLattice - The AgentLattice containing the TeamAgentConfig
9094
+ * @param params - Build params with resolved tools and model
9095
+ * @returns AgentClient (the TeamLead ReactAgent)
5951
9096
  */
5952
9097
  build(agentLattice, params) {
9098
+ const config = agentLattice.config;
9099
+ if (!(0, import_protocols.isTeamAgentConfig)(config)) {
9100
+ throw new Error(
9101
+ `TeamAgentGraphBuilder received non-TEAM config: ${config.type}`
9102
+ );
9103
+ }
5953
9104
  const tools = params.tools.map((t) => {
5954
9105
  const toolClient = getToolClient(t.key);
5955
9106
  return toolClient;
5956
- }).filter((tool36) => tool36 !== void 0);
5957
- const subagents = params.subAgents.map((sa) => {
5958
- if (sa.client) {
5959
- return {
5960
- name: sa.config.name,
5961
- description: sa.config.description,
5962
- runnable: sa.client
5963
- };
5964
- } else {
5965
- const subagentClient = createAgentClientFromAgentLattice({
5966
- config: sa.config
5967
- });
5968
- return {
5969
- name: sa.config.name,
5970
- description: sa.config.description,
5971
- runnable: subagentClient
5972
- };
5973
- }
9107
+ }).filter((tool38) => tool38 !== void 0);
9108
+ const teammates = params.subAgents.map((sa) => {
9109
+ const baseConfig = sa.config;
9110
+ return {
9111
+ name: baseConfig.name,
9112
+ role: baseConfig.name,
9113
+ description: baseConfig.description,
9114
+ systemPrompt: baseConfig.prompt,
9115
+ tools
9116
+ };
5974
9117
  });
5975
9118
  const middlewareConfigs = params.middleware || [];
5976
- const filesystemBackend = this.createFilesystemBackendFactory(middlewareConfigs);
5977
- const middlewares = this.createMiddlewares(middlewareConfigs);
5978
- const deepAgent = createDeepAgent({
5979
- tools,
9119
+ let filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
9120
+ const middlewares = createCommonMiddlewares(middlewareConfigs);
9121
+ if (!filesystemBackend) {
9122
+ filesystemBackend = async (config2) => {
9123
+ return new StateBackend(config2);
9124
+ };
9125
+ }
9126
+ const teamConfig = {
9127
+ teammates,
9128
+ maxConcurrency: config.maxConcurrency,
5980
9129
  model: params.model,
5981
- contextSchema: params.stateSchema,
5982
- systemPrompt: params.prompt,
5983
- subagents,
9130
+ systemPrompt: config.prompt || void 0,
9131
+ tools,
5984
9132
  checkpointer: getCheckpointSaver("default"),
5985
- skills: params.skillCategories,
9133
+ scheduleLatticeKey: config.scheduleLatticeKey,
9134
+ pollIntervalMs: config.pollIntervalMs,
5986
9135
  backend: filesystemBackend,
5987
9136
  middleware: middlewares
5988
- });
5989
- return deepAgent;
9137
+ };
9138
+ const teamLead = createAgentTeam(teamConfig);
9139
+ return teamLead;
5990
9140
  }
5991
9141
  };
5992
9142
 
@@ -6011,6 +9161,7 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
6011
9161
  registerDefaultBuilders() {
6012
9162
  this.builders.set(import_protocols.AgentType.REACT, new ReActAgentGraphBuilder());
6013
9163
  this.builders.set(import_protocols.AgentType.DEEP_AGENT, new DeepAgentGraphBuilder());
9164
+ this.builders.set(import_protocols.AgentType.TEAM, new TeamAgentGraphBuilder());
6014
9165
  }
6015
9166
  /**
6016
9167
  * 注册自定义Builder
@@ -6218,6 +9369,32 @@ var AgentLatticeManager = class _AgentLatticeManager extends BaseLatticeManager
6218
9369
  };
6219
9370
  this.register(config.key, agentLattice);
6220
9371
  }
9372
+ /**
9373
+ * Register a pre-built teammate agent client for dynamic team spawning.
9374
+ * Used when spawning teammates via agentWorkerGraph - the agent must exist
9375
+ * in the lattice before the worker invokes it.
9376
+ *
9377
+ * @param key Unique key (e.g. `team:${teamId}:${agentName}`)
9378
+ * @param client Pre-built AgentClient (ReactAgent or CompiledGraph)
9379
+ */
9380
+ registerTeammateAgent(key, client) {
9381
+ const agentLattice = {
9382
+ config: {
9383
+ key,
9384
+ name: key,
9385
+ description: `Teammate agent: ${key}`,
9386
+ type: import_protocols.AgentType.REACT
9387
+ },
9388
+ client
9389
+ };
9390
+ this.register(key, agentLattice);
9391
+ }
9392
+ /**
9393
+ * Unregister a teammate agent. Call when a team is done to clean up.
9394
+ */
9395
+ unregisterTeammateAgent(key) {
9396
+ return this.remove(key);
9397
+ }
6221
9398
  /**
6222
9399
  * 获取AgentLattice
6223
9400
  * @param key Lattice键名
@@ -6379,6 +9556,8 @@ var getAgentLattice = (key) => agentLatticeManager.getAgentLattice(key);
6379
9556
  var getAgentConfig = (key) => agentLatticeManager.getAgentConfig(key);
6380
9557
  var getAllAgentConfigs = () => agentLatticeManager.getAllAgentConfigs();
6381
9558
  var validateAgentInput = (key, input) => agentLatticeManager.validateAgentInput(key, input);
9559
+ var registerTeammateAgent = (key, client) => agentLatticeManager.registerTeammateAgent(key, client);
9560
+ var unregisterTeammateAgent = (key) => agentLatticeManager.unregisterTeammateAgent(key);
6382
9561
  var getAgentClient = (key, options) => agentLatticeManager.initializeClient(key, options);
6383
9562
  var createAgentClientFromAgentLattice = (agentLattice, options) => agentLatticeManager.createAgentClientFromConfig(agentLattice, options);
6384
9563
 
@@ -6575,9 +9754,9 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6575
9754
  next: (chunk) => {
6576
9755
  queue.push(chunk);
6577
9756
  if (resolveNext) {
6578
- const resolve2 = resolveNext;
9757
+ const resolve3 = resolveNext;
6579
9758
  resolveNext = null;
6580
- resolve2();
9759
+ resolve3();
6581
9760
  }
6582
9761
  },
6583
9762
  error: (err) => {
@@ -6587,17 +9766,17 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6587
9766
  errorNext = null;
6588
9767
  reject(err);
6589
9768
  } else if (resolveNext) {
6590
- const resolve2 = resolveNext;
9769
+ const resolve3 = resolveNext;
6591
9770
  resolveNext = null;
6592
- resolve2();
9771
+ resolve3();
6593
9772
  }
6594
9773
  },
6595
9774
  complete: () => {
6596
9775
  isCompleted = true;
6597
9776
  if (resolveNext) {
6598
- const resolve2 = resolveNext;
9777
+ const resolve3 = resolveNext;
6599
9778
  resolveNext = null;
6600
- resolve2();
9779
+ resolve3();
6601
9780
  }
6602
9781
  }
6603
9782
  });
@@ -6609,8 +9788,8 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6609
9788
  }
6610
9789
  if (queue.length === 0) {
6611
9790
  if (isCompleted) break;
6612
- await new Promise((resolve2, reject) => {
6613
- resolveNext = resolve2;
9791
+ await new Promise((resolve3, reject) => {
9792
+ resolveNext = resolve3;
6614
9793
  errorNext = reject;
6615
9794
  });
6616
9795
  }
@@ -8585,10 +11764,10 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
8585
11764
  }
8586
11765
  const tools = await this.getAllTools();
8587
11766
  console.log(`[MCP] Registering ${tools.length} tools to Tool Lattice...`);
8588
- for (const tool36 of tools) {
8589
- const toolKey = prefix ? `${prefix}_${tool36.name}` : tool36.name;
8590
- tool36.name = toolKey;
8591
- toolLatticeManager.registerExistingTool(toolKey, tool36);
11767
+ for (const tool38 of tools) {
11768
+ const toolKey = prefix ? `${prefix}_${tool38.name}` : tool38.name;
11769
+ tool38.name = toolKey;
11770
+ toolLatticeManager.registerExistingTool(toolKey, tool38);
8592
11771
  console.log(`[MCP] Registered tool: ${toolKey}`);
8593
11772
  }
8594
11773
  console.log(`[MCP] Successfully registered ${tools.length} tools to Tool Lattice`);
@@ -8598,6 +11777,70 @@ var mcpManager = McpLatticeManager.getInstance();
8598
11777
 
8599
11778
  // src/index.ts
8600
11779
  var Protocols = __toESM(require("@axiom-lattice/protocols"));
11780
+
11781
+ // src/util/encryption.ts
11782
+ var import_crypto = require("crypto");
11783
+ var ALGORITHM = "aes-256-gcm";
11784
+ var IV_LENGTH = 16;
11785
+ var SALT_LENGTH = 32;
11786
+ var ITERATIONS = 1e5;
11787
+ var DEFAULT_ENCRYPTION_KEY = "lattice-default-encryption-key-dev-only-32b!";
11788
+ var cachedKey = null;
11789
+ var keyValidated = false;
11790
+ function getEncryptionKey() {
11791
+ if (cachedKey) {
11792
+ return cachedKey;
11793
+ }
11794
+ const key = process.env.LATTICE_ENCRYPTION_KEY || DEFAULT_ENCRYPTION_KEY;
11795
+ cachedKey = (0, import_crypto.pbkdf2Sync)(key, "lattice-encryption-salt", ITERATIONS, 32, "sha256");
11796
+ if (!keyValidated) {
11797
+ keyValidated = true;
11798
+ validateEncryptionKey();
11799
+ }
11800
+ return cachedKey;
11801
+ }
11802
+ function encrypt(plaintext, key) {
11803
+ const actualKey = key || getEncryptionKey();
11804
+ const salt = (0, import_crypto.randomBytes)(SALT_LENGTH);
11805
+ const iv = (0, import_crypto.randomBytes)(IV_LENGTH);
11806
+ const derivedKey = (0, import_crypto.pbkdf2Sync)(actualKey, salt, ITERATIONS, 32, "sha256");
11807
+ const cipher = (0, import_crypto.createCipheriv)(ALGORITHM, derivedKey, iv);
11808
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
11809
+ const authTag = cipher.getAuthTag();
11810
+ return Buffer.concat([salt, iv, encrypted, authTag]).toString("base64");
11811
+ }
11812
+ function decrypt(encrypted, key) {
11813
+ const actualKey = key || getEncryptionKey();
11814
+ const data = Buffer.from(encrypted, "base64");
11815
+ const salt = data.subarray(0, SALT_LENGTH);
11816
+ const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
11817
+ const authTag = data.subarray(-16);
11818
+ const ciphertext = data.subarray(SALT_LENGTH + IV_LENGTH, -16);
11819
+ const derivedKey = (0, import_crypto.pbkdf2Sync)(actualKey, salt, ITERATIONS, 32, "sha256");
11820
+ const decipher = (0, import_crypto.createDecipheriv)(ALGORITHM, derivedKey, iv);
11821
+ decipher.setAuthTag(authTag);
11822
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
11823
+ }
11824
+ function isUsingDefaultKey() {
11825
+ return !process.env.LATTICE_ENCRYPTION_KEY;
11826
+ }
11827
+ function validateEncryptionKey() {
11828
+ if (isUsingDefaultKey()) {
11829
+ if (process.env.NODE_ENV === "production") {
11830
+ console.warn(
11831
+ "\u26A0\uFE0F WARNING: Using default encryption key in production environment. Set LATTICE_ENCRYPTION_KEY environment variable for security."
11832
+ );
11833
+ } else {
11834
+ console.warn(
11835
+ "\u26A0\uFE0F WARNING: Using default encryption key. Set LATTICE_ENCRYPTION_KEY environment variable in production."
11836
+ );
11837
+ }
11838
+ }
11839
+ }
11840
+ function clearEncryptionKeyCache() {
11841
+ cachedKey = null;
11842
+ keyValidated = false;
11843
+ }
8601
11844
  // Annotate the CommonJS export names for ESM import in node:
8602
11845
  0 && (module.exports = {
8603
11846
  AGENT_TASK_EVENT,
@@ -8607,42 +11850,73 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
8607
11850
  AgentType,
8608
11851
  ChunkBuffer,
8609
11852
  ChunkBufferLatticeManager,
11853
+ CompositeBackend,
8610
11854
  ConsoleLoggerClient,
8611
11855
  DefaultScheduleClient,
11856
+ EMPTY_CONTENT_WARNING,
8612
11857
  EmbeddingsLatticeManager,
8613
11858
  FileSystemSkillStore,
11859
+ FilesystemBackend,
8614
11860
  GraphBuildOptions,
8615
11861
  InMemoryAssistantStore,
8616
11862
  InMemoryChunkBuffer,
11863
+ InMemoryDatabaseConfigStore,
11864
+ InMemoryMailboxStore,
11865
+ InMemoryTaskListStore,
8617
11866
  InMemoryThreadStore,
11867
+ LINE_NUMBER_WIDTH,
8618
11868
  LoggerLatticeManager,
11869
+ MAX_LINE_LENGTH,
8619
11870
  McpLatticeManager,
11871
+ MemoryBackend,
8620
11872
  MemoryLatticeManager,
8621
11873
  MemoryQueueClient,
8622
11874
  MemoryScheduleStorage,
8623
11875
  MemoryType,
11876
+ MessageType,
8624
11877
  ModelLatticeManager,
8625
11878
  PinoLoggerClient,
8626
11879
  PostgresDatabase,
8627
11880
  Protocols,
8628
11881
  QueueLatticeManager,
11882
+ SandboxFilesystem,
8629
11883
  SandboxLatticeManager,
8630
11884
  ScheduleLatticeManager,
8631
11885
  SkillLatticeManager,
8632
11886
  SqlDatabaseManager,
11887
+ StateBackend,
11888
+ StoreBackend,
8633
11889
  StoreLatticeManager,
11890
+ TOOL_RESULT_TOKEN_LIMIT,
11891
+ TRUNCATION_GUIDANCE,
11892
+ TaskStatus,
11893
+ TeamAgentGraphBuilder,
8634
11894
  ThreadStatus,
8635
11895
  ToolLatticeManager,
8636
11896
  VectorStoreLatticeManager,
8637
11897
  agentLatticeManager,
11898
+ buildGrepResultsDict,
11899
+ checkEmptyContent,
11900
+ clearEncryptionKeyCache,
11901
+ createAgentTeam,
11902
+ createFileData,
8638
11903
  createInfoSqlTool,
8639
11904
  createListTablesSqlTool,
8640
11905
  createQueryCheckerSqlTool,
8641
11906
  createQuerySqlTool,
11907
+ createTeamMiddleware,
11908
+ createTeammateTools,
11909
+ decrypt,
8642
11910
  describeCronExpression,
8643
11911
  embeddingsLatticeManager,
11912
+ encrypt,
8644
11913
  eventBus,
8645
11914
  eventBusDefault,
11915
+ fileDataToString,
11916
+ formatContentWithLineNumbers,
11917
+ formatGrepMatches,
11918
+ formatGrepResults,
11919
+ formatReadResponse,
8646
11920
  getAgentClient,
8647
11921
  getAgentConfig,
8648
11922
  getAgentLattice,
@@ -8652,6 +11926,7 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
8652
11926
  getChunkBuffer,
8653
11927
  getEmbeddingsClient,
8654
11928
  getEmbeddingsLattice,
11929
+ getEncryptionKey,
8655
11930
  getLoggerLattice,
8656
11931
  getModelLattice,
8657
11932
  getNextCronTime,
@@ -8664,7 +11939,11 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
8664
11939
  getToolLattice,
8665
11940
  getVectorStoreClient,
8666
11941
  getVectorStoreLattice,
11942
+ globSearchFiles,
11943
+ grepMatchesFromFiles,
11944
+ grepSearchFiles,
8667
11945
  hasChunkBuffer,
11946
+ isUsingDefaultKey,
8668
11947
  isValidCronExpression,
8669
11948
  isValidSandboxName,
8670
11949
  isValidSkillName,
@@ -8673,6 +11952,7 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
8673
11952
  modelLatticeManager,
8674
11953
  normalizeSandboxName,
8675
11954
  parseCronExpression,
11955
+ performStringReplacement,
8676
11956
  queueLatticeManager,
8677
11957
  registerAgentLattice,
8678
11958
  registerAgentLattices,
@@ -8685,15 +11965,22 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
8685
11965
  registerQueueLattice,
8686
11966
  registerScheduleLattice,
8687
11967
  registerStoreLattice,
11968
+ registerTeammateAgent,
8688
11969
  registerToolLattice,
8689
11970
  registerVectorStoreLattice,
8690
11971
  sandboxLatticeManager,
11972
+ sanitizeToolCallId,
8691
11973
  scheduleLatticeManager,
8692
11974
  skillLatticeManager,
8693
11975
  sqlDatabaseManager,
8694
11976
  storeLatticeManager,
8695
11977
  toolLatticeManager,
11978
+ truncateIfTooLong,
11979
+ unregisterTeammateAgent,
11980
+ updateFileData,
8696
11981
  validateAgentInput,
11982
+ validateEncryptionKey,
11983
+ validatePath,
8697
11984
  validateSkillName,
8698
11985
  validateToolInput,
8699
11986
  vectorStoreLatticeManager